前言:如下圖所示:要使用RecycleView 需要設定LayoutManager來決佈局,以及Adapter (RecyclerView.Adapter) 並用Adapter來接收Data
RecyclerView是 Google釋出的新元件,用來取代ListView跟GridView 以及StraggeredGridView,只要設定好LayoutManager就可以輕鬆改變佈局,非常的實用。網路上有非常多的教程,程式碼參考(複制) Squire Island 裏頭的設計方式給我很大的啟發,以及Android RecylcerView藝術般的控件 堪稱入門神器XDD
- 第一步:添加第三方控件
首先要在gradle的dependencies裏頭添加控件包:recyclerView
compile 'com.android.support:recyclerview-v7:23.2.0':
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.4.0' compile 'com.android.support:recyclerview-v7:23.2.0' }
如果沒有加入控件包的話繼續下一步會業障重喔XDD
- 第二步:在XML添加RecyclerView元件
首先進入res/layout 找到 activity_main.xml 加入RecyclerView:
<android.support.v7.widget.recyclerview xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/recycler_view" android:layout_height="match_parent" android:layout_width="match_parent" android:layout_centerhorizontal="true" android:layout_centervertical="true"/>
- 接著我們要加入ViewHolder,設定ViewHolder從簡單來說,裏頭的內容決定了每一個格子會有什麼元件:舉下圖來看好了,裏頭的Item 0~3就只有一個TextView而已!
- 這時候我們繼續在res/layout裡頭,點擊右鍵創建一個新的Layout,我決定把它取名叫做....... Hmmm.....就決定是Rick惹!?(被巴)
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingRight="10dp"> <TextView android:id="@+id/text" android:layout_height="wrap_content" android:layout_width="match_parent" android:layout_gravity="center" android:textSize="@dimen/text_size" android:background="@color/colorLightBule" /> </LinearLayout>
- 第三步:初始化RecyclerVIew:
public class MainActivity extends AppCompatActivity { private RecyclerView mRecyclerView; private RecyclerView.LayoutManager mLayoutManager; private ListmDatas; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); putDataBase(); settingRecycleView(); } private void putDataBase(){ mDatas = new ArrayList (); for(int i =0 ; i < 50 ; i++) { mDatas.add(String.valueOf(i)); } } private void settingRecycleView(){ mRecyclerView = (RecyclerView)findViewById(R.id.recycler_view); mRecyclerView.setHasFixedSize(true); mRecyclerView.addItemDecoration(new MarginDecoration(this, MarginDecoration.VERTICAL_LIST)); mLayoutManager = new StaggeredGridLayoutManager((2), StaggeredGridLayoutManager.VERTICAL); mRecyclerView.setLayoutManager(mLayoutManager); mRecyclerView.setAdapter(new RecycleViewAdapter(mDatas)); } }
在settingRecyclerView的部分,我們增加了等等會提到的ItemDecoration 以及設定好 mLayoutManager 是 StaggeredGridLayoutManager。
在這裡必需值得一提的是還提供LinearLayoutManager跟GridLayoutManager,當然也可以自訂自己需要的Manager。
在這裡我選擇了StaggeredGridLayoutManager :瀑布流(中文名字感覺很像某種拳法?!)
StaggeredGridLayout主要的特徵是可以隨著內容的多寡來調整,如下圖所示,測試測試測試測試測試~~~~ 的欄位佔了左上角,右邊的排序就變成 右邊0 - 1 - 2 - 左邊 3
如果把 StaggeredGridLayoutManager 改成 GridLayoutManager,右邊的 1,會跟左邊的測試測試測試咚滋咚滋咚滋:一樣大小,各位看官有空可以嘗試看看呦!
最後再把未來會寫的Adapter丟進去就可以了!
- 第四部:創建ViewHolder
public class ViewHolder extends RecyclerView.ViewHolder { public TextView mTextView; public ViewHolder(View itemView) { super(itemView); mTextView = (TextView)itemView.findViewById(R.id.text); } }
- 第五步:設定RecyclerView Adapter
創建好RecyclerViewAdapter後,有三個常用到的function需要@Override
- onCreateViewHolder :主要是從res/layout裡頭把要的layout放進去裡頭,我們會將之前已經新增好的recycleview_holder放入第四步創建好的ViewHolder裡頭
- onBindViewHolder :只要是每個item view被放進顯示用的list內時的callback,所以要依照格子位置(position)設定正確資料,待會會將資料放在我們先前創建好的TextView裡頭
- getItemCount:主要是決定這個RecyclerView到底有幾個item好正確的畫出scroll bar和處理滑動事件用的,等等我會使用mData.Size(); 來設定item的數量。
public class RecycleViewAdapter extends RecyclerView.Adapter{ private List mDatas; public RecycleViewAdapter(List mDatas){ if(mDatas == null){ throw new IllegalArgumentException("invaild DataBase"); }else{ this.mDatas = mDatas; } } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycleview_holder, parent, false); ViewHolder vh = new ViewHolder(v); return vh; } @Override public void onBindViewHolder(ViewHolder holder, int position) { holder.mTextView.setText(mDatas.get(position)); } @Override public int getItemCount() { return mDatas.size(); } }
如果都順利的完成以上步驟的話,恭喜你,初步應該能夠跑出一些畫面:
附註:記得把之前在MainActivty寫的"mRecylceView.addItemDecoration"給Mark掉,等等我們在下一步才會開始寫這個function.
現在執行的話看起來應該會像是下圖,你會發現圖案間沒有空格也沒有線,看起來沒有很好看,所以接下來,我打算寫一個非常簡單的Decoration來讓格跟格子分隔開來。
第六步:增加Decoration 首先我做了以下幾個步驟:拿取Android內的圖像資源android.R.attr.listDivider來當等等會繪製的線
- 依照剛剛判定的結果來繪製Decoration
- 再從getItemOffsets來設定每個item decoration的尺算
public class MarginDecoration extends RecyclerView.ItemDecoration { private int margin; private static final int[] ATTRS = new int[]{ android.R.attr.listDivider }; public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL; public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL; private Drawable mDivider; private int mOrientation; /* * setting Resource from attr * */ public MarginDecoration(Context context,int orientation ) { margin = context.getResources().getDimensionPixelSize(R.dimen.item_margin); final TypedArray a = context.obtainStyledAttributes(ATTRS); mDivider = a.getDrawable(0); a.recycle(); setOrientation(orientation); } public void setOrientation(int orientation) { if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) { throw new IllegalArgumentException("invalid orientation"); } mOrientation = orientation; } @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { if (mOrientation == VERTICAL_LIST) { drawVertical(c, parent); } else { drawHorizontal(c, parent); } } public void drawVertical(Canvas c, RecyclerView parent) { final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int left = child.getLeft(); final int right =left +child.getWidth(); final int top = child.getBottom() + params.bottomMargin +margin; final int bottom = top + mDivider.getIntrinsicHeight() ; mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } public void drawHorizontal(Canvas c, RecyclerView parent) { final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int top = child.getTop(); final int bottom = top + child.getHeight(); final int left = child.getRight() + params.rightMargin; final int right = left + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { outRect.set(margin, margin, margin , margin); } }接下來我想要講解一下我的剛剛做的步驟: 首先我創建了一個建構子:
MarginDecoration裏頭主要是將從android.attr裏頭拿出listDivider
接著判定了再MainActivty傳來的方向,然在onDraw裡頭來決定如果是橫向(vertical)的話要怎麼畫,如果是直向的話要怎麼畫(horizonal)
onDraw畫圖其實並沒有非常困難,而且這個onDraw這個方法也常常被用到(例如自訂元件)
但onDraw這種看不見圖的方式讓我非常困擾,所以我打算在這邊分享幾個ideal,上頭寫的 drawVertical 跟 drawHorizonal裏頭的code並沒有很重要,主要是知道怎麼畫,怎麼變化比較重要!
舉個例子好了,現在我想要畫一個直線:
裡頭我們可以看到裏頭有一個 .setBounds(left,top,right,bottom)的function,那我到底要怎弄的?
假想一下,這條橫線從左邊開始繪製:所以我打算將left設定為原點:0 ,繪製多少呢?繪製到右邊 10好了!那程式看起來就會是(0,10)接著我們在設定高度為1好了,那程式看起來就會是 (0,1),Android的原點在左上方,並不是一開始直覺式的認為應該是在左上方:如下圖
所以這樣寫來應該是:mDivider.setBounds(0,0,10,1);
如果想要再往下畫一條線,左邊跟右邊的長度不見,而高度一樣也是設定為一:
那看起來應該像是:mDivider.setBounds(0,1,10,2);這樣看起來感覺有沒有比較簡單?
完成了Decoration後我們就只要很簡單的在MainActivty套用上他就好了:
mLayoutManager = new StaggeredGridLayoutManager((2), StaggeredGridLayoutManager.VERTICAL);
看起的圖會像這樣:
如果把程式碼改成這樣:
mLayoutManager = new StaggeredGridLayoutManager((2), StaggeredGridLayoutManager.Horizontal);
這樣看起來有沒有很簡單?專案已經快要完成了,接下來只要改幾地方好了:
首先我們在drawable創建一個 xml檔案,用來替換 divider 的顏色,所以在這裡我打算命名為:divider_bg.xml
程式碼如下:
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <gradient android:centerColor="#7C6EFD" android:endColor="#FFFFFF" android:startColor="#FFFFFF" android:type="linear" /> <size android:height="1dp"/> </shape>
接著最後在res/values/styles.xml裏頭增加
<item name="android:listDivider">@drawable/divider_bg</item>
程式碼看起來會像是這樣:
<resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> <item name="android:listDivider">@drawable/divider_bg</item> </style> </resources>這樣我們的分隔線就有顏色了! 以上介紹了如何創建,如何使用,以及如何改變裝飾線(decoration)的用法,當然,RecylceView還有許許多多的用法,以及更深入的解析,就需要大家再深一步的鑽研了!
以下是源碼:
https://github.com/book87118/Android_Googlecalendar_Outlookcalender_Sync
希望大家coding愉快
Ps:如果有錯誤的地方或者有其他問題,希望大家可以留言給我呦,在我能力範圍之內會盡量幫助大家,如果這篇文章有幫助到你還麻煩在Git上面點讚呦XDDD(溜)