Android開發(fā)中RecyclerView組件使用的一些進(jìn)階技講解
RecyclerView的優(yōu)勢(shì):
- 它自帶ViewHolder來(lái)實(shí)現(xiàn)View的復(fù)用機(jī)制,再也不用ListView那樣在getView()里自己寫了
- 使用LayoutManager可以實(shí)現(xiàn)ListView,GridView以及流式布局的列表效果
- 通過(guò)setItemAnimator(ItemAnimator animator)可以實(shí)現(xiàn)增刪動(dòng)畫(懶的話,可以使用默認(rèn)的ItemAnimator對(duì)象,效果也不錯(cuò))
- 控制item的間隔,可以使用addItemDecoration(ItemDecoration decor),不過(guò)里邊的ItemDecoration是一個(gè)抽象類,需要自己去實(shí)現(xiàn)...
用法介紹:
導(dǎo)入RecyclerView的v7庫(kù):
RecyclerView是一個(gè)android.support.v7庫(kù)里的控件,因此在使用的時(shí)候我們需要在gradle配置文件里加上compile 'com.android.support:recyclerview-v7:22.2.1'來(lái)引入google官方的這個(gè)庫(kù)
xml布局中,使用常規(guī)的控件引入方式,來(lái)引入RecyclerView,如下:
<android.support.v7.widget.RecyclerView android:id="@+id/recyclerview_content" style="?recyclerview_style" android:scrollbars="vertical" android:fadeScrollbars="true" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginBottom="-55dp" />
代碼中的寫法基本和ListView相差無(wú)幾,但還是要重點(diǎn)說(shuō)一下:
在實(shí)例化RecyclerView之后,我們需要使用setLayoutManager()給它設(shè)置布局管理器,其中的實(shí)參即就是LayoutManager,這里總共有兩種LayoutManager:
1.StaggeredGridLayoutManager,是我們之前提到的流式布局:
它有一個(gè)構(gòu)造方法StaggeredGridLayoutManager(int spanCount, int orientation),第一個(gè)是網(wǎng)格的列數(shù),第二個(gè)參數(shù)是數(shù)據(jù)呈現(xiàn)的方向
(如果是豎直,那么第一個(gè)參數(shù)的意義就是列數(shù),反之為行數(shù)。而且第二個(gè)參數(shù)在StaggeredGridLayoutManager里也有同樣名稱的常量,請(qǐng)同學(xué)們自行采納[這里還是建議大家使用庫(kù)里自帶的常量,因?yàn)樗麄円话愣际钦椭担@樣可以避免各個(gè)類里所判別的常量值不一樣而導(dǎo)致的其他問(wèn)題]);
2.GridLayoutManager,網(wǎng)格布局(流式布局應(yīng)該是它的一個(gè)特殊情況):
GridLayoutManager(Context context, int spanCount)或
GridLayoutManager(Context context, int spanCount, int orientation,
boolean reverseLayout)需要說(shuō)明的是,最后一個(gè)參數(shù)表示的是是否逆向布局(意思是將數(shù)據(jù)反向顯示,原先從左向右,從上至下。設(shè)為true之后全部逆轉(zhuǎn))。
小提示:在這兩個(gè)LayoutManager中,默認(rèn)的orientation為vertical,reverseLayout為false。對(duì)應(yīng)的參數(shù)在GridLayoutManager中都有對(duì)應(yīng)的方法來(lái)進(jìn)行補(bǔ)充設(shè)置。而在StaggeredGridLayoutManager中所有的方法都針對(duì)reverseLayout做了判斷,然而它并沒(méi)有給出這個(gè)參數(shù)設(shè)定值的api。
線性布局, LinearLayoutManager
LinearLayoutManager(Context context)構(gòu)建一個(gè)默認(rèn)布局方向?yàn)閂ERTICAL的RecyclerView,這種樣式也是ListView默認(rèn)的。
LinearLayoutManager(Context context, int orientation, boolean reverseLayout)。發(fā)現(xiàn)沒(méi)有,線性布局和網(wǎng)格布局幾乎是一樣的。只是少了一個(gè)spanCount參數(shù)。
和ListView一樣,為RecyclerView設(shè)置Adapter
class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyViewHolder>
{
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
MyViewHolder holder = new MyViewHolder(LayoutInflater.from(
HomeActivity.this).inflate(R.layout.item_home, parent,
false));
return holder;
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position)
{
holder.tv.setText(mDatas.get(position));
}
@Override
public int getItemCount()
{
return mDatas.size();
}
class MyViewHolder extends ViewHolder
{
TextView tv;
public MyViewHolder(View view)
{
super(view);
tv = (TextView) view.findViewById(R.id.id_num);
}
}
}
如上,即就是Adapter的寫法。其中的ViewHolder就是拿來(lái)負(fù)責(zé)View的回收和復(fù)用的,這樣就不需要我們自己寫完ViewHolder之后,還要在getView(int position, View convertView, ViewGroup parent)里一頓判斷,一頓綁定,一頓find了。而且這里的ViewHolder成為了RecyclerView中必須繼承的一部分,重寫完后就需要放入 RecyclerView.Adapter< >這里來(lái)對(duì)基類的范型初始化。
在這里,Recyclerview已經(jīng)為你封裝好了:
- getItemCount()就不必多說(shuō)了,和ListView是一樣的
- getItemViewType(int position)是用來(lái)根據(jù)position的不同來(lái)實(shí)現(xiàn)RecyclerView中對(duì)不同布局的要求。從這個(gè)方法中所返回的值會(huì)在onCreateViewHolder中用到。比如頭部,尾部,等等的特殊itemView(這里說(shuō)成ViewHolder比較好)都可以在這里進(jìn)行判斷。
- onCreateViewHolder(ViewGroup parent, int viewType)是用來(lái)配合寫好的ViewHolder來(lái)返回一個(gè)ViewHolder對(duì)象。這里也涉及到了條目布局的加載。viewType則表示需要給當(dāng)前position生成的是哪一種ViewHolder,這個(gè)參數(shù)也說(shuō)明了RecyclerView對(duì)多類型ItemView的支持。
- onBindViewHolder(MyViewHolder holder, int position)專門用來(lái)綁定ViewHolder里的控件和數(shù)據(jù)源中position位置的數(shù)據(jù)。
這里,會(huì)有人問(wèn),那么item的子控件findViewById 去哪兒了?我們把它交給了ViewHolder的構(gòu)造方法(其他方法也可以),它的本質(zhì)是在onCreateViewHolder方法里生成ViewHolder的時(shí)候執(zhí)行的。
提升:
1.代碼重構(gòu):
在上邊看了這么多,有木有覺(jué)得,ViewHolder的功能并不是非常明確?它既負(fù)責(zé)了子控件的查詢,又負(fù)責(zé)了子控件的裝載工作。而布局加載和數(shù)據(jù)綁定卻交給了Adapter......
我們來(lái)看看掘金的做法:
首先,它把Adapter和ViewHolder的功能以一種較為低耦合的方式進(jìn)行了職能分離,讓ViewHolder里所有的邏輯代碼全部都只出現(xiàn)在ViewHolder中。我們現(xiàn)在就對(duì)前邊提到的代碼進(jìn)行重構(gòu):
ViewHodler:
class MyViewHolder extends ViewHolder{
TextView tv;
public MyViewHolder(Context context, View view){
super(view);
tv = (TextView) view.findViewById(R.id.id_num);
//當(dāng)然,我們也可以在這里使用view來(lái)對(duì)RecyclerView的一個(gè)item進(jìn)行事件監(jiān)聽,也可以使用
//tv等子控件來(lái)實(shí)現(xiàn)item的子控件的事件監(jiān)聽。這也是我之所以要傳context的原因之一呢~
......
}
public static MyViewHolder newInstance(Activity context, ViewGroup parent){
View view = LayoutInflater.from(
context).inflate(R.layout.item_home, parent,false);
return new MyViewHolder(context, view);
}
}
//你沒(méi)看錯(cuò),數(shù)據(jù)綁定也被整合進(jìn)來(lái)了,
//將adapter里的數(shù)據(jù)根據(jù)position獲取到后傳進(jìn)來(lái)。當(dāng)然,也可以根據(jù)具體情況來(lái)做調(diào)整。
public void onBinViewHolder(String data){
tv.setText(data);//既然這里也有子控件,那么這里也可以做item子控件的事件監(jiān)聽嘍
}
RecyclerView:
class HomeAdapter extends RecyclerView.Adapter<MyViewHolder>{
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType){//如需要,還要對(duì)viewType做判斷
return MyViewHolder.newInstance(this, parent)
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position){
holder.onBindViewHolder(mDatas.get(position));
}
@Override
public int getItemCount(){
return mDatas.size();
}
}
抽取一個(gè)條目點(diǎn)擊事件,讓它更像ListView:
class HomeAdapter extends RecyclerView.Adapter<MyViewHolder>{
private OnItemClickListener mOnItemClickListener;
public void setOnItemClickLitener(OnItemClickLitener mOnItemClickLitener)
{
this.mOnItemClickLitener = mOnItemClickLitener;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType){//如需要,還要對(duì)viewType做判斷
return MyViewHolder.newInstance(this, parent)
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position){
holder.onBindViewHolder(mDatas.get(position));
//如果設(shè)置了回調(diào),則設(shè)置點(diǎn)擊事件
if (mOnItemClickLitener != null) {
viewHolder.itemView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mOnItemClickLitener.onItemClick(viewHolder.itemView, i);
}
});
}
}
@Override
public int getItemCount(){
return mDatas.size();
}
public interface OnItemClickLitener {
void onItemClick(View view, int position);
}
}
接口調(diào)用
mAdapter.setOnItemClickLitener(new OnItemClickLitener() {
@Override
public void onItemClick(View view, int position) {
......
}
});
2.External
上邊提到了
控制item的間隔,可以使用addItemDecoration(ItemDecoration decor),不過(guò)里邊的ItemDecoration是一個(gè)抽象類,需要自己去實(shí)現(xiàn)...
這個(gè)問(wèn)題,那我們來(lái)實(shí)際解決一下:
這是羊神實(shí)現(xiàn)的一個(gè)子類
package com.zhy.sample.demo_recyclerview;
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;
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);
}
}
}
然后就是為我們的RecyclerView實(shí)例添加分割線
addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST));
系統(tǒng)默認(rèn)的分割線往往達(dá)不到我們產(chǎn)品和設(shè)計(jì)師的要求,怎么辦呢?
<item name="android:listDivider">@drawable/your_custom_divider</item>
通過(guò)以上xml屬性,將系統(tǒng)的listDivider設(shè)為自己畫出來(lái)的分割線,只需要放在你對(duì)應(yīng)activity的主題下即可。
技巧:RecyclerView 滾動(dòng)條的顯示與隱藏
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
>
</android.support.v7.widget.RecyclerView>
縱向顯示:
android:scrollbars="vertical"
橫向顯示:
android:scrollbars="horizontal"
隱藏:
android:scrollbars="none"
- Android RecyclerView 基礎(chǔ)知識(shí)詳解
- Android RecyclerView加載不同布局簡(jiǎn)單實(shí)現(xiàn)
- Android添加圖片到ListView或者RecyclerView顯示
- Android中RecyclerView實(shí)現(xiàn)橫向滑動(dòng)代碼
- Android RecyclerView詳解之實(shí)現(xiàn) ListView GridView瀑布流效果
- Android中RecyclerView點(diǎn)擊Item設(shè)置事件
- Android RecyclerView藝術(shù)般的控件使用完全解析
- Android代碼實(shí)現(xiàn)AdapterViews和RecyclerView無(wú)限滾動(dòng)
- Android RecyclerView滑動(dòng)刪除和拖動(dòng)排序
- Android RecyclerView的Item自定義動(dòng)畫及DefaultItemAnimator源碼分析
- 學(xué)習(xí)Android開發(fā)之RecyclerView使用初探
- Android RecyclerView 數(shù)據(jù)綁定實(shí)例代碼
相關(guān)文章
淺談android組件化之ARouter簡(jiǎn)單使用
本篇文章主要介紹了淺談android組件化之ARouter簡(jiǎn)單使用,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-09-09
Kotlin圖文并茂講解續(xù)體與續(xù)體攔截器和調(diào)度器
這篇文章主要介紹了Kotlin開發(fā)中續(xù)體與續(xù)體攔截器和調(diào)度器的相關(guān)使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08
詳解如何在Flutter中集成華為認(rèn)證服務(wù)
這篇文章主要介紹了詳解如何在Flutter中集成華為認(rèn)證服務(wù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02
Android中制作進(jìn)度框和環(huán)形進(jìn)度條的簡(jiǎn)單實(shí)例分享
這篇文章主要介紹了Android中制作進(jìn)度框和環(huán)形進(jìn)度條的簡(jiǎn)單實(shí)例分享,環(huán)形進(jìn)度條帶有基本的百分比顯示,需要的朋友可以參考下2016-03-03
Android?PickerScrollView滑動(dòng)選擇控件使用方法詳解
這篇文章主要為大家詳細(xì)介紹了Android?PickerScrollView滑動(dòng)選擇控件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
Android Flutter實(shí)現(xiàn)3D動(dòng)畫效果示例詳解
在Flutter中提供了AnimatedWidget組件用于構(gòu)建可復(fù)用的動(dòng)畫組件。本文我們用AnimatedWidget來(lái)實(shí)現(xiàn)組件的3D旋轉(zhuǎn)效果,感興趣的可以了解一下2022-03-03
Android?Material組件庫(kù)日期選擇和時(shí)間選擇器的使用方法
這篇文章主要介紹了Android?Material組件庫(kù)(日期選擇和時(shí)間選擇器)基本使用,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-11-11
Android TreeView效果實(shí)現(xiàn)方法(附demo源碼下載)
這篇文章主要介紹了Android TreeView效果實(shí)現(xiàn)方法,結(jié)合實(shí)例形式分析了Android TreeView效果的實(shí)現(xiàn)原理與具體技巧,并附帶demo源碼供讀者下載,需要的朋友可以參考下2016-02-02

