RecyclerView選中Item滾動到屏幕中間 / 指定位置

版權聲明:本文爲博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接和本聲明。
本文連接:https://blog.csdn.net/weimingjue/article/details/82805361
最近博主發現讓RecyclerView滑動到某一位置並置頂的博客一大堆,抄的是徹底如出一轍。此外,雖然這些博客「解決」了這些問題,但這種解決方案過於淺顯、粗暴,甚至都違背了開發思想。遂在此糾正這種錯誤。
RecyclerView提供了幾種移動的方法android

scrollToPosition微信

scrollTo網絡

scrollBydom

smoothScrollBy異步

smoothScrollToPositionide

因爲多數博客魚龍混雜,本博客若是讓你很是滿意或解決了你們的根本性問題,但願多多支持在下方點贊和回覆一下,舉手之勞方便你們。this

雖然裏面有移動到指定位置的方法scrollToPosition(直接閃現至某一位位置)、smoothScrollToPosition(慣性滑動至某一位置)可是貌似都不盡人意,由於他們只保證可以展現出來,並不能保證在第一位。而此時若是你打開源碼就會發現,原來全都是調用的LayoutManager移動方法,首先打開咱們耳熟能詳的LinearLayoutManager驚喜就在眼前.net

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就會有你想要的xml

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博主「王能」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接及本聲明。 原文連接:https://blog.csdn.net/weimingjue/article/details/82805361

相關文章
相關標籤/搜索