《Android》『RecyclerView』 – 透過 recyclerView 實現 ListView 效果的基本用法
《Android Developers 參考文獻》
《簡單介紹》
在以前,若是我們想在有限的手機螢幕上,顯示多個選項、資料或者圖片,通常會使用 ListView、GridView 或者 Gallery 這三種元件,目的是讓使用者能夠有條理的瀏覽所有資訊,但在使用這些元件的時候,卻往往造成了一些不必要的系統資源浪費,在 Android 5.0 (Lollipop) 的版本以後,google 提供了一個新的方法 – RecyclerView,利用這個新的元件,我們可以有效地實現 ListView、GridView 與 Gallery 這三種元件的效果,且在使用上更加的靈活,相信不久之後,RecyclerView 將會逐漸完全取代這些元件。
《基本用法》
在 RecyclerView 中,我們透過設定 LayoutManager 來決定它的呈現方式;設定 ItemDecoration 來決定 Item 之間的間隔;設定 ItemAnimator 來決定 Item 增加或刪除時的動畫,這邊我們先以實作一個垂直滑動的 RecyclerView 為例,用來取代 ListView 元件。
1 2 3 4 5 6 7 8 9 10 11 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout> |
activity_main.xml
首先我們在 Layout 佈局檔中加入 RecyclerView。
1 2 3 4 5 6 7 8 9 10 11 12 |
<?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="match_parent"> <TextView android:id="@+id/txtItem" android:layout_width="match_parent" android:layout_height="30dp"/> </LinearLayout> |
item_view.xml
接著建立一個 item_view.xml 佈局檔,用來當作 RecyclerView 中的每一個 item 的 view。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
. . . public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> { private List<String> mDataSet; public MyAdapter(List<String> data) { mDataSet = data; } @Override public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view, parent, false); ViewHolder viewholder = new ViewHolder(view); return viewholder; } @Override public void onBindViewHolder(ViewHolder holder, int position) { holder.txtItem.setText(mDataSet.get(position)); } @Override public int getItemCount() { return mDataSet.size(); } public class ViewHolder extends RecyclerView.ViewHolder { public TextView txtItem; public ViewHolder(View v) { super(v); txtItem = (TextView) v.findViewById(R.id.txtItem); } } } |
MyAdapter
與 ListView 一樣,我們都必須建立一個 Adapter 來控管每個 item 的顯示設定,我們自訂了一個名為 MyAdapter 的類別,此類別繼承了 RecyclerView.Adapter ,並在其中帶入自訂的 MyAdapter.ViewHolder,相關覆寫的方法說明如下 –
- 透過建構子把資料清單(mDataSet)傳進來。
- 在 MyAdapter 中自訂一個繼承 RecyclerView.ViewHolder 的類別,這邊命名為 ViewHolder,並在此類別中定義每個 item 的介面與邏輯。
- 在 onCreateViewHolder 中指定 item 所使用的 view 佈局,並將該 view 轉換成 MyAdapter.ViewHolder 之物件回傳,如此一來,我們就可以使用在前面自訂的 ViewHolder 類別中,所定義的每個 item 的介面。
- 在 onBindViewHolder 中透過 position 參數指定每個 item 所用到的資料 (mDataSet.get(position))。
- 在 getItemCount 中回傳清單數量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
. . . public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { RecyclerView recyclerView; super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ArrayList<String> Dataset = new ArrayList<String>(); for(int i = 0; i < 10; i++) { Dataset.add(i + ""); } MyAdapter myAdapter = new MyAdapter(myDataset); LinearLayoutManager layoutManager = new LinearLayoutManager(this); //設定此 layoutManager 為 linearlayout (類似ListView) layoutManager.setOrientation(LinearLayoutManager.VERTICAL); 設定此 layoutManager 為垂直堆疊 recyclerView = (RecyclerView) findViewById(R.id.recyclerView); recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST)); //設定分割線 recyclerView.setLayoutManager(layoutManager); //設定 LayoutManager recyclerView.setAdapter(myAdapter); //設定 Adapter } } |
MainActivity.java
定義好 Adapter 以後,我們就可以直接來使用 RecyclerView 啦!這邊除了要 setAdapter(),另外還要 setLayoutManager(),透過所使用的 layoutManager 來決定 RecyclerView 的呈現方式,另外若有需要,透過 addItemDecoration 來設定分割線。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
package com.zhy.sample.demo_recyclerview; /* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * limitations under the License. */ import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.State; import android.util.Log; import android.view.View; /** * This class is from the v7 samples of the Android SDK. It's not by me! * <p/> * See the license above for details. */ public class DividerItemDecoration extends RecyclerView.ItemDecoration { 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; public DividerItemDecoration(Context context, int orientation) { 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) { Log.v("recyclerview - itemdecoration", "onDraw()"); if (mOrientation == VERTICAL_LIST) { drawVertical(c, parent); } else { drawHorizontal(c, parent); } } public void drawVertical(Canvas c, RecyclerView parent) { final int left = parent.getPaddingLeft(); final int right = parent.getWidth() - parent.getPaddingRight(); final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); android.support.v7.widget.RecyclerView v = new android.support.v7.widget.RecyclerView(parent.getContext()); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int top = child.getBottom() + params.bottomMargin; final int bottom = top + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } public void drawHorizontal(Canvas c, RecyclerView parent) { final int top = parent.getPaddingTop(); final int bottom = parent.getHeight() - parent.getPaddingBottom(); 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.getRight() + params.rightMargin; final int right = left + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } @Override public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { if (mOrientation == VERTICAL_LIST) { outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); } else { outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); } } } |
DividerItemDecoration.java
這是網路上的開源碼,所提供的是一個參照到 Android.R.attr.listDivider 內容之分割線,此分割線是系統默認的,我們可以在 theme.xml 中找到這個屬性。接著,我們將它所使用的 @drawable 改成自己的 drawable 即可。
1 2 3 4 |
<!-- Application theme. --> <style name="AppTheme" parent="AppBaseTheme"> <item name="android:listDivider">@drawable/divider_bg</item> </style> |
style.xml
1 2 3 4 5 6 7 8 9 10 11 12 |
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" > <gradient android:centerColor="#ff00ff00" android:endColor="#ff0000ff" android:startColor="#ffff0000" android:type="linear" /> <size android:height="4dp"/> </shape> |
divider_bg.xml
臉書留言
一般留言