纠正:Android RecyclerView滚动到指定位置并置顶(滚动方法、移动置顶、定位滑动到指定位置item)

最近博主发现让RecyclerView滑动到某一位置并置顶的博客一大堆,抄的是完全一模一样。此外,虽然这些博客“解决”了这些问题,但这种解决方案过于浅显、粗暴,甚至都违背了开发思想。遂在此纠正这种错误。

RecyclerView提供了几种移动的方法

scrollToPosition

scrollTo

scrollBy

smoothScrollBy

smoothScrollToPosition

由于多数博客鱼龙混杂,本博客如果让你非常满意或解决了大家的根本性问题,希望多多支持在下方点赞和回复一下,举手之劳方便大家。

虽然里面有移动到指定位置的方法scrollToPosition(直接闪现至某一位位置)、smoothScrollToPosition(惯性滑动至某一位置)但是貌似都不尽人意,因为他们只保证能够展示出来,并不能保证在第一位。而此时如果你打开源码就会发现,原来全都是调用的LayoutManager移动方法,首先打开我们耳熟能详的LinearLayoutManager惊喜就在眼前
scrollToPosition

在scrollToPosition旁边有木有一个很像的方法

        @Override
        public void scrollToPosition(int position) {
            mPendingScrollPosition = position;
            mPendingScrollPositionOffset = INVALID_OFFSET;
            if (mPendingSavedState != null) {
                mPendingSavedState.invalidateAnchor();
            }
            requestLayout();
        }
        public void scrollToPositionWithOffset(int position, int offset) {
            mPendingScrollPosition = position;
            mPendingScrollPositionOffset = offset;
            if (mPendingSavedState != null) {
                mPendingSavedState.invalidateAnchor();
            }
            requestLayout();
        }

当看到offset时也许就会明白:没错,这个就是item移动后相对父控件的偏移值,传入0就会有你想要的
smoothScrollToPosition

        @Override
        public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
                int position) {
            LinearSmoothScroller linearSmoothScroller =
                    new LinearSmoothScroller(recyclerView.getContext());
            linearSmoothScroller.setTargetPosition(position);
            startSmoothScroll(linearSmoothScroller);
        }

而smoothScrollToPosition原来仅仅是new了一个LinearSmoothScroller然后调用startSmoothScroll

我们只需要自定义一个LinearSmoothScroller,之前写的有点仓促,仔细看LinearSmoothScroller的源码发现,其实谷歌已经埋下了伏笔,既然纠正就纠正到底吧

    public class TopSmoothScroller extends LinearSmoothScroller {
        TopSmoothScroller(Context context) {
            super(context);
        }
        @Override
        protected int getHorizontalSnapPreference() {
            return SNAP_TO_START;//具体见源码注释
        }
        @Override
        protected int getVerticalSnapPreference() {
            return SNAP_TO_START;//具体见源码注释
        }
    }

 然后调用LinearLayoutManager的startSmoothScroll即可

    final TopSmoothScroller mScroller = new TopSmoothScroller(getActivity());
    mScroller.setTargetPosition(integer);
    mManager.startSmoothScroll(mScroller);

是否恍然大悟:其实我们并不需要什么bd,也不需要修改LinearLayoutManager,仅仅需要几行代码即可解决。

多看看源码,多思考思考,你也可以。

真理往往掌握在少数人手中,你是不是其中一员呢?
对于一些不会或质疑的人,此处追加demo:

    public class RvHuaDongActivity extends BaseActivity {
        @BindView(R.id.rv_rvhuadong)
        RecyclerView mRv;
        private LinearLayoutManager mManager;
     
        //此处等于setContentView
        @Override
        protected int getLayouRes() {
            return R.layout.activity_rv_hua_dong;
        }
     
        //此处等于onCreate
        @Override
        protected void initData() {
            List<String> list = new ArrayList<>();
            for (int i = 0; i < 100; i++) {
                list.add("position" + i);
            }
            mManager = new LinearLayoutManager(this);
            mRv.setLayoutManager(mManager);
            mRv.setAdapter(new MyAdapter(list));
        }
     
        @Override
        protected void setListener() {
        }
     
        @OnClick({R.id.tv_rvhuadong_GuanXing_1, R.id.tv_rvhuadong_GuanXing_2, R.id.tv_rvhuadong_GuanXing_3,
                R.id.tv_rvhuadong_ShanXian_1, R.id.tv_rvhuadong_ShanXian_2, R.id.tv_rvhuadong_ShanXian_3})
        public void onViewClicked(View view) {
            switch (view.getId()) {
                case R.id.tv_rvhuadong_GuanXing_1:
                    int position1 = (int) (Math.random() * 100);
                    Toast.makeText(this, "滑到:" + position1, Toast.LENGTH_SHORT).show();
                    LinearSmoothScroller s1 = new TopSmoothScroller(getActivity());
                    s1.setTargetPosition(position1);
                    mManager.startSmoothScroll(s1);
                    break;
                case R.id.tv_rvhuadong_GuanXing_2:
                    LinearSmoothScroller s2 = new TopSmoothScroller(getActivity());
                    s2.setTargetPosition(20);
                    mManager.startSmoothScroll(s2);
                    break;
                case R.id.tv_rvhuadong_GuanXing_3:
                    LinearSmoothScroller s3 = new TopSmoothScroller(getActivity());
                    s3.setTargetPosition(99);
                    mManager.startSmoothScroll(s3);
                    break;
                case R.id.tv_rvhuadong_ShanXian_1:
                    int position2 = (int) (Math.random() * 100);
                    Toast.makeText(this, "闪到:" + position2, Toast.LENGTH_SHORT).show();
                    mManager.scrollToPositionWithOffset(position2, 0);
                    break;
                case R.id.tv_rvhuadong_ShanXian_2:
                    mManager.scrollToPositionWithOffset(20, 0);
                    break;
                case R.id.tv_rvhuadong_ShanXian_3:
                    mManager.scrollToPositionWithOffset(99, 0);
                    break;
            }
        }
     
        private class MyAdapter extends RecyclerView.Adapter<BaseViewHolder> {
            private final List<String> mList;
     
            public MyAdapter(List<String> list) {
                mList = list;
            }
     
            @Override
            public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                LinearLayout ll = new LinearLayout(getActivity());
                ll.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
                ll.setOrientation(LinearLayout.VERTICAL);
     
                AppCompatTextView tv = new AppCompatTextView(RvHuaDongActivity.this);
                tv.setTextSize(30);
                tv.setBackgroundColor(0xffeeeeee);
                ll.addView(tv);
     
                RecyclerView rv = new RecyclerView(getActivity());
                rv.setLayoutManager(new LinearLayoutManager(getActivity()));
                rv.setNestedScrollingEnabled(true);
                rv.setAdapter(new ItemAdapter(new ArrayList<String>()));
                ll.addView(rv, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
                return new BaseViewHolder(ll);
            }
     
            @Override
            public void onBindViewHolder(BaseViewHolder holder, int position) {
                ViewGroup vg = (ViewGroup) holder.itemView;
                TextView tv = (TextView) vg.getChildAt(0);
                tv.setText(mList.get(position));
     
                RecyclerView rv = (RecyclerView) vg.getChildAt(1);
                ItemAdapter adapter = (ItemAdapter) rv.getAdapter();
                adapter.mList.clear();
                for (int i = 0; i < 6; i++) {
                    adapter.mList.add("item" + i);
                }
                adapter.notifyDataSetChanged();//在bind时确定好数据
            }
     
            @Override
            public int getItemCount() {
                return mList.size();
            }
        }
     
        private class ItemAdapter extends RecyclerView.Adapter<BaseViewHolder> {
            private final List<String> mList;
     
            public ItemAdapter(List<String> list) {
                mList = list;
            }
     
     
            @Override
            public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                AppCompatTextView tv = new AppCompatTextView(RvHuaDongActivity.this);
                tv.setTextSize(30);
                tv.setBackgroundColor(0xffeeeeee);
                tv.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
                return new BaseViewHolder(tv);
            }
     
            @Override
            public void onBindViewHolder(BaseViewHolder holder, int position) {
                TextView tv = (TextView) holder.itemView;
                tv.setText(mList.get(position));
                if (position >= mList.size() - 2) {
                    tv.getLayoutParams().height = 600;
                } else {
                    tv.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
                }
                tv.setLayoutParams(tv.getLayoutParams());
            }
     
            @Override
            public int getItemCount() {
                return mList.size();
            }
        }
     
        public static class TopSmoothScroller extends LinearSmoothScroller {
            TopSmoothScroller(Context context) {
                super(context);
            }
     
            @Override
            protected int getHorizontalSnapPreference() {
                return SNAP_TO_START;//具体见源码注释
            }
     
            @Override
            protected int getVerticalSnapPreference() {
                return SNAP_TO_START;//具体见源码注释
            }
        }
    }

activity_rv_hua_dong.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#fff"
        android:orientation="vertical">
     
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            >
     
            <TextView
                android:id="@+id/tv_rvhuadong_GuanXing_1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="10dp"
                android:text="惯性随机滑动"
                android:textColor="#494949"
                android:textSize="15sp"
                />
     
            <TextView
                android:id="@+id/tv_rvhuadong_GuanXing_2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="10dp"
                android:text="惯性滑动到20"
                android:textColor="#494949"
                android:textSize="15sp"
                />
     
            <TextView
                android:id="@+id/tv_rvhuadong_GuanXing_3"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="10dp"
                android:text="惯性滑动到99"
                android:textColor="#494949"
                android:textSize="15sp"
                />
        </LinearLayout>
     
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            >
     
            <TextView
                android:id="@+id/tv_rvhuadong_ShanXian_1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="10dp"
                android:text="随机闪现"
                android:textColor="#494949"
                android:textSize="15sp"
                />
     
            <TextView
                android:id="@+id/tv_rvhuadong_ShanXian_2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="10dp"
                android:text="闪现到20"
                android:textColor="#494949"
                android:textSize="15sp"
                />
     
            <TextView
                android:id="@+id/tv_rvhuadong_ShanXian_3"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="10dp"
                android:text="闪现到99"
                android:textColor="#494949"
                android:textSize="15sp"
                />
        </LinearLayout>
     
        <android.support.v7.widget.RecyclerView
            android:id="@+id/rv_rvhuadong"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    </LinearLayout>

support包27.0.2,目前除了startSmoothScroll进行中然后立即调用scrollToPositionWithOffset(谁会这么做)位置会出现偏差 ,其他连续调用等操作均没有任何问题

注:

1.终于明白下面评论里的最后一个条目置顶的事了,对于类似rv嵌套rv置顶的问题(尤其是最后一个置顶):首先你的内层rv高度(如果是竖着的)必须是wrap,为了效率和复用内层rv要在bind初始化好基本数据LayoutManager、Adapter、setNestedScrollingEnabled(true);,在外层rv bind时必须确定好你内层rv的数据(我上面的例子都有写)。

2.置顶失败的举例:①rv嵌套内层的数据需要再次请求网络动态获取的②adapter里图片高度是网络请求决定的③rv设置数据用的是handler或子线程(这3个问题的根本原因是,滑到最后一个条目了了,你还在异步加载数据,rv发现滑不动自然就把最后一个条目停在最底下了,然后你才把数据返回来早就晚了)④用了一些支持header、footer的Adapter的,条目可能需要+1才能滑到

具体的使用场景及效果可以参考微信的通讯录

转载请注明出处:王能的博客https://blog.csdn.net/weimingjue/article/details/82805361
---------------------
作者:王能
来源:CSDN
原文:https://blog.csdn.net/weimingjue/article/details/82805361
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
除了使用LayoutManager的scrollToPositionWithOffset方法实现RecyclerView定位指定item置顶之外,还有以下几种方法: 1.使用SmoothScroller SmoothScroller是RecyclerView提供的一个平滑滚动的工具类,通过重写其computeScrollVectorForPosition方法和getVerticalSnapPreference方法,可以实现RecyclerView定位指定item置顶。具体实现步骤如下: ```java int position = 10; // 定位item的position RecyclerView recyclerView = findViewById(R.id.recycler_view); RecyclerView.SmoothScroller smoothScroller = new LinearSmoothScroller(recyclerView.getContext()) { @Override public PointF computeScrollVectorForPosition(int targetPosition) { return new PointF(0, 1); } @Override protected int getVerticalSnapPreference() { return LinearSmoothScroller.SNAP_TO_START; } }; smoothScroller.setTargetPosition(position); recyclerView.getLayoutManager().startSmoothScroll(smoothScroller); ``` 其中,computeScrollVectorForPosition方法返回一个PointF对象,用于指定RecyclerView滚动的方向,这里返回的是(0, 1),表示垂直向下滚动;getVerticalSnapPreference方法用于指定RecyclerView滚动定位位置,这里返回的是SNAP_TO_START,表示定位到顶部。 2.使用scrollToPosition方法 scrollToPosition方法可以让RecyclerView滚动指定位置,但是无法控制定位的偏移量。如果需要置顶,可以在滚动后手动调整RecyclerView滚动位置。具体实现步骤如下: ```java int position = 10; // 定位item的position RecyclerView recyclerView = findViewById(R.id.recycler_view); recyclerView.scrollToPosition(position); recyclerView.post(() -> { View itemView = recyclerView.getLayoutManager().findViewByPosition(position); if (itemView != null) { int offsetY = -(recyclerView.getHeight() - itemView.getHeight()) / 2; // 置顶的偏移量 recyclerView.scrollBy(0, offsetY); } }); ``` 其中,scrollToPosition方法可以将RecyclerView滚动指定位置;post方法用于在RecyclerView绘制完成后获取定位item的视图,并计算置顶的偏移量;scrollBy方法用于手动调整RecyclerView滚动位置。 以上是实现RecyclerView定位指定item置顶的几种方法,开发者可以根据实际需求选择合适的方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值