標籤:
"A flexible view for providing a limited window into a large data set."
可以說是ListView的升級版,ListVie中我們需要自己寫ViewHolder,當然你也可以不寫,是在RecylerView中,是要讓寫的喲~RecyclerView適用於無法在一個螢幕範圍內展現格式一樣的資料時,需要用多行或多列來展示。例如展示連絡人,圖片,視頻等。使用者需要滑動螢幕來查看資料,這時RecyclerView的特性就有用武之地了。比如,當使用者滑動使當前一個可視的Item滑出螢幕,這個Item的視圖將會被回收並在一個新Item進入可視範圍後重新被使用。可回收利用View是個很實用的功能,它不僅可以減少CPU不斷inflate View的開銷,而且可以節省緩衝View的記憶體開銷。
RecylerView還有一大特色,就是動畫!
RecyclerView不再負責顯示工作
和ListView不一樣的是,RecyclerView不再負責Item的擺放等顯示方面的功能。所有和布局、繪製等方面的工作都其拆分成不同的類進行管理。所以開發人員可以自訂各種各樣滿足定製需求的的功能類。
RecyclerView.Adapter |
管理的資料集合,為每個Item建立視圖 |
| RecyclerView.ViewHolder |
承載Item視圖的子視圖 |
| RecyclerView.LayoutManager |
負責Item視圖的布局 |
| RecyclerView.ItemDecoration |
為每個Item視圖添加子視圖,在Demo中被用來繪製Divider |
| RecyclerView.ItemAnimator |
負責添加、刪除資料時的動畫效果 |
ViewHolder
關於ViewHolder,Google早就推薦開發人員使用,但也只是建議。但是現在,RecyclerView.Adapter最終要求開發人員必須使用ViewHolder。
public class MyViewHolder extends ViewHolder{ public ImageView iv; public TextView tv; public MyViewHolder(View rootView) { super(rootView); iv = (ImageView)rootView.findViewById(R.id.item_iv); tv = (TextView)rootView.findViewById(R.id.item_tv); } }RecyclerView.Adapter
Adapter負責扮演兩個角色:不僅為底部資料提供支援而且還負責為資料建立合適的視圖。Adapter適用在Android很多控制項,例如ListView、AutoCompleteTextView等。
public class MyAdapter extends Adapter<MyViewHolder> { private List<Item> mData; public MyAdapter(List<Item> data){ this.mData = data; } @Override public int getItemCount() { return mData.size(); } @Override public void onBindViewHolder(MyViewHolder holder, int position) { Item bean = mData.get(position); holder.tv.setText(bean.tv); } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent,false); MyViewHolder vh = new MyViewHolder(itemView); return vh; }
}
onCreateViewHolder中負責為Item建立視圖,onBindViewHolder負責將資料繫結到Item的視圖上。
RecyclerView.LayoutManager
LayoutManager是RecyclerView中最有意思的類。該類負責將每個Item視圖在RecylerView中的布局。目前Google提供了LayoutManager的一個子類:LinearLayoutManager。LinearLayoutManager提供了橫向和豎向兩種布局。
MyLayoutManager manager = new MyLayoutManager(this); manager.setOrientation(LinearLayout.HORIZONTAL);//預設是LinearLayout.VERTICAL mRecyclerView.setLayoutManager(manager);
LinearLayoutManager提供了如下幾個方法來協助開發人員擷取螢幕上的頂部item和底部item:
- findFirstVisibleItemPosition()
- findFirstCompletelyVisibleItemPosition()
- findLastVisibleItemPosition()
findLastCompletelyVisibleItemPosition()
RecyclerView.ItemDecoration通過ItemDecoration可以使各個Item在視覺上相互分開,其實和ListView的Divider很像。ItemDecoration並不是RecyclerView必須設定的,開發人員可以不設定或者設定多個Decoration。RecyclerView會遍曆所有的ItemDecoration並調用各自的繪圖方法。
public class MyDecoration extends ItemDecoration { private static final int[] ATTRS = new int[]{ android.R.attr.listDivider }; private Drawable mDivider; public MyDecoration(Context ctx){ final TypedArray a = ctx.obtainStyledAttributes(ATTRS); mDivider = a.getDrawable(0); } @Override public void onDraw(Canvas c, RecyclerView parent, State state) { int top = parent.getPaddingTop(); int bottom = parent.getHeight() - parent.getPaddingBottom(); int childCount = parent.getChildCount(); for(int i=0;i < childCount;i++){ View child = parent.getChildAt(i); RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams)child.getLayoutParams(); int left = child.getRight() + layoutParams.rightMargin; int right = left + mDivider.getIntrinsicWidth(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) { outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); } }RecyclerView.ItemAnimatior
- 刪除某一個Item
- 添加一個新的Item
- 移動某個Item
Google提供了一個名為DefaultItemAnimator的預設ItemAnimator供開發人員使用。如果開發人員不為RecyclerView設定ItemAnimator,RecyclerView也會使用預設的DefaultItemAnimator。
顯然,為了讓動畫效果起效,開發人員必須通知Adapter資料有改變。之前我們使用Adapter時會調用notifyDataSetChanged()來通知Adapter資料改變並更新視圖,現在RecyclerView,Adapter提供了許多notifyXyz()方法。
流程
- 執行個體化RecyclerView
- 為RecyclerView設定LayoutManager
- 為RecyclerView設定Adapater
- 如果有需求,可以設定一個或多個ItemDecorations,當然,也可以不設定
- 如果有需求,可以設定ItemAnimator
監聽事件RecyclerView不再負責Item視圖的布局及顯示,所以RecyclerView也沒有為Item開放OnItemClick等點擊事件,這就需要開發人員自己實現。
因為ViewHolder我們可以拿到每個Item的根布局,所以如果我們為根布局設定單獨的OnClick監聽並將其開放給Adapter,那不就可以在組裝RecyclerView時就能夠設定ItemClickListener,只不過這個Listener不是設定到RecyclerView上而是設定到Adapter。
public class MyViewHolder extends ViewHolder implements OnClickListener,OnLongClickListener{ public ImageView iv; public TextView tv; private MyItemClickListener mListener; private MyItemLongClickListener mLongClickListener; public MyViewHolder(View rootView,MyItemClickListener listener,MyItemLongClickListener longClickListener) { super(rootView); iv = (ImageView)rootView.findViewById(R.id.item_iv); tv = (TextView)rootView.findViewById(R.id.item_tv); this.mListener = listener; this.mLongClickListener = longClickListener; rootView.setOnClickListener(this); rootView.setOnLongClickListener(this); } /** * 點擊監聽 */ @Override public void onClick(View v) { if(mListener != null){ mListener.onItemClick(v,getPosition()); } } /** * 長按監聽 */ @Override public boolean onLongClick(View arg0) { if(mLongClickListener != null){ mLongClickListener.onItemLongClick(arg0, getPosition()); } return true; } }item長寬public class MyLayoutManager extends LinearLayoutManager { public MyLayoutManager(Context context) { super(context); } @Override public void onMeasure(Recycler recycler, State state, int widthSpec,int heightSpec) { View view = recycler.getViewForPosition(0); if(view != null){ measureChild(view, widthSpec, heightSpec); int measuredWidth = MeasureSpec.getSize(widthSpec); int measuredHeight = view.getMeasuredHeight(); setMeasuredDimension(measuredWidth, measuredHeight); } } }我是天王蓋地虎的分割線
參考:http://www.grokkingandroid.com/first-glance-androids-recyclerview/
Android -- RecyclerView