1. 程式人生 > >Android5.0新控制元件——RecyclerView的使用全解

Android5.0新控制元件——RecyclerView的使用全解

介紹

RecyclerView與ListView原理是類似的:都是僅僅維護少量的View並且可以展示大量的資料集。RecyclerView用以下兩種方式簡化了資料的展示和處理:

使用LayoutManager來確定每一個item的排列方式。
為增加和刪除專案提供預設的動畫效果。
你也可以定義你自己的LayoutManager和新增刪除動畫,RecyclerView專案結構如下:
這裡寫圖片描述

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

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

LinearLayoutManager
GridLayoutManager
StaggeredGridLayoutManager

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());

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

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

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

這裡寫圖片描述

簡單的RecyclerView使用方法

1、新增依賴

 compile 'com.android.support:recyclerview-v7:24.2.1'

2. 使用RecyclerView

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

3.建立介面卡設定給RecyclerView

View view = inflater.inflate(R.layout.fragment_home, container, false);
        //獲取rv控制元件
        RecyclerView rv = (RecyclerView) view.findViewById(R.id.rv);
        rv.setLayoutManager(new LinearLayoutManager(getContext()));
        //設定Adapter
        MyAdapter adapter = new MyAdapter();
        rv.setAdapter(adapter);
        return view;

4.建立介面卡

private class MyAdapter extends  RecyclerView.Adapter<MyHolder>{
        private final List<String> list;
        public MyAdapter(){
            list =  new ArrayList<String>();
            for (int i = 0; i < 30; i++) {
                list.add("商品記錄" + i)}

}
       //OnCreateViewHolder用來給rv建立快取的

     public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            //引數3:判斷條件 true  1.是打氣 2.新增到parent
            View view = LayoutInflater.from(getContext()).inflate(R.layout.main_recyclerview_item,parent,false);
            MyHolder holder = new MyHolder(view);
            return holder;
        }
        //給快取控制元件設定資料
        public void onBindViewHolder(MyHolder holder, int position) {

            String item = list.get(position);
            holder.textView.setText(item);
            holder.icon.setImageResource(R.drawable.ic_default);
        }
        //獲取記錄數

  public int getItemCount() {
            return list.size();
      }

5.建立HolderView

public class MyHolder extends RecyclerView.ViewHolder {
    public ImageView icon;
    public TextView textView;
    //實現的方法
    public MyHolder(View itemView) {
        super(itemView);
        icon= (ImageView) itemView.findViewById(R.id.item_iv_icon);
        textView= (TextView) itemView.findViewById(R.id.item_tv_title);
    }
}  

封裝RecyclerView通用介面卡

好處:1.數量動態 2.型別不限 Map<Integer,View>    
         作用:封裝了Adapter編寫的冗餘程式碼,提供簡潔的基類

思路:不寫死空間變數,而採用Map方式。
步驟:先關注比較火的控制元件庫 使用一下,看一下好處 學習下底層原始碼 自己動手體會
①封裝了BaseHolder

//抽取BaseHolder繼承RecyclerView.ViewHolder
public class BaseHolder extends RecyclerView.ViewHolder {
    //不寫死控制元件變數,而採用Map方式
    private HashMap<Integer, View> mViews = new HashMap<>();
    public BaseHolder(View itemView) {
        super(itemView);
    }
    /**
     *獲取控制元件的方法
     */
    public<T> T getView(Integer viewId){
        //根據儲存變數的型別 強轉為該型別
        View view = mViews.get(viewId);
        if(view==null){
           view= itemView.findViewById(viewId);
            //快取
            mViews.put(viewId,view);
        }
        return (T)view;
    }
    /**
     *傳入文字控制元件id和設定的文字值,設定文字
     */
    public BaseHolder setText(Integer viewId, String value){
        TextView textView = getView(viewId);
        if (textView != null) {
            textView.setText(value);
        }
        return this;
    }
    /**
     * 傳入圖片控制元件id和資源id,設定圖片
     */
    public BaseHolder setImageResource(Integer viewId, Integer resId) {
        ImageView imageView = getView(viewId);
        if (imageView != null) {
            imageView.setImageResource(resId);
        }
        return this;
    }
    //...還可以擴展出各種控制元件。
    //Fluent API 鏈式api  obj.setxxx().setyyy()....
}

②Holder封裝好之後,再封裝BaseAdapter

封裝的時候,部分引數可以選擇由外部的建構函式或者set方法
    public class BaseAdapter<T> extends RecyclerView.Adapter<BaseHolder> {
        private List<T> mList = new ArrayList<>();
        private int layoutId;
        public BaseAdapter(int layoutId,List<T> list){
            this.layoutId=layoutId;
            this. mList=list;
        }
        //onCreateViewHolder用來給rv建立快取
        @Override
        public BaseHolder onCreateViewHolder(ViewGroup parent, int viewType) {
           //引數3 判斷條件 true  1.打氣 2.新增到paraent
            // false 1.打氣 (參考parent的寬度)
            View view =   LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
            BaseHolder holder = new BaseHolder(view);
            return holder;
        }
       //onBindViewHolder給快取控制元件設定資料
        @Override
        public void onBindViewHolder(BaseHolder holder, int position) {
            T item = mList.get(position);
            convert(holder,item);
        }
    protected void convert(BaseHolder holder, T item) {
        //什麼都沒有做
    }
        //獲取記錄資料
        @Override
        public int getItemCount() {
            return mList.size();
        }
    }

③封裝之後,繼承實現

/**
 *1.extends  把父類的程式碼繼承過來。  可以少寫很多程式碼。
 *2.因為是全部拿來的程式碼,所以不是所有的程式碼都適合當前。對於不適合當前的程式碼(方法)
 * 我們可選擇@Override 覆蓋|重寫
 */
public class MyHomeAdapter extends BaseAdapter<String> {
    public MyHomeAdapter(List<String> list) {
        super(R.layout.main_recyclerview_item, list);
    }
    @Override
    protected void convert(BaseHolder holder, String item) {
        holder.setText(R.id.item_tv_title,item).setImageResource(R.id.image,R.drawable.ic_default);
    }
}

④封裝之後,程式碼實現

public class HomeFragment extends Fragment {
    private List<String> list;
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_home, container, false);
        //獲取rv控制元件
        RecyclerView rv = (RecyclerView) view.findViewById(R.id.rv); b
        rv.setLayoutManager(new LinearLayoutManager(getContext()));
        initData();
        //設定Adapter
        MyHomeAdapter adapter = new MyHomeAdapter(list);
        rv.setAdapter(adapter);
        return view;

案例1

依賴
佈局檔案activity_recycler_view.xml:

<?xml version="1.0" encoding="utf-8"?>
<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" >

<android.support.v7.widget.RecyclerView
    android:id="@+id/id_recyclerview"
    android:divider="#FFB900"
    android:dividerHeight="1dp"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

</RelativeLayout>

讓我們來看看變化最大的Adaper:

class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyViewHolder>
{
    private List<String> mList;
    private Context mContext;;
    public HomeAdapter(Context mContext,List<String>mList){
        this.mContext=mContext;
        this.mList=mList;
    }

    public void removeData(int position) {
        mList.remove(position);
        notifyItemRemoved(position);
    }
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
    {
        MyViewHolder holder = new MyViewHolder(LayoutInflater.from(
                mContext).inflate(R.layout.item_recycler, parent,
                false));
        return holder;
    }

    @Override
    public void onBindViewHolder(final MyViewHolder holder, final int position)
    {
        holder.tv.setText(mList.get(position));
    }

    @Override
    public int getItemCount()
    {
        return mList.size();
    }

    class MyViewHolder extends RecyclerView.ViewHolder
    {

        TextView tv;
        public MyViewHolder(View view)
        {
            super(view);
            tv = (TextView) view.findViewById(R.id.tv_item);
        }
    }
}

最大的改進就是對ViewHolder進行了封裝定義,不用自己去自定義了,另外Adaper繼承RecyclerView.Adapter,在onCreateViewHolder載入條目佈局,在onBindViewHolder中將檢視與資料進行繫結。
佈局檔案item_recycler.xml:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:background="@android:color/white"
    android:layout_height="wrap_content"
    >

    <TextView
        android:id="@+id/tv_item"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:text="moon" />
</FrameLayout>

這裡寫圖片描述