android - 在滚动视图中显示视图时被通知的方式?

时间:2012-03-24 14:12:25

标签: android view hide listener scrollview

我有一个简单的问题:

假设我在scrollView(或horizo​​ntalScrollView)上有一些视图。 有没有办法添加一个监听器,告诉我何时这样的视图进入可见区域内外?

我见过的唯一类似问题是: Android: how to check if a View inside of ScrollView is visible? 但是我希望在发生这样的事件时被告知(变得隐藏/可见)。

3 个答案:

答案 0 :(得分:3)

对您正在使用的视图类进行子类化(我为ImageView做了这个,因为我只是将它们添加到我的滚动视图中):

public class PeekImageView extends ImageView implements ViewTreeObserver.OnScrollChangedListener {
    private static final String LOG_TAG = "PeekImageView";
    private InViewportListener inViewportListener;
    private boolean isInViewport = false;

    public PeekImageView(Context context) {
        super(context);
    }

    public PeekImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public PeekImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public interface InViewportListener {
        void onViewportEnter(PeekImageView view);
        void onViewportExit(PeekImageView view);
    }

    public void setInViewportListener(InViewportListener listener) {
        this.inViewportListener = listener;
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        ViewTreeObserver vto = getViewTreeObserver();
        if (vto != null) {
            vto.addOnScrollChangedListener(this);
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        ViewTreeObserver vto = getViewTreeObserver();
        if (vto != null) {
            vto.removeOnScrollChangedListener(this);
        }
    }

    @Override
    public void onScrollChanged() {
        Rect bounds = new Rect();
        boolean inViewport = getLocalVisibleRect(bounds);
        Log.d(LOG_TAG, "is in view " + bounds + " : " + inViewport + " ; " + bounds);
        if (inViewportListener != null && isInViewport != inViewport) {
            if (inViewport) {
                inViewportListener.onViewportEnter(this);
            } else {
                inViewportListener.onViewportExit(this);
            }
        }
        isInViewport = inViewport;
    }
}

只要视图进入或离开窗口的可见部分(视口),将InViewportListener附加到此PeekImageView的实例就会通知您。

答案 1 :(得分:1)

您可以执行以下操作:

1)保留ScrollView中包含的列表/视图数组。

2)在滚动视图上设置一个监听器,以便更改滚动时:Synchronise ScrollView scroll positions - android

3)在侦听器中使用Android: how to check if a View inside of ScrollView is visible?方法循环浏览这些视图以查看它们是否已经离开了屏幕

这是一种基本的方法,但它会起作用,它的速度有多快取决于你的屏幕上的什么等,但它会让你朝着正确的方向前进

答案 2 :(得分:0)

我找到了一个很好的方式来通知我在这里问过什么。

它适用于带有垂直LinearLayout的scrollView,但是如果你希望你也可以使其适用于其他情况,具体取决于具体情况。

我不确定我是否应该处理onSizeChanged()方法,如果是,那么该做什么,但在所有其他情况下,这段代码工作正常。

这是代码:

MainActivity.java(用于测试):

public class MainActivity extends Activity
  {
  @Override
  protected void onCreate(final Bundle savedInstanceState)
    {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    final CustomScrollView scrollView=(CustomScrollView)findViewById(R.id.scrollView1);
    scrollView.setOnChildViewVisibilityChangedListener(new onChildViewVisibilityChangedListener()
      {
        @Override
        public void onChildViewVisibilityChanged(final int index,final View v,final boolean becameVisible)
          {
          Log.d("Applog","index:"+index+" visible:"+becameVisible);
          }
      });
    final ViewGroup container=(ViewGroup)findViewById(R.id.linearLayout);
    for(int i=0;i<20;++i)
      {
      final TextView tv=new TextView(this);
      tv.setText("item "+i);
      tv.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,300));
      container.addView(tv);
      }
    }
  }

<强> activity_main.xml中

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <com.example.scrollviewvisibilitydetector.CustomScrollView
        android:id="@+id/scrollView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"  >

        <LinearLayout android:id="@+id/linearLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" >
        </LinearLayout>
    </com.example.scrollviewvisibilitydetector.CustomScrollView>

</RelativeLayout>

CustomScrollView.java(真正的交易......):

public class CustomScrollView extends ScrollView
  {
  Set<Integer>                         _shownViewsIndices =new HashSet<Integer>();
  onChildViewVisibilityChangedListener _onChildViewVisibilityChangedListener;

  public interface onChildViewVisibilityChangedListener
    {
    public void onChildViewVisibilityChanged(int index,View v,boolean becameVisible);
    }

  public CustomScrollView(final Context context)
    {
    super(context);
    }

  public CustomScrollView(final Context context,final AttributeSet attrs)
    {
    super(context,attrs);
    }

  public CustomScrollView(final Context context,final AttributeSet attrs,final int defStyle)
    {
    super(context,attrs,defStyle);
    }

  public void setOnChildViewVisibilityChangedListener(final onChildViewVisibilityChangedListener onChildViewVisibilityChangedListener)
    {
    _onChildViewVisibilityChangedListener=onChildViewVisibilityChangedListener;
    }

  @Override
  protected void onLayout(final boolean changed,final int l,final int t,final int r,final int b)
    {
    super.onLayout(changed,l,t,r,b);
    checkViewsVisibility(l,t);
    }

  private void checkViewsVisibility(final int l,final int t)
    {
    final ViewGroup viewGroup=(ViewGroup)getChildAt(0);
    final int childCount=viewGroup.getChildCount();
    if(childCount==0)
      return;
    final int parentBottom=t+getHeight();
    // prepare to use binary search to find a view that is inside the bounds
    int min=0,max=childCount-1,piv=-1;
    int childTop,childBottom;
    View v;
    // check previously shown views
    for(final Iterator<Integer> iterator=_shownViewsIndices.iterator();iterator.hasNext();)
      {
      final Integer cur=iterator.next();
      v=viewGroup.getChildAt(cur);
      childTop=v.getTop();
      childBottom=v.getBottom();
      if(childTop<=parentBottom&&childBottom>=t)
        {
        if(piv==-1)
          piv=cur;
        }
      else
        {
        if(_onChildViewVisibilityChangedListener!=null)
          _onChildViewVisibilityChangedListener.onChildViewVisibilityChanged(cur,v,false);
        iterator.remove();
        }
      }
    if(piv==-1)
      {
      // check first view
      v=viewGroup.getChildAt(min);
      childTop=v.getTop();
      childBottom=v.getBottom();
      if(childTop<=parentBottom&&childBottom>=t)
        piv=min;
      else
        {
        // check last view
        v=viewGroup.getChildAt(max);
        childTop=v.getTop();
        childBottom=v.getBottom();
        if(childTop<=parentBottom&&childBottom>=t)
          piv=min;
        }
      if(piv==-1)
        while(true)
          {
          piv=(min+max)/2;
          v=viewGroup.getChildAt(piv);
          childTop=v.getTop();
          childBottom=v.getBottom();
          if(childTop<=parentBottom&&childBottom>=t)
            break;
          if(max-min==1)
            return;
          if(childBottom<t)
            // view above bounds
            min=piv;
          else max=piv;
          }
      }
    //
    for(int i=piv;i<childCount;++i)
      {
      v=viewGroup.getChildAt(i);
      childTop=v.getTop();
      childBottom=v.getBottom();
      // _shownViewsIndices.
      if(childTop<=parentBottom&&childBottom>=t&&!_shownViewsIndices.contains(i))
        {
        _shownViewsIndices.add(i);
        if(_onChildViewVisibilityChangedListener!=null)
          _onChildViewVisibilityChangedListener.onChildViewVisibilityChanged(i,v,true);
        }
      }
    for(int i=piv-1;i>=0;--i)
      {
      v=viewGroup.getChildAt(i);
      childTop=v.getTop();
      childBottom=v.getBottom();
      if(childTop<=parentBottom&&childBottom>=t&&!_shownViewsIndices.contains(i))
        {
        _shownViewsIndices.add(i);
        if(_onChildViewVisibilityChangedListener!=null)
          _onChildViewVisibilityChangedListener.onChildViewVisibilityChanged(i,v,true);
        }
      }
    }

  @Override
  protected void onScrollChanged(final int l,final int t,final int oldl,final int oldt)
    {
    super.onScrollChanged(l,t,oldl,oldt);
    checkViewsVisibility(l,t);
    }
  }