RecyclerView使用,優化,條目閃爍問題

咚咚恰,發表於2020-10-23

RecyclerView的基本介紹

RecyclerView是谷歌V7包下新增的控制元件,用來替代ListView的使用,在RecyclerView標準化了ViewHolder類似於ListView中convertView用來做檢視緩.

RecyclerView好處

①RecylerView封裝了viewholder的回收複用,也就是說RecylerView標準化了ViewHolder,編寫Adapter面向的是 ViewHolder而不再是View了,複用的 邏輯被封裝了,寫起來更加簡單。

②提供了一種插拔式的體驗,高度的解耦,異常的靈活,針對一個Item的顯示RecylerView專門抽取出了相應的類,來控制Item的顯示,使其的擴充套件性非常強。例如recyclerview不侷限與下拉選單,它還支援GridView效果和瀑布流效果

③可以控制Item增刪的動畫,可以通過ItemAnimator這個類進行控制,當然針對增刪的動畫,RecylerView有其自己預設的實現。

涉及到的類

Adapter:使用RecyclerView之前,你需要一個繼承自RecyclerView.Adapter的介面卡,作用是將資料與每一個item的介面進行繫結。
LayoutManager:用來確定每一個item如何進行排列擺放,何時展示和隱藏。回收或重用一個View的時候,LayoutManager會向介面卡請求新的資料來替換舊的資料,這種機制避免了建立過多的View和頻繁的呼叫findViewById方法(與ListView原理類似)。

RecyclerView的三種顯示方式

目前SDK中提供了三種自帶的LayoutManager:
LinearLayoutManager
GridLayoutManager
StaggeredGridLayoutManager

RecyclerView依賴,佈局,屬性使用

1、新增依賴

implementation ‘com.android.support:recyclerview-v7:27.1.1’

2.然後是在XML檔案用使用它

<android.support.v7.widget.RecyclerView
        android:id="@+id/rv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
   />

3.建立完佈局之後在MainActivity中獲取這個RecyclerView,並宣告LayoutManager與Adapter,程式碼如下:

recyclerView = (RecyclerView) findViewById(R.id.recyclerView);  
LinearLayoutManager layoutManager = new LinearLayoutManager(this );  
//設定佈局管理器  
recyclerView.setLayoutManager(layoutManager);  
//設定為垂直佈局,這也是預設的  
layoutManager.setOrientation(OrientationHelper. VERTICAL);  
//設定Adapter  
recyclerView.setAdapter( recycleAdapter);  
 //設定分隔線  
recyclerView.addItemDecoration( new DividerGridItemDecoration(this ));  
//設定增加或刪除條目的動畫  
recyclerView.setItemAnimator( new DefaultItemAnimator());  

RecyclerView的介面卡RecyclerView.Adapter

RecyclerView.Adapter

①onCreateViewHolder()
這個方法主要生成為每個Item inflater出一個View,但是該方法返回的是一個ViewHolder。該方法把View直接封裝在ViewHolder中,然後我們面向的是ViewHolder這個例項,當然這個ViewHolder需要我們自己去編寫。直接省去了當初的convertView.setTag(holder)和convertView.getTag()這些繁瑣的步驟。

②onBindViewHolder()
這個方法主要用於適配渲染資料到View中。方法提供給你了一個viewHolder,而不是原來的convertView。

③getItemCount()
這個方法就類似於BaseAdapter的getCount方法了,即總共有多少個條目。

建立介面卡和ViewHolder


public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

    private final ArrayList<String> mData;
    private final Context mContext;
    private final LayoutInflater mInflater;
    private OnItemClickListener mListener;
    
    public MyAdapter(Context context,ArrayList<String> data) {
        this.mContext = context;
        this.mData = data;
        mInflater = LayoutInflater.from(context);
    }

    @NonNull
    @Override
    public MyAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        //建立一個自定義ViewHolder
        View inflate = mInflater.inflate(R.layout.item, parent,false);
        ViewHolder viewHolder = new ViewHolder(inflate);
        return viewHolder;

    }
    
    @Override
    public void onBindViewHolder(@NonNull MyAdapter.ViewHolder holder, final int position) {
        //將資料與介面繫結
        holder.mTv.setText(mData.get(position));
        if (position%2 == 0){
            holder.mIv.setImageResource(R.drawable.friend_circle_default);
        }else{
            holder.mIv.setImageResource(R.drawable.cate);
        }
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //這個方法被呼叫的時候,想要讓MainActivity知道
                if (mListener != null){
                    mListener.onItemClick(v,position);
                }
            }
        });
    }

    //1.定義一個介面
    public interface OnItemClickListener{
        void onItemClick(View view,int position);
    }

    //2.傳遞介面類實現物件
    public void setOnItemClickListener(OnItemClickListener listener){
        this.mListener = listener;
    }
    
    @Override
    public int getItemCount() {
        //獲取item數量
        return mData.size();
    }
    
    /**
     * RecyclerView 內部封裝了VIewHolder的回收複用,標準化了ViewHolder,編寫Adapter就是面向ViewHolder,而不是View
     */
    public class ViewHolder extends RecyclerView.ViewHolder{
        private TextView mTv;
        private ImageView mIv;
        public ViewHolder(View itemView) {
            super(itemView);
            mTv = itemView.findViewById(R.id.tv);
            mIv = itemView.findViewById(R.id.iv);
        }
    }
}

RecyclerView介面回撥
1.定義介面
在哪裡定義?
定義外部介面和內部介面都可以,google一般定義內部,所以我們也定義內部介面
介面裡有什麼方法
取決於我們要做什麼,需要什麼方法就定義什麼

 //Adapter內部定義
    public interface OnItemClickListener{
        void onItemClick(View view,int position);
    }

2.設定傳遞介面類實現物件的方法

 public void setOnItemClickListener(OnItemClickListener listener){
        this.mListener = listener;
    }

3.傳遞介面實現類物件


mAdapter.setOnItemClickListener(new OnItemClickListener() {
        @Override
        public void onItemClick(View view, int position) {
            Toast.makeText(MainActivity.this, "position:" + position, Toast.LENGTH_SHORT).show();
            startActivity(new Intent(MainActivity.this, MultiTypeActivity.class));
        }
    });

Recycleview優化:

1,如果確定recycleview中資料變化的位置,可以對其進行區域性重新整理

2,在編寫佈局的時候,如果能通過程式碼實現排布,可以將xml方式進行替換,提升頁面載入的效率;另外尤其在多佈局展示中,對應不同item中公用的一些控制元件或元件,可以通過自定義view的方式,減少view的構造和巢狀。

3,如果一個item中,多個子view都需要設定點選事件的監聽,我們可以設定一個全域性的監聽,然後通過view.getId或view.getTag來找到具體item,將具體view的點選事件設定到viewHolder中,避免列表在不停滑動過程中,猶豫onbindviewholder要反覆繪製資料帶來的點選事件的重複建立。

4,如果一個recyclerview中巢狀了多個recyclerview,而巢狀recycerview的介面卡是一樣的,可以共享一個物件池。

5,對recyclerview資料的預載入,當我們載入完一頁資料後,不管使用者有沒有及時上拉檢視,我們可以再後臺動態的載入一定數量的資料,當列表上劃時,計算劃出的條目數量,然後從預載入的資料中取出對應條目的數量追加到recycleview末尾,這樣給使用者更為流暢的滑動體驗。

6,對於scrollview中巢狀recyclerview,我們可以呼叫setNestedScrollingEnabled(false)來禁止recycleview的滑動,以確保外部view可流暢的滑動。

7,如果item高度固定,可以設定RecyclerView.setHasFixedSize(true); 來避免 requestLayout 浪費資源;

8,呼叫RecyclerView.onViewRecycled(holder) 來回收資源。

9,通過 RecycleView.setItemViewCacheSize(size); 來加大 RecyclerView 的快取,用空間換時間來提高滾動的流暢性

10,瀑布流上拉載入時發生item抖動,建議採用notifyItemRangeChanged進行區域性重新整理

關於recycleview重新整理是條目閃爍問題

1,如果僅僅是重新整理某一個item,一定要呼叫指定位置的區域性重新整理,儘量避免更為消耗資源的全部重新整理

2,如果沒有給item加出場動畫,儘量把預設的動畫禁止掉,防止onbindviewholder重繪item的時候,由於出場動畫給item的重新整理帶來閃爍的bug

3,對於item中圖片的閃爍,一方面要注意圖片的載入是否加了出場動畫(比如glide預設有出場動畫,應該把它禁止掉),第二就是看有沒有在重新整理的時候動態繪製imageview的形狀,如果繪製效率不高的情況下,也會引發圖片閃爍,第三在item中儘量不要使用辦透明效果,尤其是資料量較大或者涉及到需要頻繁重新整理的情況,因為半透明繪製底層涉及到大量的數學運算,而這些嚴重影響item的繪製,所以當item數量龐大時,會出現item閃爍甚至滑動卡頓的問題。

相關文章