【问题标题】:android - way to be informed when a view is shown within a scrollview?android - 在滚动视图中显示视图时通知的方式?
【发布时间】:2012-03-24 14:12:25
【问题描述】:

我有一个简单的问题:

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

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

【问题讨论】:

    标签: android view hide listener scrollview


    【解决方案1】:

    子类化您正在使用的视图类(我为 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 的实例上,将在视图进入或离开窗口的可见部分(视口)时通知您。

    【讨论】:

    • 是否可以只为容器本身做,而不是扩展可以在其中的每个视图?
    • 尝试将 OnScrollChangeListener 添加到容器的 ViewTreeObserver 中,看看是否以及何时调用它。如果未调用,则需要扩展子视图。
    • 我找到了更好的方法。现在将发布它。
    • OnAttachStateChangeListener for api >= 12
    【解决方案2】:

    你可以这样做:

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

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

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

    这是一个基本的方法,但它会起作用,它的速度取决于你屏幕上的内容等,但它会让你朝着正确的方向前进

    【讨论】:

    • 是否可以扩展 scrollView 并检查视图何时位于 scrollView 本身的边界框中,并为每个视图存储它们以前是否在其中?
    【解决方案3】:

    我找到了一种很好的方式来通知我在这里询问的内容。

    它适用于具有垂直 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);
        }
      }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-26
      • 2022-01-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多