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>