检测适配器中的滚动方向(上/下)

Posted

技术标签:

【中文标题】检测适配器中的滚动方向(上/下)【英文标题】:Detecting the scrolling direction in the adapter (up/down) 【发布时间】:2012-08-20 08:18:39 【问题描述】:

我正在尝试在我的项目中模仿 Google Plus 应用程序,因为它现在似乎是参考。

滚动时的列表视图效果真的很好,我想做类似的事情。

我从 LayoutAnimationController 开始 http://android-er.blogspot.be/2009/10/listview-and-listactivity-layout.html

LayoutAnimationController controller 
   = AnimationUtils.loadLayoutAnimation(
     this, R.anim.list_layout_controller);
  getListView().setLayoutAnimation(controller);

这似乎很糟糕,因为并非所有元素都是动画的:

所以我最终使用了适配器的 getView 并使用了这个:

        AnimationSet set = new AnimationSet(true);

        Animation animation = new AlphaAnimation(0.0f, 1.0f);
        animation.setDuration(800);
        set.addAnimation(animation);

        animation = new TranslateAnimation(
            Animation.RELATIVE_TO_SELF, 0.0f,Animation.RELATIVE_TO_SELF, 0.0f,
            Animation.RELATIVE_TO_SELF, 1.0f,Animation.RELATIVE_TO_SELF, 0.0f
        );
        animation.setDuration(600);
        set.addAnimation(animation);

        row.startAnimation(set);

结果太棒了,我真的很高兴!

不幸的是,它仅在我从列表的顶部滚动到底部时才有效!

我想让它在另一边滚动时工作,我需要稍微改变一下 TranslateAnimation。

所以我的问题是,有没有办法检测我在适配器中是向上还是向下滚动?

【问题讨论】:

您的示例中的“行”变量是什么? 您可以在这篇文章中看到完整的解决方案:***.com/questions/16791100/… 另一个可行的解决方案***.com/a/34788591/1939564 【参考方案1】:

OnScrollListener 分配给您的ListView。创建一个标志,指示用户是向上还是向下滚动。通过检查当前 first 可见项位置是否大于或小于前一个 first 可见项位置,为标志设置适当的值。将该支票放入onScrollStateChanged()

示例代码:

private int mLastFirstVisibleItem;
private boolean mIsScrollingUp;

public void onScrollStateChanged(AbsListView view, int scrollState) 
    final ListView lw = getListView();

    if (view.getId() == lw.getId()) 
        final int currentFirstVisibleItem = lw.getFirstVisiblePosition();

        if (currentFirstVisibleItem > mLastFirstVisibleItem) 
            mIsScrollingUp = false;
         else if (currentFirstVisibleItem < mLastFirstVisibleItem) 
            mIsScrollingUp = true;
        

        mLastFirstVisibleItem = currentFirstVisibleItem;
     

检查mIsScrollingUpgetView()中是真还是假,并相应地分配动画。

【讨论】:

我写了我的解决方案,但你的似乎也很正确。我想知道哪个“更好”,我想我需要更少的代码,但也许使用这个位置更“脏” @Waza_Be:是的,除了我的回答添加了更多代码之外,没有太大区别。但是无论如何,例如,如果您扩展并希望在用户将ListView 扔掉时跳过动画,那么在OnScrollListener 内进行控制可能最合适。 :-) 这并不能真正告诉您是向上还是向下滚动,只有在您向上或向下滚动时才会告诉您,因为第一个可见项目在它不可见之前不会改变。在我的情况下,我实际上需要一个常量回调,因为列表正在滚动。 添加克里斯托弗的评论。这是获得有关您是否滚动的更直接反馈的简单方法。您可以根据列表视图的第一个子项的位置来执行此操作。它也适用于网格视图。 listView.getChildAt(0).getTop() 不错。虽然如果列表项的高度很大,这可能不是最优雅的解决方案。【参考方案2】:

我最终这样做了:

@Override
public View getView(int position, View convertView, ViewGroup parent) 
    Log.i("",position+" - "+lastposition);

    if (position >= lastposition)
        animation = new TranslateAnimation(Animation.RELATIVE_TO_SELF,
                0.0f, Animation.RELATIVE_TO_SELF, 0.0f,
                Animation.RELATIVE_TO_SELF, 1.0f,
                Animation.RELATIVE_TO_SELF, 0.0f);
    else
        animation = new TranslateAnimation(Animation.RELATIVE_TO_SELF,
                0.0f, Animation.RELATIVE_TO_SELF, 0.0f,
                Animation.RELATIVE_TO_SELF, -1.0f,
                Animation.RELATIVE_TO_SELF, 0.0f);

    animation.setDuration(600);
    set.addAnimation(animation);

    row.startAnimation(set);

    lastposition = position;


【讨论】:

甜蜜。这是一个更好的解决方案。 lastposition 来自哪里?【参考方案3】:

更复杂的解决方案(在列表视图中处理长项目高度)

    创建自定义列表视图

    public class ScrollDetectingListView extends ListView 
        public ScrollDetectingListView(Context context) 
            super(context);
        
    
        public ScrollDetectingListView(Context context, AttributeSet attrs) 
            super(context,attrs);
         
    
        public ScrollDetectingListView(Context context, AttributeSet attrs, int defStyle) 
            super(context, attrs, defStyle);
        
    
        //we need this protected method for scroll detection
        public int getVerticalScrollOffset() 
            return computeVerticalScrollOffset();
        
    
    

    覆盖 onScroll

        listView.setOnScrollListener(new AbsListView.OnScrollListener() 
    
        private int mInitialScroll = 0;
    
        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) 
    
        
    
        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) 
            int scrolledOffset = listView.getVerticalScrollOffset();
            if (scrolledOffset!=mInitialScroll) 
                //if scroll position changed
                boolean scrollUp = (scrolledOffset - mInitialScroll) < 0;
                mInitialScroll = scrolledOffset;
            
        
        );
    

【讨论】:

迄今为止最好的答案。这解决了 Listview 回收其视图使“listview.getChildAt(0).getTop()”不可靠时的问题 listView.getVerticalScrollOffset() 未找到【参考方案4】:

接受的答案并没有真正“检测到”向上或向下滚动。如果当前可见项目真的很大,它将不起作用。使用onTouchListener 是可行的方法。

这是我使用的代码sn-p:

listView.setOnTouchListener(new View.OnTouchListener() 
    float initialY, finalY;
    boolean isScrollingUp;

    @Override
    public boolean onTouch(View v, MotionEvent event) 
        int action = MotionEventCompat.getActionMasked(event);

        switch(action) 
            case (MotionEvent.ACTION_DOWN):
                initialY = event.getY();
            case (MotionEvent.ACTION_UP):
                finalY = event.getY();

                if (initialY < finalY) 
                    Log.d(TAG, "Scrolling up");
                    isScrollingUp = true;
                 else if (initialY > finalY) 
                    Log.d(TAG, "Scrolling down");
                    isScrollingUp = false;
                
            default:
        

        if (isScrollingUp) 
            // do animation for scrolling up
         else 
            // do animation for scrolling down
        

        return false; // has to be false, or it will freeze the listView
    
);

【讨论】:

想象用户在没有从屏幕上分离他们的“工具”的情况下上下滚动,在这种情况下,初始值不会被更新,你会得到isScrollingUp标志的错误结果。您应该始终检查currentprevious 点。不是最终的finalinitial【参考方案5】:

这是我遇到的最简单、最简单的方法。它就像一个魅力。

view.addOnScrollListener(new View.OnScrollListener() 
                @Override
                public void onScrolled(@NonNull View view, int dx, int dy) 
                    if (dy > 0) 
                        //Scrolling down
                     else if (dy < 0) 
                        //Scrolling up
                    
                
            );

【讨论】:

【参考方案6】:

试试这个。我希望它对你有帮助。来自@Gal Rom 答案的逻辑。

lv.setOnScrollListener(new OnScrollListener() 
        private int mLastFirstVisibleItem;

        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) 

        

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem,
                int visibleItemCount, int totalItemCount) 

            if(mLastFirstVisibleItem<firstVisibleItem)
            
                Log.i("SCROLLING DOWN","TRUE");
            
            if(mLastFirstVisibleItem>firstVisibleItem)
            
                Log.i("SCROLLING UP","TRUE");
            
            mLastFirstVisibleItem=firstVisibleItem;

        
    );

【讨论】:

【参考方案7】:

这是我的方法:它可以让您更直接地反馈您滚动了多少: OnScroll,您只需获取列表中第一项的 Top 位置即可。立即获得实际的滚动位置信息非常可靠。

listView.getChildAt(0).getTop()

【讨论】:

在适配器的 getView 中,您可以找到 lisView 作为 parent 参数。【参考方案8】:

我使用了这个更简单的解决方案:

public class ScrollDetectingListView extends ListView

...

setOnScrollListener( new OnScrollListener() 


    private int mInitialScroll = 0;

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) 
    
        int scrolledOffset = computeVerticalScrollOffset();

        boolean scrollUp = scrolledOffset > mInitialScroll;
        mInitialScroll = scrolledOffset;
    


    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) 


    


【讨论】:

什么是 computeVerticalScroll()? developer.android.com/reference/android/widget/AbsListView.html中的受保护方法【参考方案9】:
list.setOnScrollListener(new OnScrollListener() 
        int last_item;
        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) 

        

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem,
                int visibleItemCount, int totalItemCount) 
            if(last_item<firstVisibleItem+visibleItemCount-1)
                System.out.println("List is scrolling upwards");
            
            else if(last_item>firstVisibleItem+visibleItemCount-1)
                System.out.println("List is scrolling downwards");
            
             last_item = firstVisibleItem+visibleItemCount-1;
        
    );

根据最后一个可见项目的位置,我决定 Listview 是向上还是向下。

【讨论】:

【参考方案10】:

不依赖于视图/等位置的通用解决方案。只需检查垂直滚动偏移量并将其与之前的滚动偏移量进行比较。如果新值大于旧值,则用户向下滚动,反之亦然。

// [START check vertical scroll direction]
int oldScrollOffset = 0;

listView.setOnScrollChangeListener(new View.OnScrollChangeListener() 
        @Override
        public void onScrollChange(View view, int i, int i1, int i2, int i3) 

            Boolean scrollDirectionDown;

            int newScrollOffset = listView.computeVerticalScrollOffset();

            if (newScrollOffset > oldScrollOffset) 
                scrollDirectionDown = true;
             else 
                scrollDirectionDown = false;
            

            oldScrollOffset = newScrollOffset;

            if (scrollDirectionDown) 
                // Update accordingly for scrolling down
                Log.d(TAG, "scrolling down");
             else 
                // Update accordingly for scrolling up
                Log.d(TAG, "scrolling up");
            

    );
// [END check vertical scroll direction]

【讨论】:

【参考方案11】:
view.setOnTouchListener(new View.OnTouchListener() 

        private long startClickTime;
        float y0 = 0;
        float y1 = 0;
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) 

            if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) 
                y0 = motionEvent.getY();
                startClickTime = System.currentTimeMillis();

             else if (motionEvent.getAction() == MotionEvent.ACTION_UP) 
                if (System.currentTimeMillis() - startClickTime < ViewConfiguration.getTapTimeout()) 

                    // Touch was a simple tap. Do whatever.

                 else 
                    y1 = motionEvent.getY();
                    // Touch was a not a simple tap.
                    if (y1 - y0 > 50) 
                        // this is down
                     else if (y1 - y0 < 50) 
                        Log.d("daniY", "-");
                        // this is up
                    
                

            

            return true;
        

    );

这对我有用,这将有助于检测我认为的所有视图的滚动方向。

【讨论】:

以上是关于检测适配器中的滚动方向(上/下)的主要内容,如果未能解决你的问题,请参考以下文章

云原生快速发展中,安全检测如何适配?

滚动时显示随机项目的getView

滚动后未选中 ListView 适配器中的 Android 复选框

滚动 Recycler View 时有意丢失数据

检测ListView中的TextView是不是为椭圆

使用 Entrypoints Dagger hilt 检测活动中的适配器 onclick 侦听器方法