Android: ¿cómo comprobar si una Vista dentro de ScrollView está visible?

Tengo un ScrollView que contiene una serie de Views . Me gustaría poder determinar si una vista es actualmente visible (si alguna parte de ella es mostrada actualmente por ScrollView ). Esperaría que el código a continuación para hacer esto, sorprendentemente no lo hace:

 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 } 

Use View#getHitRect lugar de View#getDrawingRect en la vista que está probando. Puede utilizar View#getDrawingRect en ScrollView lugar de calcular explícitamente.

Código de la View#getDrawingRect :

  public void getDrawingRect(Rect outRect) { outRect.left = mScrollX; outRect.top = mScrollY; outRect.right = mScrollX + (mRight - mLeft); outRect.bottom = mScrollY + (mBottom - mTop); } 

Código de la View#getHitRect :

 public void getHitRect(Rect outRect) { outRect.set(mLeft, mTop, mRight, mBottom); } 

Esto funciona:

 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 } 

Si desea detectar que la vista es TOTALMENTE 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; } else { return false; } } 

Para expandir un poco la respuesta de Bill Mote usando getLocalVisibleRect, es posible que desee comprobar si la vista sólo es parcialmente visible:

 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 } 

Me enfrenté al mismo problema hoy. Mientras Googling y leyendo la referencia de Android encontré este post y un método que terminé usando en su lugar;

 public final boolean getLocalVisibleRect (Rect r) 

Niza de ellos no sólo para proporcionar Rect sino también booleano indicando si Ver visible en absoluto. En el lado negativo este método es indocumentado 🙁

 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; } } 

Mi solución es usar NestedScrollView Elemento de desplazamiento:

  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"); } } } }); } 

Si desea detectar si su View es totalmente visible , pruebe con este método:

 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. } } 

Estrictamente hablando, puedes obtener la visibilidad de una vista con:

 if (myView.getVisibility() == View.VISIBLE) { //VISIBLE } else { //INVISIBLE } 

Los posibles valores constantes de la visibilidad en una Vista son:

VISIBLE Esta vista es visible. Utilice setVisibility (int) y android: visibilidad.

INVISIBLE Esta vista es invisible, pero todavía ocupa espacio para propósitos de diseño. Utilice setVisibility (int) y android: visibilidad.

GONE Esta vista es invisible, y no toma ningún espacio para propósitos de diseño. Utilice setVisibility (int) y android: visibilidad.

Puede utilizar el FocusAwareScrollView que notifica cuando la vista se vuelve visible:

 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 } } }); } 

Aquí está la clase:

 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); } } 

Utilizando @Qberticus respuesta que fue al punto, pero gran btw, compined un montón de códigos para comprobar si cada vez que un scrollview se llama y se desplazó que desencadenar la respuesta @ Quberticus y puede hacer lo que quieras, en mi caso tengo un Redes sociales que contienen videos por lo que cuando la vista se dibuja en la pantalla que reproducir el video misma idea como facebook e Instagram. Aquí está el código:

 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 { } } } } }); 
FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.