Android: Как проверить, виден ли вид внутри ScrollView?
у меня есть ScrollView который содержит ряд Views. Я хотел бы иметь возможность определить, является ли вид в настоящее время видимым (если какая-либо его часть в настоящее время отображается ScrollView). Я ожидал бы, что приведенный ниже код сделает это, удивительно, что это не так:
Rect bounds = new Rect();
view.getDrawingRect(bounds);
Rect scrollBounds = new Rect(scroll.getScrollX(), scroll.getScrollY(),
scroll.getScrollX() + scroll.getWidth(), scroll.getScrollY() + scroll.getHeight());
if(Rect.intersects(scrollBounds, bounds))
{
//is visible
}
11 ответов:
использовать
View#getHitRectвместоView#getDrawingRectна вид вы тестируете. Вы можете использоватьView#getDrawingRectнаScrollViewвместо явного вычисления.код
View#getDrawingRect:public void getDrawingRect(Rect outRect) { outRect.left = mScrollX; outRect.top = mScrollY; outRect.right = mScrollX + (mRight - mLeft); outRect.bottom = mScrollY + (mBottom - mTop); }код
View#getHitRect:public void getHitRect(Rect outRect) { outRect.set(mLeft, mTop, mRight, mBottom); }
это работает:
Rect scrollBounds = new Rect(); scrollView.getHitRect(scrollBounds); if (imageView.getLocalVisibleRect(scrollBounds)) { // Any portion of the imageView, even a single pixel, is within the visible window } else { // NONE of the imageView is within the visible window }
Если вы хотите обнаружить, что вид полностью видны:
private boolean isViewVisible(View view) { Rect scrollBounds = new Rect(); mScrollView.getDrawingRect(scrollBounds); float top = view.getY(); float bottom = top + view.getHeight(); if (scrollBounds.top < top && scrollBounds.bottom > bottom) { return true; } else { return false; } }
чтобы немного расширить ответ Билла Моута с помощью getLocalVisibleRect, вы можете проверить, является ли представление только частично видимым:
Rect scrollBounds = new Rect(); scrollView.getHitRect(scrollBounds); if (!imageView.getLocalVisibleRect(scrollBounds) || scrollBounds.height() < imageView.getHeight()) { // imageView is not within or only partially within the visible window } else { // imageView is completely visible }
Я сегодня столкнулся с той же проблемой. Во время поиска и чтения ссылки на Android я нашел этот пост и метод, который я использовал вместо этого;
public final boolean getLocalVisibleRect (Rect r)приятно из них не только предоставление прямой, но и логическое указание, если вид виден вообще. С отрицательной стороны этот метод недокументирован : (
Я хочу, чтобы обнаружить, если ваш
Viewполностьюvisible, попробуйте с помощью этого метода:private boolean isViewVisible(View view) { Rect scrollBounds = new Rect(); mScrollView.getDrawingRect(scrollBounds); float top = view.getY(); float bottom = top + view.getHeight(); if (scrollBounds.top < top && scrollBounds.bottom > bottom) { return true; //View is visible. } else { return false; //View is NOT visible. } }строго говоря, вы можете получить видимость вида с:
if (myView.getVisibility() == View.VISIBLE) { //VISIBLE } else { //INVISIBLE }возможные постоянные значения видимости в представлении:
видимого Этот вид виден. Используйте с setVisibility (int) и android:видимость.
невидимка Это представление невидимо, но оно по-прежнему занимает место для целей макета. Используйте с setVisibility (int) и android:видимость.
ушел Этот вид невидим, и он не занимает никакого места для целей компоновки. Используйте с setVisibility (int) и android:видимость.
public static int getVisiblePercent(View v) { if (v.isShown()) { Rect r = new Rect(); v.getGlobalVisibleRect(r); double sVisible = r.width() * r.height(); double sTotal = v.getWidth() * v.getHeight(); return (int) (100 * sVisible / sTotal); } else { return -1; } }
мое решение-использовать
NestedScrollViewспирального элемента:final Rect scrollBounds = new Rect(); scroller.getHitRect(scrollBounds); scroller.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() { @Override public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) { if (myBtn1 != null) { if (myBtn1.getLocalVisibleRect(scrollBounds)) { if (!myBtn1.getLocalVisibleRect(scrollBounds) || scrollBounds.height() < myBtn1.getHeight()) { Log.i(TAG, "BTN APPEAR PARCIALY"); } else { Log.i(TAG, "BTN APPEAR FULLY!!!"); } } else { Log.i(TAG, "No"); } } } }); }
можно использовать
FocusAwareScrollViewкоторый уведомляет, когда вид становится видимым:FocusAwareScrollView focusAwareScrollView = (FocusAwareScrollView) findViewById(R.id.focusAwareScrollView); if (focusAwareScrollView != null) { ArrayList<View> viewList = new ArrayList<>(); viewList.add(yourView1); viewList.add(yourView2); focusAwareScrollView.registerViewSeenCallBack(viewList, new FocusAwareScrollView.OnViewSeenListener() { @Override public void onViewSeen(View v, int percentageScrolled) { if (v == yourView1) { // user have seen view1 } else if (v == yourView2) { // user have seen view2 } } }); }вот это класс :
import android.content.Context; import android.graphics.Rect; import android.support.v4.widget.NestedScrollView; import android.util.AttributeSet; import android.view.View; import java.util.ArrayList; import java.util.List; public class FocusAwareScrollView extends NestedScrollView { private List<OnScrollViewListener> onScrollViewListeners = new ArrayList<>(); public FocusAwareScrollView(Context context) { super(context); } public FocusAwareScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public FocusAwareScrollView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public interface OnScrollViewListener { void onScrollChanged(FocusAwareScrollView v, int l, int t, int oldl, int oldt); } public interface OnViewSeenListener { void onViewSeen(View v, int percentageScrolled); } public void addOnScrollListener(OnScrollViewListener l) { onScrollViewListeners.add(l); } public void removeOnScrollListener(OnScrollViewListener l) { onScrollViewListeners.remove(l); } protected void onScrollChanged(int l, int t, int oldl, int oldt) { for (int i = onScrollViewListeners.size() - 1; i >= 0; i--) { onScrollViewListeners.get(i).onScrollChanged(this, l, t, oldl, oldt); } super.onScrollChanged(l, t, oldl, oldt); } @Override public void requestChildFocus(View child, View focused) { super.requestChildFocus(child, focused); } private boolean handleViewSeenEvent(View view, int scrollBoundsBottom, int scrollYOffset, float minSeenPercentage, OnViewSeenListener onViewSeenListener) { int loc[] = new int[2]; view.getLocationOnScreen(loc); int viewBottomPos = loc[1] - scrollYOffset + (int) (minSeenPercentage / 100 * view.getMeasuredHeight()); if (viewBottomPos <= scrollBoundsBottom) { int scrollViewHeight = this.getChildAt(0).getHeight(); int viewPosition = this.getScrollY() + view.getScrollY() + view.getHeight(); int percentageSeen = (int) ((double) viewPosition / scrollViewHeight * 100); onViewSeenListener.onViewSeen(view, percentageSeen); return true; } return false; } public void registerViewSeenCallBack(final ArrayList<View> views, final OnViewSeenListener onViewSeenListener) { final boolean[] viewSeen = new boolean[views.size()]; FocusAwareScrollView.this.postDelayed(new Runnable() { @Override public void run() { final Rect scrollBounds = new Rect(); FocusAwareScrollView.this.getHitRect(scrollBounds); final int loc[] = new int[2]; FocusAwareScrollView.this.getLocationOnScreen(loc); FocusAwareScrollView.this.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() { boolean allViewsSeen = true; @Override public void onScrollChange(NestedScrollView v, int x, int y, int oldx, int oldy) { for (int index = 0; index < views.size(); index++) { //Change this to adjust criteria float viewSeenPercent = 1; if (!viewSeen[index]) viewSeen[index] = handleViewSeenEvent(views.get(index), scrollBounds.bottom, loc[1], viewSeenPercent, onViewSeenListener); if (!viewSeen[index]) allViewsSeen = false; } //Remove this if you want continuous callbacks if (allViewsSeen) FocusAwareScrollView.this.setOnScrollChangeListener((NestedScrollView.OnScrollChangeListener) null); } }); } }, 500); } }
Я знаю, что это очень поздно. Но у меня есть хорошее решение. Ниже приведен фрагмент кода для получения процент видимости вида в виде прокрутки.
прежде всего установите сенсорный прослушиватель на вид прокрутки для получения обратного вызова для остановки прокрутки.
@Override public boolean onTouch(View v, MotionEvent event) { switch ( event.getAction( ) ) { case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: new Handler().postDelayed(new Runnable() { @Override public void run() { if(mScrollView == null){ mScrollView = (ScrollView) findViewById(R.id.mScrollView); } int childCount = scrollViewRootChild.getChildCount(); //Scroll view location on screen int[] scrollViewLocation = {0,0}; mScrollView.getLocationOnScreen(scrollViewLocation); //Scroll view height int scrollViewHeight = mScrollView.getHeight(); for (int i = 0; i < childCount; i++){ View child = scrollViewRootChild.getChildAt(i); if(child != null && child.getVisibility() == View.VISIBLE){ int[] viewLocation = new int[2]; child.getLocationOnScreen(viewLocation); int viewHeight = child.getHeight(); getViewVisibilityOnScrollStopped(scrollViewLocation, scrollViewHeight, viewLocation, viewHeight, (String) child.getTag(), (childCount - (i+1))); } } } }, 150); break; } return false; }в приведенном выше фрагменте кода мы получаем обратные вызовы для событий scroll view touch и публикуем запуск после 150 миллисов(не обязательно) после остановки обратного вызова для прокрутки. В этом runnable мы получим местоположение scroll просмотр на экране и прокрутка высоты просмотра. Затем получите прямой дочерний экземпляр viewgroup представления прокрутки и получите дочерние счетчики. В моем случае прямой дочерний элемент прокрутки - LinearLayout с именем scrollViewRootChild. Затем повторите все дочерние представления scrollViewRootChild. В приведенном выше фрагменте кода Вы можете видеть, что я получаю расположение ребенка на экране в целочисленном массиве с именем viewLocation, получить высоту зрения в имени переменной viewHeight. Затем я вызвал частный метод getViewVisibilityOnScrollStopped. Вы можете получить представление о внутренней работе этого метода, прочитав документацию.
/** * getViewVisibilityOnScrollStopped * @param scrollViewLocation location of scroll view on screen * @param scrollViewHeight height of scroll view * @param viewLocation location of view on screen, you can use the method of view claas's getLocationOnScreen method. * @param viewHeight height of view * @param tag tag on view * @param childPending number of views pending for iteration. */ void getViewVisibilityOnScrollStopped(int[] scrollViewLocation, int scrollViewHeight, int[] viewLocation, int viewHeight, String tag, int childPending) { float visiblePercent = 0f; int viewBottom = viewHeight + viewLocation[1]; //Get the bottom of view. if(viewLocation[1] >= scrollViewLocation[1]) { //if view's top is inside the scroll view. visiblePercent = 100; int scrollBottom = scrollViewHeight + scrollViewLocation[1]; //Get the bottom of scroll view if (viewBottom >= scrollBottom) { //If view's bottom is outside from scroll view int visiblePart = scrollBottom - viewLocation[1]; //Find the visible part of view by subtracting view's top from scrollview's bottom visiblePercent = (float) visiblePart / viewHeight * 100; } }else{ //if view's top is outside the scroll view. if(viewBottom > scrollViewLocation[1]){ //if view's bottom is outside the scroll view int visiblePart = viewBottom - scrollViewLocation[1]; //Find the visible part of view by subtracting scroll view's top from view's bottom visiblePercent = (float) visiblePart / viewHeight * 100; } } if(visiblePercent > 0f){ visibleWidgets.add(tag); //List of visible view. } if(childPending == 0){ //Do after iterating all children. } }Если вы чувствуете какие-либо улучшения в этом коде, пожалуйста, внесите свой вклад.
С помощью @Qberticus ответ, который можно было бы поставить точку, но отличный кстати, мне купить кучу кодов, чтобы проверить, если всякий раз, когда представление ScrollView называется и есть прокручивать его вызовет @Qberticus ответ, и вы можете делать все что угодно, в моем случае у меня есть социальные сети, содержащие видео, когда вид нарисовал на экране я играю на видео же идея, как facebook и Instagram. Вот код:
mainscrollview.getViewTreeObserver().addOnScrollChangedListener(new OnScrollChangedListener() { @Override public void onScrollChanged() { //mainscrollview is my scrollview that have inside it a linearlayout containing many child views. Rect bounds = new Rect(); for(int xx=1;xx<=postslayoutindex;xx++) { //postslayoutindex is the index of how many posts are read. //postslayoutchild is the main layout for the posts. if(postslayoutchild[xx]!=null){ postslayoutchild[xx].getHitRect(bounds); Rect scrollBounds = new Rect(); mainscrollview.getDrawingRect(scrollBounds); if(Rect.intersects(scrollBounds, bounds)) { vidPreview[xx].startPlaywithoutstoppping(); //I made my own custom video player using textureview and initialized it globally in the class as an array so I can access it from anywhere. } else { } } } } });
Comments