"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/