對RecyclerView一直處於一種朦朧的理解狀態,最近項目經常使用RecyclerView,本系列主要從淺入深的總結自己對RecyclerView的理解
前言
面對一個新控件,首先要學會使用,然後再逐步學習其原理,RecyclerView作爲ListView 的替代,靈活性更強,我把它理解爲一個插線板,需要什麼功能就插入什麼,非常方便。
初學Android時,習慣於控件直接綁定內容,例如TextView,直接可以設置文字信息,對於ListView和RecyclerView這種展示大量數據的控件非常不理解,所以對控件的使用也非常不熟悉。使用控件時,我們主要會處理三個主體:Layout(View)、adapter、ItemModel。三個主體理解到位了,對於此類控件的使用變會的容易些。
Layout不用過多解釋,要使用這類控件,一般最少要涉及2個layout,一個作爲Activity的Layout,一個作爲列表Item的layout
ItemModel,可以把他看做數據,即每個列表要展示的內容。
adapter就是將內容綁定到view 的工具。可以理解爲MVC模式
RecyclerView的使用
首先再Android studio中導入RecyclerView,網上有一萬種方法,讀者可以自行查閱。
導入RecyclerView後,便要創建上面說的三種主體,首先是Layout,創建一個Activity的Layout,如下,Activity中只包含了一個RecyclerView。
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.recyclerviewt.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/RVContent"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
</android.support.v7.widget.RecyclerView>
</android.support.constraint.ConstraintLayout>
另外一個Layout爲RecyclerView每一個列表的視圖,本文每個Item顯示一個數字,所以界面只包含了一個TextView:
<?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">
<TextView
android:id="@+id/view_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="40sp"
android:gravity="center"
/>
</LinearLayout>
然後創建adapter繼承RecyclerView.Adapter, 需要重寫三個方法,分別是onCreateViewHolder、onBindViewHolder、getItemCount
viewHolder
ViewHolder可以理解爲一個View的容器,列表在滾動的時候,爲了避免不斷的findViewByID影響效率,將view存放在ViewHolder中進行復用,例如每頁展示7個Items,當向上滑動時,item1劃出界面,就可以將item的viewholder複用,更新爲下方可見的item內容,避免不斷的findViewByID
public static class ViewHolder extends RecyclerView.ViewHolder {
TextView textView;
public ViewHolder(View itemView) {
super(itemView);
textView = (TextView)itemView.findViewById(R.id.view_text);
}
}
onCreateViewHolder
onCreateViewHolder的作用就是創建ViewHolder,在列表剛打開時,由於之前沒有創建過ViewHolder,無法複用,所以需要重新創建,新建一個item的view,放到ViewHolder容器中。
public MyAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.rv_item_view,parent,false);
MyAdapter.ViewHolder viewHolder = new MyAdapter.ViewHolder(view);
return viewHolder;
}
onBindViewHolder
上面講了每個ViewHolder在複用時需要更新裏面View的內容,相當於把新的View內容與VIewHolder連接起來,所以每次需要在ViewHolder中更新view時都會調用這個方法
@Override
public void onBindViewHolder(@NonNull MyAdapter.ViewHolder holder, int position) {
holder.textView.setText(Items.get(position).toString());
}
adapter創建好後,需要準備ItemModel,本文每個列表只顯示數字,所以不需要新建數據結構,只是生成了20個數字。
Activity
和其他控件一樣,在使用時需要在Activity中進行綁定,對於RecyclerView需要設置其排列方向、動畫、以及與adapter進行綁定,完整的Activity如下:
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private MyAdapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initRV();
}
private void initRV(){
mAdapter = new MyAdapter(initData());
mLayoutManager = new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false);
mRecyclerView = (RecyclerView) findViewById(R.id.RVContent);
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.HORIZONTAL));
}
private ArrayList<Integer> initData(){
ArrayList<Integer> data = new ArrayList<>();
for(int i=0;i<20;i++){
data.add(i);
}
return data;
}
}
我調整了TextView的寬高和顏色,並設置排列方式爲水平排列,效果如下:
設置間隔
RecyclerView和ListView不同,爲了保證RecyclerView的靈活性,item之間的間隔的設計也獨立了出來,通過調用adapter的addItemDecortion方法添加間隔。
1.系統自帶的間隔線:
addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.HORIZONTAL))
2.自定義間隔
系統自帶的分割線是通過DividerItemDecoration類進行實現的,查看源碼可以看到,這個類是通過繼承RecyclerView.ItemDecoration實現間隔的樣式:
public class DividerItemDecoration extends ItemDecoration
我們也可以通過繼承來實現自己的間隔,需要重寫onDraw和getItemOffsets方法,onDraw主要實現我們要在間隔中繪製的內容,getItemOffsets用來設置每個Item周圍的間隔大小,本文自定義一種空白間隔,不繪製內容,第一個和最後一個item的兩邊間距50,中間的item間距12,自定義ItemDecoration如下:
class mItemDecoration extends RecyclerView.ItemDecoration{
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) parent.getLayoutManager();
Context context = MainActivity.this;
if(parent.getChildAdapterPosition(view) == 0){
outRect.left = dip2px(context,50);
outRect.right = dip2px(context,12);
} else if(parent.getChildAdapterPosition(view) == linearLayoutManager.getItemCount()-1){
outRect.right = dip2px(context,50);
outRect.left = dip2px(context,12);
} else{
outRect.left = dip2px(context,12);
outRect.right = dip2px(context,12);
}
}
}
不繪製內容,所以沒有重寫onDraw方法,需要間隔,所以只重寫了getItemOffsets方法,方法中outRect表示每個Item外圍的寬度,默認爲0,通過getChildAdapterPosition()方法可以得到當前繪製的間隔是哪個Item的。recyclerView設置爲自定義的間隔效果如下:
這樣一個簡單的RecyclerVIew的使用就完成了。