(一).前言:
話說RecyclerView已經面市很久,也在很多應用中得到廣泛的使用,在整個開發者圈子裡面也擁有很不錯的口碑,那說明RecyclerView擁有比ListView,GridView之類控件有很多的優點,例如:數據綁定,Item View創建,View的回收以及重用等機制。那麼今天開始我們來重點學習一下RecyclerView控件,本系列文章會包括到以下三個部分:
- RecyclerView控件的基本使用,包括基礎,進階,高級部分,動畫之類
- RecyclerView控件的實戰實例
- RecyclerView控件集合AA(Android Annotations)注入框架實例
那麼今天我們首先來看第一部分:RecyclerView控件的基本使用,進階,動畫相關知識點。本次講解所有用的Demo例子已經全部更新到下面的項目中了,歡迎大家star和fork。
FastDev4Android框架項目地址:https://github.com/jiangqqlmj/FastDev4Android
(二).RecyclerView基本介紹:
通過使用RecyclerView控件,我們可以在APP中創建帶有Material Design風格的復雜列表。RecyclerView控件和ListView的原理有很多相似的地方,都是維護少量的View來進行顯示大量的數據,不過RecyclerView控件比ListView更加高級並且更加靈活。當我們的數據因為用戶事件或者網絡事件發生改變的時候也能很好的進行顯示。和ListView不同的是,RecyclerView不用在負責Item的顯示相關的功能,在這邊所有有關布局,繪制,數據綁定等都被分拆成不同的類進行管理,下面我這邊會一個個的進行講解。同時RecyclerView控件提供了以下兩種方法來進行簡化和處理大數量集合:
- 采用LayoutManager來處理Item的布局
- 提供Item操作的默認動畫,例如在增加或者刪除item的時候
你也可以自定義LayoutManager或者設置添加/刪除的動畫,整體的RecyclerView結構圖如下:
為了使用RecyclerView控件,我們需要創建一個Adapter和一個LayoutManager:
Adapter:繼承自RecyclerView.Adapetr類,主要用來將數據和布局item進行綁定。
LayoutManager:布局管理器,設置每一項view在RecyclerView中的位置布局以及控件item view的顯
示或者隱藏.當View重用或者回收的時候,LayoutManger都會向Adapter來請求新的數據來進行替換原來數據的內容。這種回收重用的機制可以提供性能,避免創建很多的view或者是頻繁的調用findViewById方法。這種機制和ListView還是很相似的。
RecyclerView提供了三種內置的LayoutManager:
- LinearLayoutManager:線性布局,橫向或者縱向滑動列表
- GridLayoutManager:表格布局
- StaggeredGridLayoutManager:流式布局,例如瀑布流效果
當然除了上面的三種內部布局之外,我們還可以繼承RecyclerView.LayoutManager來實現一個自定義的LayoutManager。
Animations(動畫)效果:
RecyclerView對於Item的添加和刪除是默認開啟動畫的。我們當然也可以通過RecyclerView.ItemAnimator類定制動畫,然後通過RecyclerView.setItemAnimator()方法來進行使用。
RecyclerView相關類:
(三).RecyclerView基本實現:
我這邊實例采用Android Studio 1.3.2。
- dependencies {
- …….
- compile'com.android.support:recyclerview-v7:23.1.1'
- }
2.新建布局,引入RecyclerView控件:
[html] view plaincopy
- <?xmlversionxmlversion="1.0" encoding="utf-8"?>
- <LinearLayoutxmlns:androidLinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"android:layout_width="match_parent"
- android:layout_height="match_parent">
- <includelayoutincludelayout="@layout/common_top_bar_layout"/>
-
- <android.support.v7.widget.RecyclerView
- android:id="@+id/recyclerView_one"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scrollbars="vertical"
- ></android.support.v7.widget.RecyclerView>
- </LinearLayout>
3.在Activity中獲取RecyclerView控件然後進行設置LayoutManger以及Adapter即可,和ListView的寫法有點類似:
[java] view plaincopy
- /**
- * 當前類注釋:RecyclerView使用實例測試demo
- * 項目名:FastDev4Android
- * 包名:com.chinaztt.fda.test
- * 作者:江清清 on 15/11/17 15:10
- * 郵箱:[email protected]
- * QQ: 781931404
- * 公司:江蘇中天科技軟件技術有限公司
- */
- public class RecyclerViewTestActivity extends BaseActivity {
- private LinearLayout top_bar_linear_back;
- private TextView top_bar_title;
- private RecyclerView recyclerView_one;
- private RecyclerView.Adapter mAdapter;
- private LinearLayoutManager mLayoutManager;
- @Override
- protected void onCreate(BundlesavedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.recyclerview_test_layout);
- top_bar_linear_back=(LinearLayout)this.findViewById(R.id.top_bar_linear_back);
- top_bar_linear_back.setOnClickListener(new CustomOnClickListener());
- top_bar_title=(TextView)this.findViewById(R.id.top_bar_title);
- top_bar_title.setText("RecyclerView使用實例");
- //開始設置RecyclerView
- recyclerView_one=(RecyclerView)this.findViewById(R.id.recyclerView_one);
- //設置固定大小
- recyclerView_one.setHasFixedSize(true);
- //創建線性布局
- mLayoutManager = newLinearLayoutManager(this);
- //垂直方向
- mLayoutManager.setOrientation(OrientationHelper.VERTICAL);
- //給RecyclerView設置布局管理器
- recyclerView_one.setLayoutManager(mLayoutManager);
- //創建適配器,並且設置
- mAdapter = newTestRecyclerAdapter(this);
- recyclerView_one.setAdapter(mAdapter);
- }
- class CustomOnClickListener implements View.OnClickListener{
- @Override
- public void onClick(View v) {
- RecyclerViewTestActivity.this.finish();
- }
- }
- }
4.自定義一個適配器來進行創建item view以及綁定數據
[java] view plaincopy
- /**
- * 當前類注釋:RecyclerView 數據自定義Adapter
- * 項目名:FastDev4Android
- * 包名:com.chinaztt.fda.adapter.base
- * 作者:江清清 on 15/11/18 22:29
- * 郵箱:[email protected]
- * QQ: 781931404
- * 公司:江蘇中天科技軟件技術有限公司
- */
- public class TestRecyclerAdapter extends RecyclerView.Adapter<TestRecyclerAdapter.ViewHolder>{
- private LayoutInflater mInflater;
- private String[] mTitles=null;
- public TestRecyclerAdapter(Context context){
- this.mInflater=LayoutInflater.from(context);
- this.mTitles=new String[20];
- for (int i=0;i<20;i++){
- int index=i+1;
- mTitles[i]="item"+index;
- }
- }
- /**
- * item顯示類型
- * @param parent
- * @param viewType
- * @return
- */
- @Override
- public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- Viewview=mInflater.inflate(R.layout.item_recycler_layout,parent,false);
- //view.setBackgroundColor(Color.RED);
- ViewHolder viewHolder=new ViewHolder(view);
- return viewHolder;
- }
- /**
- * 數據的綁定顯示
- * @param holder
- * @param position
- */
- @Override
- public void onBindViewHolder(ViewHolder holder, int position) {
- holder.item_tv.setText(mTitles[position]);
- }
-
- @Override
- public int getItemCount() {
- return mTitles.length;
- }
-
- //自定義的ViewHolder,持有每個Item的的所有界面元素
- public static class ViewHolder extends RecyclerView.ViewHolder {
- public TextView item_tv;
- public ViewHolder(View view){
- super(view);
- item_tv = (TextView)view.findViewById(R.id.item_tv);
- }
- }
這個自定義Adapter和我們在使用Listview時候的Adapter相比還是有點不太一樣的,首先這邊我們需要繼承RecyclerView.Adaper類,然後實現兩個重要的方法onBindViewHodler()以及onCreateViewHolder(),這邊我們看出來區別,使用RecyclerView控件我們就可以把Item View視圖創建和數據綁定這兩步進行分來進行管理,用法就更加方便而且靈活了,並且我們可以定制打造千變萬化的布局。同時這邊我們還需要創建一個ViewHolder類,該類必須繼承自RecyclerView.ViewHolder類,現在Google也要求我們必須要實現ViewHolder來承載Item的視圖。
該Demo運行效果如下:
上面的例子我們這邊比較簡單使用LinearLayoutManager來實現了,其中布局是采用垂直布局的,當然我們還可以設置線性布局的方向為橫向,只要如下設置即可:
[java] view plaincopy
- mLayoutManager.setOrientation(OrientationHelper.HORIZONTAL);
運行效果如下:
那麼另外兩種內置的布局如下:
1.GridLayoutManger:使用如下設置:
[java] view plaincopy
- GridLayoutManager girdLayoutManager=new GridLayoutManager(this,4);
- recyclerView_one.setLayoutManager(girdLayoutManager);
運行效果如下:
2.GridLayoutManger:使用如下設置:
[java] view plaincopy
- StaggeredGridLayoutManager staggeredGridLayoutManager=new StaggeredGridLayoutManager(2,OrientationHelper.VERTICAL);
- recyclerView_one.setLayoutManager(staggeredGridLayoutManager);
(四).RecyclerView分隔線實現(ItemDecoration):
大家肯定觀察到上面的顯示效果還是比較丑,例如就沒有分隔線這個效果,下面我們一起來實現以下分隔線的效果。還記得前面的一個表格中有寫關於RecyclerView的相關類:
RecyclerView.ItemDecoration
給每一項Item視圖添加子View,可以進行畫分隔線之類的東西
我們可以創建一個繼承RecyclerView.ItemDecoration類來繪制分隔線,通過ItemDecoration可 以讓我們每一個Item從視覺上面相互分開來,例如ListView的divider非常相似的效果。當然像我們上面的例子ItemDecoration 我們沒有設置也沒有報錯哦,那說明ItemDecoration我們並不是強制需要使用,作為我們開發者可以設置或者不設置Decoration的。實現一個ItemDecoration,系統提供的ItemDecoration是一個抽象類,內部除去已經廢棄的方法以外,我們主要實現以下三個方法:
[java] view plaincopy
- public static abstract class ItemDecoration {
- public void onDraw(Canvas c,RecyclerView parent, State state) {
- onDraw(c, parent);
- }
- public void onDrawOver(Canvas c,RecyclerView parent, State state) {
- onDrawOver(c, parent);
- }
- public void getItemOffsets(RectoutRect, View view, RecyclerView parent, State state) {
- getItemOffsets(outRect,((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
- parent);
- }
- }
又因為當我們RecyclerView在進行繪制的時候會進行繪制Decoration,那麼會去調用onDraw和onDrawOver方法,那麼這邊我們其實只要去重寫onDraw和getItemOffsets這兩個方法就可以實現啦。然後LayoutManager會進行Item布局的時候,回去調用getItemOffset方法來計算每個Item的Decoration合適的尺寸,下面我們來具體實現一個Decoration。TestDecoration.java
[java] view plaincopy
- /**
- * 當前類注釋:自定義實現一個Decoration分隔線
- * 項目名:FastDev4Android
- * 包名:com.chinaztt.fda.widget
- * 作者:江清清 on 15/11/19 12:29
- * 郵箱:[email protected]
- * QQ: 781931404
- * 公司:江蘇中天科技軟件技術有限公司
- */
- public class TestDecoration extends RecyclerView.ItemDecoration {
- //采用系統內置的風格的分割線
- private static final int[] attrs=newint[]{android.R.attr.listDivider};
- private Drawable mDivider;
-
- public TestDecoration(Context context) {
- TypedArray typedArray=context.obtainStyledAttributes(attrs);
- mDivider=typedArray.getDrawable(0);
- }
-
- /**
- * 進行自定義繪制
- * @param c
- * @param parent
- * @param state
- */
- @Override
- public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
- int top=parent.getPaddingTop();
- intbottom=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();
- intleft=child.getRight()+layoutParams.rightMargin;
- intright=left+mDivider.getIntrinsicWidth();
- mDivider.setBounds(left,top,right,bottom);
- mDivider.draw(c);
- }
- }
-
- @Override
- public void getItemOffsets(Rect outRect,View view, RecyclerView parent, RecyclerView.State state) {
- outRect.set(0,0,mDivider.getIntrinsicWidth(),0);
- }
- }
我這邊實例中采用系統主題(android.R.attr.listDivider)來設置成分隔線的,然後來獲取尺寸,位置進行setBound(),繪制,接著通過outRect.set()來設置繪制整個區域范圍,最後不要忘記往RecyclerView中設置該自定義的分割線,
//添加分隔線
[java] view plaincopy
- recyclerView_one.addItemDecoration(newTestDecoration(this));
運行效果如下:
上面的分割線效果只是垂直畫了分割線,但是我們水平方向也要進行畫分割線,那麼我們下面對於自定義的Decoration進行改進,AdvanceDecoration.java就出現啦。
[java] view plaincopy
- /**
- * 當前類注釋:改進之後的自定義Decoration分割線
- * 項目名:FastDev4Android
- * 包名:com.chinaztt.fda.widget
- * 作者:江清清 on 15/11/19 12:53
- * 郵箱:[email protected]
- * QQ: 781931404
- * 公司:江蘇中天科技軟件技術有限公司
- */
- public class AdvanceDecoration extends RecyclerView.ItemDecoration{
- //采用系統內置的風格的分割線
- private static final int[] attrs=newint[]{android.R.attr.listDivider};
- private Drawable mDivider;
- private int orientation;
- public AdvanceDecoration(Contextcontext,int orientation) {
- TypedArray typedArray=context.obtainStyledAttributes(attrs);
- mDivider=typedArray.getDrawable(0);
- typedArray.recycle();
- this.orientation=orientation;
- }
- @Override
- public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
- drawHDeraction(c,parent);
- drawVDeraction(c,parent);
- }
- /**
- * 繪制水平方向的分割線
- * @param c
- * @param parent
- */
- private void drawHDeraction(Canvas c,RecyclerView parent){
- int left=parent.getPaddingLeft();
- intright=parent.getWidth()-parent.getPaddingRight();
- int childCount=parent.getChildCount();
- for(int i=0;i<childCount;i++){
- View child=parent.getChildAt(i);
- RecyclerView.LayoutParams layoutParams=(RecyclerView.LayoutParams)child.getLayoutParams();
- inttop=child.getBottom()+layoutParams.bottomMargin;
- intbottom=top+mDivider.getIntrinsicHeight();
- mDivider.setBounds(left,top,right,bottom);
- mDivider.draw(c);
- }
- }
- /**
- * 繪制垂直方向的分割線
- * @param c
- * @param parent
- */
- private void drawVDeraction(Canvas c,RecyclerView parent){
- int top=parent.getPaddingTop();
- intbottom=parent.getHeight()-parent.getPaddingBottom();
- int childCount=parent.getChildCount();
- for(int i=0;i<childCount;i++){
- View child=parent.getChildAt(i);
- RecyclerView.LayoutParamsla youtParams=(RecyclerView.LayoutParams)child.getLayoutParams();
- intleft=child.getRight()+layoutParams.rightMargin;
- intright=left+mDivider.getIntrinsicWidth();
- mDivider.setBounds(left,top,right,bottom);
- mDivider.draw(c);
- }
- }
- @Override
- public void getItemOffsets(Rect outRect,View view, RecyclerView parent, RecyclerView.State state) {
- if(OrientationHelper.HORIZONTAL==orientation){
- outRect.set(0, 0,mDivider.getIntrinsicWidth(), 0);
- }else {
- outRect.set(0, 0, 0,mDivider.getIntrinsicHeight());
- }
- }
- }
改良之後的自定義分割器的構造函數中新增一個int參數,用來表示橫向還是縱向布局,這樣我們可以分別來繪制分割線。具體使用方法:
recyclerView_one.addItemDecoration(newAdvanceDecoration(this,OrientationHelper.VERTICAL));
運行比較效果如下:
1.橫向
2.縱向
(五).RecyclerView高級用戶(監聽事件處理)
我們知道在ListView使用的時候,該控件給我們提供一個onItemClickListener監聽器,這樣當我們的item發生觸發事件的時候,會回調相關的方法,以便我們方便處理Item點擊事件。對於RecyclerView來講,非常可惜的時候,該控件沒有給我們提供這樣的內置監聽器方法,不過我們可以進行改造實現。我們先來看一下之前我們寫得TestRecyclerAdapter中的onCreateViewHolder()方法中的代碼:
[java] view plaincopy
- public ViewHolder onCreateViewHolder(ViewGroupparent, int viewType) {
- Viewview=mInflater.inflate(R.layout.item_recycler_layout,parent,false);
- //這邊可以做一些屬性設置,甚至事件監聽綁定
- //view.setBackgroundColor(Color.RED);
- ViewHolder viewHolder=newViewHolder(view);
- return viewHolder;
- }
該方法創建一個ViewHolder,其中承載的就是每一項Item View視圖,那麼我們可以在view創建出來之後給它進行添加相應的屬性或者監聽方法,例如:背景顏色,大小,以及點擊事件。既然可以這樣解決,OK,我們給View添加一個onClickListener監聽器,然後點擊的時候回調onClick()方法。同時我們需要自定義一個類似於onItemClickListener()的監聽器來處理。
[java] view plaincopy
- /**
- * 自定義RecyclerView 中item view點擊回調方法
- */
- interface OnRecyclerItemClickListener{
- /**
- * item view 回調方法
- * @param view 被點擊的view
- * @param position 點擊索引
- */
- void onItemClick(View view, intposition);
- }
然後聲明以及Adapter初始化的時候傳入進去:
[java] view plaincopy
- public TestRecyclerAdapter(Contextcontext,OnRecyclerItemClickListener onRecyclerItemClickListener){
- ……..
- this.onRecyclerItemClickListener=onRecyclerItemClickListener;
- }
然後我們在onClick回調方法中調用OnRecyclerItemClickListener接口的方法。
[java] view plaincopy
- view.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if(onRecyclerItemClickListener!=null){
- onRecyclerItemClickListener.onItemClick(view, (int)view.getTag());
- }
- }
- });
上面的onItemClick中第二個參數的position,采用view.getTag的方法獲取,那麼我們就需要在onBindViewHolder()方法中設置一個tag了.
[java] view plaincopy
- public voidonBindViewHolder(ViewHolder holder, int position) {
- holder.item_tv.setText(mTitles[position]);
- holder.itemView.setTag(position);
- }
最後我們在外部使用一下接口:
[java] view plaincopy
- mAdapter = new TestRecyclerAdapter(this, new TestRecyclerAdapter.OnRecyclerItemClickListener() {
- @Override
- public void onItemClick(View view,int position) {
- Toast.makeText(RecyclerViewTestActivity.this, "點擊了第"+position+"項", Toast.LENGTH_SHORT).show();
- }
- });
運行結果如下:
(六).RecyclerView數據添加刪除處理
講了以上RecyclerView中各種用戶,處理之後,現在我們來看一下當數據發生變化之後的處理。例如我們在使用ListView的時候,當數據發生變化的時候可以通過notifyDatasetChange()來刷新界面。對於RecyclerView控件來講,給我們提供更加高級的使用方法notifyItemInserted(position)和notifyItemRemoved(position)
我們可以在TestRecyclerAdapter中添加數據新增和數據刪除的方法如下:
[java] view plaincopy
- //添加數據
- public void addItem(String data, intposition) {
- mTitles.add(position, data);
- notifyItemInserted(position);
- }
- //刪除數據
- public void removeItem(String data) {
- int position = mTitles.indexOf(data);
- mTitles.remove(position);
- notifyItemRemoved(position);
- }
然後我們在Activity中進行調用即可:
[java] view plaincopy
- //添加數據
- mAdapter.addItem("additem",5);
- //刪除數據
- mAdapter.removeItem("item4");
在運行之前我們不要忘記RecyclerView給提供了動畫設置,我這邊就直接采用了默認動畫,設置方法如下:
[java] view plaincopy
- //添加默認的動畫效果
- recyclerView_one.setItemAnimator(new DefaultItemAnimator());
最終運行效果如下圖:
(七).RecyclerView總結
到此為止就完成我們RecyclerView控件使用的第一講內容,其中包括控件的基本介紹,基本使用,高級用法(自定義間隔符,加入點擊監聽事件以及Item添加刪除動畫處理)。 總體來講RecyclerView控件是非常不錯,尤其在布局以及數據綁定,動畫方面,除了系統內置的三種布局方式之外,我們還可以定制出我們自己的布局 管理器。同時當item數據發生變化的時候還給我們提供非常炫的效果。相信大家在今天這一講之後,會越來越愛上RecyclerView控件的使用,從此 可以拋棄ListView和GridView啦.
本次具體實例注釋過的全部代碼已經上傳到FastDev4Android項目中了。同時歡迎大家去Github站點進行clone或者下載浏覽:
https://github.com/jiangqqlmj/FastDev4Android 同時歡迎大家star和fork整個開源快速開發框架項目~下一講我們會通過一個具體實例來自定義實現一個廣告條控件實例。