前言:
RecyclerView是 Google釋出的新元件,用來取代ListView跟GridView 以及StraggeredGridView,只要設定好LayoutManager就可以輕鬆改變佈局,非常的實用。網路上有非常多的教程,程式碼參考(複制) Squire Island 裏頭的設計方式給我很大的啟發,以及Android RecylcerView藝術般的控件 堪稱入門神器XDD
如下圖所示:要使用RecycleView 需要設定LayoutManager來決佈局,以及Adapter (
RecyclerView.Adapter) 並用Adapter來接收Data
首先要在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
首先進入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惹!?(被巴)
取好名字叫做 recyclerview_holder以後,我們繼續在裡頭創建一個非常簡單的TextView代碼如下:
<?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的Adapter以及等等會用得
ItemDecoration,現在一起把所有的東西都設定好吧!
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private RecyclerView.LayoutManager mLayoutManager;
private List mDatas;
@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丟進去就可以了!
在寫Adapter之前必須先自訂一個ViewHolder,這裡頭主要是拿來定義要放什麼控件在Adapter裡頭,我們現在創建一個Java腳本,命名為ViewHolder,準備把之後會拿來接收資料的TextView設定好!
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView mTextView;
public ViewHolder(View itemView) {
super(itemView);
mTextView = (TextView)itemView.findViewById(R.id.text);
}
}
- 第五步:設定RecyclerView Adapter
首先我們創建一個Java檔案:暫時把它取名叫做......hmmm......就決定把你命名為
Pikachu!!!!!!
創建好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的數量。
PS:以上這段感謝掐理大大的指證
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來當等等會繪製的線
判定等等的StaggeredGridLayout會顯示橫向還是縱向(Vertical or Horizontal)
- 依照剛剛判定的結果來繪製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(溜)