最近看了不少 RecyclerView 的使用文章,一直暈乎乎的,徹底不知道套路是啥。不少人都是直接上代碼,可是卻沒有詳細說明代碼的使用,因而打算本身寫寫,理理思路。順便幫助那些正在學習 Android 的新人。android
本文源碼參見 https://github.com/huanshen/Learn-Android/tree/master/recycleTestgit
首先 recycleView 須要咱們引入,因此在 build.gradle ( model ) 中引入:github
compile 'com.android.support:recyclerview-v7:26.0.+'
下面咱們開始寫佈局文件:canvas
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <android.support.v7.widget.RecyclerView android:id="@+id/my_recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="vertical"/> </RelativeLayout>
這至關於在屏幕上佔了個位置給 recyclerView 用,可是咱們還得爲其添加 item 項,item 也得有本身的佈局呢,因而咱們繼續編寫下面這個佈局。數組
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="wenzi " android:id="@+id/text"/> </LinearLayout>
佈局寫好了,接下去就是怎麼把數據在 view 上進行顯示。這個固然得用到 適配器啦, recycleView 有本身的適配器,咱們只須要繼承就好,而後編寫具體的處理代碼,具體以下,後面有詳細的分析的。app
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> { public String[] datas = null; public MyAdapter(String[] data) { datas = data; } //建立新View,被LayoutManager所調用 @Override public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item,viewGroup,false); ViewHolder vh = new ViewHolder(view); return vh; } //將數據與界面進行綁定的操做 @Override public void onBindViewHolder(ViewHolder viewHolder, int position) { viewHolder.mTextView.setText(datas[position]); } //獲取數據的數量 @Override public int getItemCount() { return datas.length; } //自定義的ViewHolder,持有每一個Item的的全部界面元素 public class ViewHolder extends RecyclerView.ViewHolder { public TextView mTextView; public ViewHolder(View view){ super(view); mTextView = (TextView) view.findViewById(R.id.text); } } }
這裏咱們再來細細分析下繼承以後要寫的東西,首先是構造函數 MyAdapter,用來傳入所須要的數據。通常都是數組或者鏈表之類的,這樣才能造成列表啊。ide
其次是 onCreateViewHolder, 我把它翻譯成 「建立視圖容器」,就是用來裝 item 視圖的。先獲取 item 的視圖,而後再把它放進容器便可。函數
接下去就是 onBindViewHolder,就是對容器裏的 item 的每個項進行綁定,這樣咱們才能將數據映射到 view 上進行顯示啊。佈局
而後就是 getItemCount 了,它其實就是返回一個數量,就是最後到底建立了幾個。性能
最後呢,咱們自定義了一個 ViewHolder ,繼承於RecyclerView.ViewHolder。 這個就更好理解啦,就是咱們要把 item 中的每一項都先裝進ViewHolder 這個大容器裏面,這樣咱們才能進行前面 綁定啊。這裏定義了一個 mTextView, 注意它也出如今了 onBindViewHolder 中噢。
把代碼好好看一遍,而後再閱讀一遍上面的分析,你應該就知道怎麼用了呢。
下一步,咱們就要開始用它們啦,具體見代碼以下:
private RecyclerView mRecyclerView; private RecyclerView.LayoutManager mLayoutManager; private RecyclerView.Adapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mRecyclerView = (RecyclerView)findViewById(R.id.my_recycler_view); //建立默認的線性LayoutManager mLayoutManager = new LinearLayoutManager(this); mRecyclerView.setLayoutManager(mLayoutManager); //若是能夠肯定每一個item的高度是固定的,設置這個選項能夠提升性能 mRecyclerView.setHasFixedSize(true); //建立並設置Adapter mAdapter = new MyAdapter(new String[]{"1231","43252345","2342342"}); mRecyclerView.setAdapter(mAdapter); }
首先咱們得找到原來的 RecyclerView 這個大容器,而後咱們建立一個默認的線性 Layoutmanger,並設置 RecyclerView 爲線性得得。 而後咱們在將數據傳入到 MyAdapter 中,並建立一個它的實例,最後調用 setAdapter 便可。
咱們前面展示的是 只有一個 item,而且樣式都是同樣的,那咱們能不能有多種不一樣的樣式呢?答案是能夠。
如上圖所示,咱們讓它奇偶採用不一樣的樣式。固然,咱們徹底還能夠自由發揮實現有圖和無圖的。
那這個具體怎麼實現的呢,仍是在上一個代碼基礎上進行修改。
首先,咱們添加一個新的樣式,叫 item1.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:id="@+id/root" android:layout_height="wrap_content"> <TextView android:layout_width="match_parent" android:layout_height="56dp" android:text="wenzi " android:background="@color/colorAccent" android:layout_margin="10dp" android:id="@+id/text"/> </LinearLayout>
而後咱們開始添加實現臉多個item 的代碼,主要添加在 咱們本身寫的適配器裏面,activity 的代碼不用動。
private final int ITEM = 1; private final int ITEM1 = 2;
首先咱們定義了兩個常量來表示 兩種不一樣的類型。由於有兩種類型,天然要定義兩種不一樣的 viewhold 了。具體代碼以下:
//自定義的ViewHolder,持有每一個Item的的全部界面元素 public class ImageHolder extends RecyclerView.ViewHolder { public TextView mTextView; public ImageHolder(View view){ super(view); mTextView = (TextView) view.findViewById(R.id.text); } } public class ColorHolder extends RecyclerView.ViewHolder{ public TextView mTextView; public ColorHolder(View view){ super(view); mTextView = (TextView) view.findViewById(R.id.text); } }
一樣的咱們也要建立兩種不一樣的 viewHold 容器,那咱們怎樣才能知道咱們須要建立哪種呢?
這時候,getItemViewType 就能夠派上用場了,這個函數就是根據不一樣的位置,爲咱們肯定不一樣的類型的。
因爲咱們的數據比較簡單,我是用奇偶來劃分的。那若是真的操做的時候,咱們必須在提供數據的同時,還要提供數據的類別,這樣咱們才知道那種數據採用哪一種 view 進行展現。
public int getItemViewType(int position) { if (position % 2 == 0){ return ITEM; } return ITEM1; }
當咱們把數據類型劃定好了,就能夠來建立 viewHold 了。
//建立新View,被LayoutManager所調用 @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { if (viewType == ITEM) { View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item, viewGroup, false); ImageHolder vh = new ImageHolder (view); return vh; }else{ View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item1, viewGroup, false); ColorHolder vh = new ColorHolder(view); return vh; } }
上面的代碼,咱們根據不一樣的類型,來引用不一樣的佈局。
建立好以後,就是對數據進行綁定啦。具體代碼以下:
//將數據與界面進行綁定的操做 @Override public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) { if (viewHolder instanceof ImageHolder) { //Toast.makeText(MainActivity.this, datas[position], Toast.LENGTH_SHORT).show(); ((ImageHolder)viewHolder).mTextView.setText(datas[position]); viewHolder.itemView.setTag(position); }else { ((ColorHolder)viewHolder).mTextView.setText(datas[position]); viewHolder.itemView.setTag(position); } }
綁定的時候,咱們要對 viewHold 的類型進行斷定,只有這樣咱們才能正確的將數據綁定到 view 上。
好了,到這裏咱們就完成了呢,相信你應該可以掌握 recycleView 了。
咱們讓每一個 item 點擊的時候,彈出相對應的 position 。
首先,咱們要實現 View.OnClickListener 接口,讓 item 能夠點擊。
public class ImageHolder extends RecyclerView.ViewHolder implements View.OnClickListener { public TextView mTextView; public ImageHolder(View view){ super(view); mTextView = (TextView) view.findViewById(R.id.text); view.setOnClickListener(this); } @Override public void onClick(View view) { if (mOnRvItemClick != null) mOnRvItemClick.onItemClick(view, getAdapterPosition()); } }
這裏,咱們在 ImageHolder 實現該接口,重寫了 onClick 方法。方法裏面的內容後面再說。該方法的參數並無 position ,所以須要咱們在作一些處理。position 位置的獲取主要是經過 getAdapterPosition 來實現的。
咱們定義了一個 item 的點擊接口
public interface onRecyclerViewItemClick { void onItemClick(View v, int position); }
這裏咱們定義了一個 onRecyclerViewItemClick 接口,其內部方法則是 onItemClick,能夠看到咱們有 position 參數了。
public MyAdapter(String[] data, onRecyclerViewItemClick onRvItemClick) { datas = data; mOnRvItemClick = onRvItemClick; }
而後咱們在初始化的時候,將 onRecyclerViewItemClick 的實例 onRvItemClick 傳給私有參數 mOnRvItemClick;而後再 onClick 中進行調用,具體見前面的所說的。
最後,咱們在調用便可。
mAdapter = new MyAdapter(data, new MyAdapter.onRecyclerViewItemClick() { @Override public void onItemClick(View v, int position) { Toast.makeText(MainActivity.this, "第" + position + "行", Toast.LENGTH_SHORT).show(); } }); mRecyclerView.setAdapter(mAdapter);
recyclerView 有一個默認的分割線方法:DividerItemDecoration。
只需這樣調用就能夠:
mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
分割線也是分水平仍是豎直的。若是咱們須要自定義本身的分割線也是能夠的。咱們只須要 extends RecyclerView.ItemDecoration 重寫其中的一些方法就能夠。
1 package com.example.shenjiaqi.httpshiyong; 2 3 import android.annotation.SuppressLint; 4 import android.content.Context; 5 import android.content.res.TypedArray; 6 import android.graphics.Canvas; 7 import android.graphics.Rect; 8 import android.graphics.drawable.Drawable; 9 import android.support.annotation.NonNull; 10 import android.support.v7.widget.LinearLayoutManager; 11 import android.support.v7.widget.RecyclerView; 12 import android.view.View; 13 import android.widget.LinearLayout; 14 15 /** 16 * Created by shenjiaqi on 2017/10/22. 17 */ 18 19 public class MyDecoration extends RecyclerView.ItemDecoration { 20 /** 21 * 22 * @param outRect 邊界 23 * @param view recyclerView ItemView 24 * @param parent recyclerView 25 * @param state recycler 內部數據管理 26 *//* 27 @Override 28 public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 29 //設定底部邊距爲1px 30 *//*outRect.set(0, 0, 0, 30);*//* 31 }*/ 32 33 public static final int HORIZONTAL = LinearLayout.HORIZONTAL; 34 public static final int VERTICAL = LinearLayout.VERTICAL; 35 36 private static final int[] ATTRS = new int[]{ android.R.attr.listDivider }; 37 38 private Drawable mDivider; 39 40 /** 41 * Current orientation. Either {@link #HORIZONTAL} or {@link #VERTICAL}. 42 */ 43 private int mOrientation; 44 45 private final Rect mBounds = new Rect(); 46 47 /** 48 * Creates a divider {@link RecyclerView.ItemDecoration} that can be used with a 49 * {@link LinearLayoutManager}. 50 * 51 * @param context Current context, it will be used to access resources. 52 * @param orientation Divider orientation. Should be {@link #HORIZONTAL} or {@link #VERTICAL}. 53 */ 54 public MyDecoration(Context context, int orientation) { 55 final TypedArray a = context.obtainStyledAttributes(ATTRS); 56 mDivider = a.getDrawable(0); 57 a.recycle(); 58 setOrientation(orientation); 59 } 60 61 /** 62 * Sets the orientation for this divider. This should be called if 63 * {@link RecyclerView.LayoutManager} changes orientation. 64 * 65 * @param orientation {@link #HORIZONTAL} or {@link #VERTICAL} 66 */ 67 public void setOrientation(int orientation) { 68 if (orientation != HORIZONTAL && orientation != VERTICAL) { 69 throw new IllegalArgumentException( 70 "Invalid orientation. It should be either HORIZONTAL or VERTICAL"); 71 } 72 mOrientation = orientation; 73 } 74 75 /** 76 * Sets the {@link Drawable} for this divider. 77 * 78 * @param drawable Drawable that should be used as a divider. 79 */ 80 public void setDrawable(@NonNull Drawable drawable) { 81 if (drawable == null) { 82 throw new IllegalArgumentException("Drawable cannot be null."); 83 } 84 mDivider = drawable; 85 } 86 87 @Override 88 public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { 89 if (parent.getLayoutManager() == null) { 90 return; 91 } 92 if (mOrientation == VERTICAL) { 93 drawVertical(c, parent); 94 } else { 95 drawHorizontal(c, parent); 96 } 97 } 98 99 @SuppressLint("NewApi") 100 private void drawVertical(Canvas canvas, RecyclerView parent) { 101 canvas.save(); 102 final int left; 103 final int right; 104 if (parent.getClipToPadding()) { 105 left = parent.getPaddingLeft(); 106 right = parent.getWidth() - parent.getPaddingRight(); 107 canvas.clipRect(left, parent.getPaddingTop(), right, 108 parent.getHeight() - parent.getPaddingBottom()); 109 } else { 110 left = 0; 111 right = parent.getWidth(); 112 } 113 114 final int childCount = parent.getChildCount(); 115 for (int i = 0; i < childCount; i++) { 116 final View child = parent.getChildAt(i); 117 parent.getDecoratedBoundsWithMargins(child, mBounds); 118 final int bottom = mBounds.bottom + Math.round(child.getTranslationY()); 119 final int top = bottom - mDivider.getIntrinsicHeight(); 120 mDivider.setBounds(left, top, right, bottom); 121 mDivider.draw(canvas); 122 } 123 canvas.restore(); 124 } 125 126 @SuppressLint("NewApi") 127 private void drawHorizontal(Canvas canvas, RecyclerView parent) { 128 canvas.save(); 129 final int top; 130 final int bottom; 131 if (parent.getClipToPadding()) { 132 top = parent.getPaddingTop(); 133 bottom = parent.getHeight() - parent.getPaddingBottom(); 134 canvas.clipRect(parent.getPaddingLeft(), top, 135 parent.getWidth() - parent.getPaddingRight(), bottom); 136 } else { 137 top = 0; 138 bottom = parent.getHeight(); 139 } 140 141 final int childCount = parent.getChildCount(); 142 for (int i = 0; i < childCount; i++) { 143 final View child = parent.getChildAt(i); 144 parent.getLayoutManager().getDecoratedBoundsWithMargins(child, mBounds); 145 final int right = mBounds.right + Math.round(child.getTranslationX()); 146 final int left = right - mDivider.getIntrinsicWidth(); 147 mDivider.setBounds(left, top, right, bottom); 148 mDivider.draw(canvas); 149 } 150 canvas.restore(); 151 } 152 153 @Override 154 public void getItemOffsets(Rect outRect, View view, RecyclerView parent, 155 RecyclerView.State state) { 156 int childAdapterPosition = parent.getChildAdapterPosition(view); 157 158 int lastCount = parent.getAdapter().getItemCount() - 1; 159 //若是當前條目與是最後一個條目,就不設置divider padding 160 if (childAdapterPosition == lastCount) { 161 outRect.set(0, 0, 0, 0); 162 return; 163 } 164 if (mOrientation == VERTICAL) { 165 outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); 166 } else { 167 outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); 168 } 169 } 170 }
爲了用咱們本身的分割線
在 drawable 中建一個 divider.xml 的文件
<?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>
而後咱們再在 style 文件中放一個 item ,name 就叫 android:listDivider;引入上面的文件。
<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</item> </style> </resources>