談?wù)凙ndroid開(kāi)發(fā)之RecyclerView的使用全解
自Android 5.0之后,谷歌公司推出了RecylerView控件,RecylerView,我想看到一個(gè)新名詞后大部分人會(huì)首先發(fā)出一個(gè)疑問(wèn),recylerview是什么?為什么會(huì)有recylerview也就是說(shuō)recylerview的優(yōu)點(diǎn)是什么?recylerview怎么用?接下來(lái)就對(duì)這幾個(gè)問(wèn)題來(lái)一起討論一下recylerview,如有謬誤歡迎批評(píng)指正,如有疑問(wèn)請(qǐng)留言。
通過(guò)本篇博客,你將學(xué)到以下知識(shí)點(diǎn)
①RecyclerView與ListView相比它的優(yōu)點(diǎn)
②RecyclerView的初步用法
③RecyclerView增加分隔線
④RecyclerView更改分隔線的樣式
⑤RecyclerView的Adapter的用法
⑥RecyclerView.Adapter中刷新的幾個(gè)方法的對(duì)比
⑦給RecyclerView增加條目點(diǎn)擊事件
1.RecyclerView是什么?
RecylerView是support-v7包中的新組件,是一個(gè)強(qiáng)大的滑動(dòng)組件,與經(jīng)典的ListView相比,同樣擁有item回收復(fù)用的功能,這一點(diǎn)從它的名字recylerview即回收view也可以看出。看到這也許有人會(huì)問(wèn),不是已經(jīng)有ListView了嗎,為什么還要RecylerView呢?這就牽扯到第二個(gè)問(wèn)題了。
2.RecyclerView的優(yōu)點(diǎn)是什么?
根據(jù)官方的介紹RecylerView是ListView的升級(jí)版,既然如此那RecylerView必然有它的優(yōu)點(diǎn),現(xiàn)就RecylerView相對(duì)于ListView的優(yōu)點(diǎn)羅列如下:
①RecylerView封裝了viewholder的回收復(fù)用,也就是說(shuō)RecylerView標(biāo)準(zhǔn)化了ViewHolder,編寫(xiě)Adapter面向的是ViewHolder而不再是View了,復(fù)用的 邏輯被封裝了,寫(xiě)起來(lái)更加簡(jiǎn)單。
②提供了一種插拔式的體驗(yàn),高度的解耦,異常的靈活,針對(duì)一個(gè)Item的顯示RecylerView專門抽取出了相應(yīng)的類,來(lái)控制Item的顯示,使其的擴(kuò)展性非常強(qiáng)。例如:你想控制橫向或者縱向滑動(dòng)列表效果可以通過(guò)LinearLayoutManager這個(gè)類來(lái)進(jìn)行控制(與GridView效果對(duì)應(yīng)的是GridLayoutManager,與瀑布流對(duì)應(yīng)的還有StaggeredGridLayoutManager等),也就是說(shuō)RecylerView不再拘泥于ListView的線性展示方式,它也可以實(shí)現(xiàn)GridView的效果等多種效果。你想控制Item的分隔線,可以通過(guò)繼承RecylerView的ItemDecoration這個(gè)類,然后針對(duì)自己的業(yè)務(wù)需求去抒寫(xiě)代碼。
③可以控制Item增刪的動(dòng)畫(huà),可以通過(guò)ItemAnimator這個(gè)類進(jìn)行控制,當(dāng)然針對(duì)增刪的動(dòng)畫(huà),RecylerView有其自己默認(rèn)的實(shí)現(xiàn)。
3.RecyclerView的用法
3.1 RecyclerView的初步用法(包括RecyclerView.Adapter用法)
說(shuō)了這么多,可能大家最關(guān)心的就是RecylerView應(yīng)該怎么用,我們先來(lái)討論討論RecylerView的用法的理論知識(shí),然后結(jié)合一個(gè)實(shí)例來(lái)體驗(yàn)一下RecylerView的優(yōu)勢(shì)首先我們需要明白的一點(diǎn)是使用RecylerView必須導(dǎo)入support-v7包,在上面我提到過(guò)RecylerView高度的解耦,異常的靈活谷歌給我們提供了多個(gè)類來(lái)控制Item的顯示。
recyclerView = (RecyclerView) findViewById(R.id.recyclerView); LinearLayoutManager layoutManager = new LinearLayoutManager(this ); //設(shè)置布局管理器 recyclerView.setLayoutManager(layoutManager); //設(shè)置為垂直布局,這也是默認(rèn)的 layoutManager.setOrientation(OrientationHelper. VERTICAL); //設(shè)置Adapter recyclerView.setAdapter( recycleAdapter); //設(shè)置分隔線 recyclerView.addItemDecoration( new DividerGridItemDecoration(this )); //設(shè)置增加或刪除條目的動(dòng)畫(huà) recyclerView.setItemAnimator( new DefaultItemAnimator());
可以看到對(duì)RecylerView的設(shè)置過(guò)程,比ListView要復(fù)雜一些,這也是RecylerView高度解耦的表現(xiàn),雖然代碼抒寫(xiě)上有點(diǎn)復(fù)雜,但它的擴(kuò)展性是極高的。
在了解了RecyclerView的一些控制之后,緊接著來(lái)看看它的Adapter的寫(xiě)法,RecyclerView的Adapter與ListView的Adapter還是有點(diǎn)區(qū)別的,RecyclerView.Adapter,需要實(shí)現(xiàn)3個(gè)方法:
①onCreateViewHolder()
這個(gè)方法主要生成為每個(gè)Item inflater出一個(gè)View,但是該方法返回的是一個(gè)ViewHolder。該方法把View直接封裝在ViewHolder中,然后我們面向的是ViewHolder這個(gè)實(shí)例,當(dāng)然這個(gè)ViewHolder需要我們自己去編寫(xiě)。直接省去了當(dāng)初的convertView.setTag(holder)和convertView.getTag()這些繁瑣的步驟。
②onBindViewHolder()
這個(gè)方法主要用于適配渲染數(shù)據(jù)到View中。方法提供給你了一個(gè)viewHolder,而不是原來(lái)的convertView。
③getItemCount()
這個(gè)方法就類似于BaseAdapter的getCount方法了,即總共有多少個(gè)條目。
實(shí)例:接著來(lái)幾個(gè)小的實(shí)例幫助大家更深入的了解RecyclerView的用法,首先來(lái)實(shí)現(xiàn)一個(gè)最簡(jiǎn)單的列表,效果如下
這種效果的MainAcitivity的代碼如下
package com.example.reclerviewpractice; import java.util.ArrayList; import java.util.List; import com.example.reclerviewpractice.adapter.MyRecyclerAdapter; import android.annotation.SuppressLint; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.OrientationHelper; import android.support.v7.widget.RecyclerView; public class MainActivity extends ActionBarActivity { private RecyclerView recyclerView; private List<String> mDatas; private MyRecyclerAdapter recycleAdapter; @SuppressLint("NewApi") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout. activity_main); recyclerView = (RecyclerView) findViewById(R.id.recyclerView ); initData(); recycleAdapter= new MyRecyclerAdapter(MainActivity.this , mDatas ); LinearLayoutManager layoutManager = new LinearLayoutManager(this); //設(shè)置布局管理器 recyclerView.setLayoutManager(layoutManager); //設(shè)置為垂直布局,這也是默認(rèn)的 layoutManager.setOrientation(OrientationHelper.VERTICAL); //設(shè)置Adapter recyclerView.setAdapter( recycleAdapter); //設(shè)置增加或刪除條目的動(dòng)畫(huà) recyclerView.setItemAnimator(new DefaultItemAnimator()); } private void initData() { mDatas = new ArrayList<String>(); for ( int i=0; i < 40; i++) { mDatas.add( "item"+i); } } }
RecyclerView的Adapter的代碼如下:
package com.example.reclerviewpractice.adapter; import java.util.List; import com.example.reclerviewpractice.R; import android.content.Context; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.ViewHolder; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; public class MyRecyclerAdapter extends RecyclerView.Adapter<MyRecyclerAdapter.MyViewHolder> { private List<String> mDatas; private Context mContext; private LayoutInflater inflater; public MyRecyclerAdapter(Context context, List<String> datas){ this. mContext=context; this. mDatas=datas; inflater=LayoutInflater. from(mContext); } @Override public int getItemCount() { return mDatas.size(); } //填充onCreateViewHolder方法返回的holder中的控件 @Override public void onBindViewHolder(MyViewHolder holder, final int position) { holder.tv.setText( mDatas.get(position)); } //重寫(xiě)onCreateViewHolder方法,返回一個(gè)自定義的ViewHolder @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = inflater.inflate(R.layout. item_home,parent, false); MyViewHolder holder= new MyViewHolder(view); return holder; } class MyViewHolder extends ViewHolder{ TextView tv; public MyViewHolder(View view) { super(view); tv=(TextView) view.findViewById(R.id. tv_item); } } }
可以看到RecyclerView標(biāo)準(zhǔn)化了ViewHolder,編寫(xiě) Adapter面向的是ViewHoder而不在是View了,復(fù)用的邏輯被封裝了,寫(xiě)起來(lái)更加簡(jiǎn)單。其實(shí)它的寫(xiě)法與BaseAdapter的寫(xiě)法是差不多的,大家可以對(duì)比下它與getView方法寫(xiě)法的區(qū)別,在onCreateViewHolder方法中初始化了一個(gè)View,然后返回一個(gè)ViewHolder,這個(gè)返回的ViewHolder類似于之前在getView中的convertView.getTag(),然后在onBindViewHolder方法中去給這個(gè)ViewHolder中的控件填充值。其實(shí)它的原理跟getView是差不多的,只是做了封裝,我們寫(xiě)起來(lái)比較簡(jiǎn)潔。到這里,看到上述運(yùn)行效果可能有很多人會(huì)說(shuō),這效果太丑了,連個(gè)分隔線都沒(méi)有,不要急,我們一步一步來(lái)。
3.2 RecyclerView增加分隔線
前面我們說(shuō)到可以通過(guò)RecyclerView.addItemDecoration(ItemDecoration decoration)這個(gè)方法進(jìn)行設(shè)置,其中它需要的參數(shù)就是我們自己定義的繼承自ItemDecoration的一個(gè)對(duì)象。我們可以創(chuàng)建一個(gè)繼承RecyclerView.ItemDecoration類來(lái)繪制分隔線,通過(guò)ItemDecoration可以讓我們每一個(gè)Item從視覺(jué)上面相互分開(kāi)來(lái),例如ListView的divider非常相似的效果。當(dāng)然像我們上面的例子ItemDecoration我們沒(méi)有設(shè)置也沒(méi)有報(bào)錯(cuò),那說(shuō)明ItemDecoration我們并不是強(qiáng)制需要使用,作為我們開(kāi)發(fā)者可以設(shè)置或者不設(shè)置Decoration的。實(shí)現(xiàn)一個(gè)ItemDecoration,系統(tǒng)提供的ItemDecoration是一個(gè)抽象類,內(nèi)部除去已經(jīng)廢棄的方法以外,我們主要實(shí)現(xiàn)以下三個(gè)方法:
public static abstract class ItemDecoration { public void onDraw(Canvas c,RecyclerView parent,State state) { onDraw(c,parent); } public void onDrawOver(Canvas c,RecyclerView parent,State state) { onDrawOver(c,parent); } public void getItemOffsets(RectoutRect, View view,RecyclerView parent,State state) { getItemOffsets(outRect,((LayoutParams)view.getLayoutParams()).getViewLayoutPosition(),parent); } }
又因?yàn)楫?dāng)我們RecyclerView在進(jìn)行繪制的時(shí)候會(huì)進(jìn)行繪制Decoration,那么會(huì)去調(diào)用onDraw和onDrawOver方法,那么這邊我們其實(shí)只要去重寫(xiě)onDraw和getItemOffsets這兩個(gè)方法就可以實(shí)現(xiàn)啦。然后LayoutManager會(huì)進(jìn)行Item布局的時(shí)候,會(huì)去調(diào)用getItemOffset方法來(lái)計(jì)算每個(gè)Item的Decoration合適的尺寸,下面我們來(lái)具體實(shí)現(xiàn)一個(gè)Decoration,DividerItemDecoration.Java
package com.example.reclerviewpractice; 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.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) { 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); 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); } } }
在這里我們采用系統(tǒng)主題(android.R.attr.listDivider)來(lái)設(shè)置成分隔線的,然后來(lái)獲取尺寸,位置進(jìn)行setBound(),繪制,接著通過(guò)outRect.set()來(lái)設(shè)置繪制整個(gè)區(qū)域范圍,當(dāng)然了它是有兩種情況的一種LinearLayoutManager.HORIZONTAL另外一種LinearLayoutManager.VERTICAL需要分別對(duì)其進(jìn)行處理,最后不要忘記往RecyclerView中設(shè)置該自定義的分割線,然后在MainActivity中加上一句recyclerView .addItemDecoration(new DividerItemDecoration(MainActivity.this,LinearLayoutManager.VERTICAL))即給RecyclerView增加分隔線。然后運(yùn)行,效果如下:
可以看到已經(jīng)有了分隔線,跟ListView的效果基本一致了。當(dāng)然了,既然谷歌給我們提供了這個(gè)專門添加分隔線的方法,那它肯定會(huì)允許我們自定義分隔線的樣式,不然把這個(gè)方法抽取出來(lái)也沒(méi)有任何意義。
3.3 改變分隔線樣式
那么怎么更改分隔線的樣式呢?在上面的DividerItemDecoration這個(gè)類中可以看到這個(gè)分隔線是跟ListView一樣的,即系統(tǒng)的默認(rèn)的樣式,因此我們可以在styles的xml文件中進(jìn)行更改,更改如下:
<!-- Application theme. --> <style name ="AppTheme" parent="AppBaseTheme"> <!-- All customizations that are NOT specific to a particular API-level can go here. --> <item name= "android:listDivider">@drawable/divider </item > </style >
divider的內(nèi)容如下:
<?xml version="1.0" encoding= "utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" > <!-- 填充的顏色 --> <solid android:color ="@color/color_red"/> <!-- 線條大小 --> <size android:height ="1dp" android:width ="1dp"/> </shape>
修改之后運(yùn)行效果如下:
可以看到分隔線的顏色變了,當(dāng)然了這只是一個(gè)小例子,我們可以按照業(yè)務(wù)需求去更改,這樣就基本實(shí)現(xiàn)了ListView的效果,看到這肯定會(huì)有人說(shuō),好麻煩,還不如ListView簡(jiǎn)單呢,從上面的代碼量看來(lái)確實(shí)是使用起來(lái)很復(fù)雜,但是如果此時(shí)你想將這個(gè)列表以GridView的形式展示出來(lái),用RecylerView僅僅是換一行代碼的事情,
在上面的代碼中我們使用了
LinearLayoutManager layoutManager = new LinearLayoutManager(this); //設(shè)置布局管理器 recyclerView.setLayoutManager(layoutManager);
RecyclerView.LayoutManager是一個(gè)抽象類,系統(tǒng)為我們提供了三個(gè)實(shí)現(xiàn)類
①LinearLayoutManager即線性布局,這個(gè)是在上面的例子中我們用到的布局
②GridLayoutManager即表格布局
③StaggeredGridLayoutManager即流式布局,如瀑布流效果
假如將上述例子換成GridView的效果,那么相應(yīng)的代碼應(yīng)該這樣改
recyclerView .setLayoutManager(new GridLayoutManager( this,4));
除此之外上述的分隔線也要做相應(yīng)的更改,因?yàn)樵谏鲜鯠ividerItemDecoration這個(gè)方法中從
final int left = parent.getPaddingLeft(); final int right = parent.getWidth() - parent.getPaddingRight();
這兩行我們可以看出來(lái),它是繪制了一條線這條線就是從RecyclerView去掉左右邊距后,剩余的部分,因?yàn)楫?dāng)顯示成ListView時(shí)每一行就一個(gè)Item所以整體效果看上去就跟ListView差不多,而當(dāng)展示成GridView那樣的效果時(shí),每一行就不止一個(gè)條目了,而有可能是多個(gè),所以這個(gè)類就不再適用了,我們需要重新寫(xiě)一個(gè)。
package com.example.reclerviewpractice; 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.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.LayoutManager; import android.support.v7.widget.RecyclerView.State; import android.support.v7.widget.StaggeredGridLayoutManager; import android.view.View; /** * * @author zhy * */ public class DividerGridItemDecoration extends RecyclerView.ItemDecoration { private static final int[] ATTRS = new int[] { android.R.attr.listDivider }; private Drawable mDivider; public DividerGridItemDecoration(Context context) { final TypedArray a = context.obtainStyledAttributes(ATTRS ); mDivider = a.getDrawable(0); a.recycle(); } @Override public void onDraw(Canvas c, RecyclerView parent, State state) { drawHorizontal(c, parent); drawVertical(c, parent); } private int getSpanCount(RecyclerView parent) { // 列數(shù) int spanCount = -1; LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { spanCount = ((GridLayoutManager) layoutManager).getSpanCount(); } else if (layoutManager instanceof StaggeredGridLayoutManager) { spanCount = ((StaggeredGridLayoutManager) layoutManager) .getSpanCount(); } return spanCount; } public void drawHorizontal(Canvas c, RecyclerView parent) { 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() - params.leftMargin; final int right = child.getRight() + params.rightMargin + mDivider.getIntrinsicWidth(); final int top = child.getBottom() + params.bottomMargin; final int bottom = top + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } 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 top = child.getTop() - params.topMargin; final int bottom = child.getBottom() + params.bottomMargin; final int left = child.getRight() + params.rightMargin; final int right = left + mDivider.getIntrinsicWidth(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } private boolean isLastColum(RecyclerView parent, int pos, int spanCount, int childCount) { LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { if ((pos + 1) % spanCount == 0) // 如果是最后一列,則不需要繪制右邊 { return true; } } else if (layoutManager instanceof StaggeredGridLayoutManager) { int orientation = ((StaggeredGridLayoutManager) layoutManager) .getOrientation(); if (orientation == StaggeredGridLayoutManager.VERTICAL ) { if ((pos + 1) % spanCount == 0) // 如果是最后一列,則不需要繪制右邊 { return true; } } else { childCount = childCount - childCount % spanCount; if (pos >= childCount) // 如果是最后一列,則不需要繪制右邊 return true; } } return false; } private boolean isLastRaw(RecyclerView parent, int pos, int spanCount, int childCount) { LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { childCount = childCount - childCount % spanCount; if (pos >= childCount) // 如果是最后一行,則不需要繪制底部 return true; } else if (layoutManager instanceof StaggeredGridLayoutManager) { int orientation = ((StaggeredGridLayoutManager) layoutManager) .getOrientation(); // StaggeredGridLayoutManager 且縱向滾動(dòng) if (orientation == StaggeredGridLayoutManager.VERTICAL ) { childCount = childCount - childCount % spanCount; // 如果是最后一行,則不需要繪制底部 if (pos >= childCount) return true; } else // StaggeredGridLayoutManager 且橫向滾動(dòng) { // 如果是最后一行,則不需要繪制底部 if ((pos + 1) % spanCount == 0) { return true; } } } return false; } @Override public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { int spanCount = getSpanCount(parent); int childCount = parent.getAdapter().getItemCount(); if (isLastRaw(parent, itemPosition, spanCount, childCount))// 如果是最后一行,則不需要繪制底部 { outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); } else if (isLastColum(parent, itemPosition, spanCount, childCount))// 如果是最后一列,則不需要繪制右邊 { outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); } else { outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight()); } } }
別忘了更改分隔線recyclerView .addItemDecoration(new DividerGridItemDecoration(this ));之后運(yùn)行發(fā)現(xiàn)效果如下
可以看到如果你準(zhǔn)備好了分隔線的這個(gè)類,從ListView效果到GridView效果,只需要幾行代碼,是不是瞬間感覺(jué)高大上了?還有更讓人瞠目結(jié)舌的效果,將上述代碼做如下更改
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(4,StaggeredGridLayoutManager.HORIZONTAL); //設(shè)置布局管理器 recyclerView.setLayoutManager(layoutManager);
這里需要注意的是StaggeredGridLayoutManager構(gòu)造的第二個(gè)參數(shù)傳一個(gè)orientation,如果傳入的是StaggeredGridLayoutManager.VERTICAL那么前面那個(gè)參數(shù)就代表有多少列;如果傳是StaggeredGridLayoutManager.HORIZONTAL那么前面那個(gè)參數(shù)就代表有多少行
運(yùn)行效果如下
這效果是不是有點(diǎn)逆天?可以看到,固定為4行,變成了左右滑動(dòng)。有一點(diǎn)需要注意,如果是橫向的時(shí)候,item的寬度需要注意去設(shè)置,畢竟橫向的寬度沒(méi)有約束了,因?yàn)榭丶梢詸M向滾動(dòng)了,另外它還可以實(shí)現(xiàn)瀑布流的效果,關(guān)于瀑布流我準(zhǔn)備后面專門寫(xiě)一篇博客。
3.4 RecyclerView增加和刪除的動(dòng)畫(huà)(包括RecyclerView.Adapter中刷新的幾個(gè)方法的對(duì)比)
在上面也提到了控制RecyclerView增加和刪除的動(dòng)畫(huà)是通過(guò)ItemAnimator這個(gè)類來(lái)實(shí)現(xiàn)的,ItemAnimator這類也是個(gè)抽象的類,系統(tǒng)默認(rèn)給我們提供了一種增加和刪除的動(dòng)畫(huà),下面我們就來(lái)看看這種動(dòng)畫(huà)的效果,我們需要做的修改如下:
LinearLayoutManager layoutManager = new LinearLayoutManager(this); //設(shè)置布局管理器 recyclerView.setLayoutManager(layoutManager); //設(shè)置增加或刪除條目的動(dòng)畫(huà) recyclerView.setItemAnimator( new DefaultItemAnimator());
然后重寫(xiě)ActionBar的
@Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu. main, menu); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id. id_action_add: recycleAdapter.addData(1); break; case R.id. id_action_delete: recycleAdapter.removeData(1); break; } return true; }
關(guān)于R.menu. main中的main.xml這個(gè)文件代碼就不貼了,在最后的一個(gè)匯總的例子里會(huì)有
recyclerViewAdapter中增加的兩個(gè)方法:
public void addData( int position) { mDatas.add(position, "Insert One"); notifyItemInserted(position); notifyItemRangeChanged(position, mDatas.size()); } public void removeData( int position) { mDatas.remove(position); notifyItemRemoved(position); notifyItemRangeChanged(position, mDatas.size()); }
這里需要說(shuō)一下RecyclerView.Adapter中刷新數(shù)據(jù)的幾個(gè)方法,一共有這么幾個(gè)方法
public final void notifyDataSetChanged() public final void notifyItemChanged(int position) public final void notifyItemRangeChanged(int positionStart, int itemCount) public final void notifyItemInserted(int position) public final void notifyItemMoved(int fromPosition, int toPosition) public final void notifyItemRangeInserted(int positionStart, int itemCount) public final void notifyItemRemoved(int position) public final void notifyItemRangeRemoved(int positionStart, int itemCount)
notifyDataSetChanged()這個(gè)方法跟我們平時(shí)用到的ListView的Adapter的方法一樣,這里就不多做描述了。
notifyItemChanged(int position),當(dāng)position位置的數(shù)據(jù)發(fā)生了改變時(shí)就會(huì)調(diào)用這個(gè)方法,就會(huì)回調(diào)對(duì)應(yīng)position的onBindViewHolder()方法了,當(dāng)然,因?yàn)閂iewHolder是復(fù)用的,所以如果position在當(dāng)前屏幕以外,也就不會(huì)回調(diào)了,因?yàn)闆](méi)有意義,下次position滾動(dòng)會(huì)當(dāng)前屏幕以內(nèi)的時(shí)候同樣會(huì)調(diào)用onBindViewHolder()方法刷新數(shù)據(jù)了。其他的方法也是同樣的道理。public final void notifyItemRangeChanged(int positionStart, int itemCount),顧名思義,可以刷新從positionStart開(kāi)始itemCount數(shù)量的item了(這里的刷新指回調(diào)onBindViewHolder()方法)。
public final void notifyItemInserted(int position),這個(gè)方法是在第position位置被插入了一條數(shù)據(jù)的時(shí)候可以使用這個(gè)方法刷新,注意這個(gè)方法調(diào)用后會(huì)有插入的動(dòng)畫(huà),這個(gè)動(dòng)畫(huà)可以使用默認(rèn)的,也可以自己定義。
public final void notifyItemMoved(int fromPosition, int toPosition),這個(gè)方法是從fromPosition移動(dòng)到toPosition為止的時(shí)候可以使用這個(gè)方法刷新
public final void notifyItemRangeInserted(int positionStart, int itemCount),顯然是批量添加。
public final void notifyItemRemoved(int position),第position個(gè)被刪除的時(shí)候刷新,同樣會(huì)有動(dòng)畫(huà)。
將上述更改運(yùn)行,點(diǎn)擊添加和刪除按鈕效果圖如下:
public final void notifyItemRangeRemoved(int positionStart, int itemCount),批量刪除。
可以看到系統(tǒng)給提供的動(dòng)畫(huà)效果還不錯(cuò),當(dāng)然我們也可以按照業(yè)務(wù)需求去自己定義動(dòng)畫(huà)效果。
3.5 給RecyclerView的Item添加點(diǎn)擊事件
到這里還有一點(diǎn)從文章開(kāi)頭到現(xiàn)在我們都沒(méi)有提及,就是Item的點(diǎn)擊事件RecyclerView監(jiān)聽(tīng)事件處理在ListView使用的時(shí)候,該控件給我們提供一個(gè)onItemClickListener監(jiān)聽(tīng)器,這樣當(dāng)我們點(diǎn)擊Item的時(shí)候,會(huì)回調(diào)相關(guān)的方法,以便我們方便處理Item點(diǎn)擊事件。對(duì)于RecyclerView來(lái)講,非??上У氖牵摽丶](méi)有給我們提供這樣的內(nèi)置監(jiān)聽(tīng)器方法,不過(guò)我們可以進(jìn)行改造實(shí)現(xiàn),可以這樣實(shí)現(xiàn)Item的點(diǎn)擊事件的監(jiān)聽(tīng),在我們的adapter中增加這兩個(gè)方法
public interface OnItemClickListener{ void onClick( int position); void onLongClick( int position); } public void setOnItemClickListener(OnItemClickListener onItemClickListener ){ this. mOnItemClickListener=onItemClickListener; }
然后onBindViewHolder方法要做如下更改
@Override public void onBindViewHolder(MyViewHolder holder, final int position) { holder. tv.setText( mDatas.get(position)); if( mOnItemClickListener!= null){ holder. itemView.setOnClickListener( new OnClickListener() { @Override public void onClick(View v) { mOnItemClickListener.onClick(position); } }); holder. itemView.setOnLongClickListener( new OnLongClickListener() { @Override public boolean onLongClick(View v) { mOnItemClickListener.onLongClick(position); return false; } }); } }<span style="color:#333333;"> </span>
在MainAcitivity中增加
recycleAdapter.setOnItemClickListener(new OnItemClickListener() { @Override public void onLongClick(int position) { Toast.makeText(MainActivity.this,"onLongClick事件 您點(diǎn)擊了第:"+position+"個(gè)Item",0).show(); } @Override public void onClick(int position) { Toast.makeText(MainActivity.this,"onClick事件 您點(diǎn)擊了第:"+position+"個(gè)Item",0).show(); } });
然后運(yùn)行,效果如下:
可以看到Item的onClick和onLongClick事件都觸發(fā)了。到此關(guān)于RecyclerView的基本用法就介紹的差不多了,當(dāng)然,還有幾個(gè)點(diǎn)沒(méi)有提到,比方說(shuō)瀑布流、下拉刷新、上拉加載等,由于篇幅原因這些在后面的更新中都會(huì)給大家呈現(xiàn)。最后將本篇博客所提到的知識(shí)點(diǎn)做一個(gè)整合,寫(xiě)到Demo中,大家可以參考,Demo進(jìn)行學(xué)習(xí)
源碼下載:demo
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android RecyclerView 上拉加載更多及下拉刷新功能的實(shí)現(xiàn)方法
- Android RecyclerView加載不同布局簡(jiǎn)單實(shí)現(xiàn)
- Android中RecyclerView實(shí)現(xiàn)橫向滑動(dòng)代碼
- Android中RecyclerView點(diǎn)擊Item設(shè)置事件
- Android中RecyclerView布局代替GridView實(shí)現(xiàn)類似支付寶的界面
- Android RecyclerView添加頭部和底部的方法
- Android RecyclerView實(shí)現(xiàn)下拉刷新和上拉加載
- Android中使用RecyclerView實(shí)現(xiàn)下拉刷新和上拉加載
- Android項(xiàng)目實(shí)戰(zhàn)之仿網(wǎng)易新聞的頁(yè)面(RecyclerView )
- Android RecyclerView下拉刷新和上拉加載更多
- Android基于RecyclerView實(shí)現(xiàn)高亮搜索列表
相關(guān)文章
Kotlin Service實(shí)現(xiàn)消息推送通知過(guò)程
這幾天分析了一下的啟動(dòng)過(guò)程,于是乎,今天寫(xiě)一下Service使用; 給我的感覺(jué)是它并不復(fù)雜,千萬(wàn)不要被一坨一坨的代碼嚇住了,雖然彎彎繞繞不少,重載函數(shù)一個(gè)接著一個(gè),就向走迷宮一樣,但只要抓住主線閱讀,很快就能找到出口2022-12-12Android采用消息推送實(shí)現(xiàn)類似微信視頻接聽(tīng)
這篇文章主要為大家詳細(xì)介紹了Android采用消息推送實(shí)現(xiàn)類似微信視頻接聽(tīng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11Android 照片選擇區(qū)域功能實(shí)現(xiàn)示例
這篇文章主要介紹了Android 照片選擇區(qū)域功能實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04Android通過(guò)aapt命令獲取apk詳細(xì)信息(包括:文件包名,版本號(hào),SDK等信息)
本文給大家分享android通過(guò)aapt命令獲取apk詳細(xì)信息(包括:文件包名,版本號(hào),SDK等信息),非常不錯(cuò),簡(jiǎn)單實(shí)用,對(duì)android sdk aapt知識(shí)感興趣的朋友一起通過(guò)本文學(xué)習(xí)吧2016-11-11詳解Android中Intent傳遞對(duì)象給Activity的方法
這篇文章主要介紹了Android中Intent傳遞對(duì)象給Activity的方法,文章中對(duì)Activity的生命周期等知識(shí)先作了簡(jiǎn)要的介紹,需要的朋友可以參考下2016-04-04Android ImageButton自定義按鈕的按下效果的代碼實(shí)現(xiàn)方法分享
這篇文章主要介紹了Android ImageButton自定義按鈕的按下效果的代碼實(shí)現(xiàn)方法,需要的朋友可以參考下2014-02-02Android?自定義?Dialog?實(shí)現(xiàn)列表?單選、多選、搜索功能
Android開(kāi)發(fā)經(jīng)常需要用到對(duì)話框來(lái)進(jìn)行信息的篩選和搜索,本文詳細(xì)介紹了如何使用自定義Dialog結(jié)合RecyclerView和搜索框?qū)崿F(xiàn)這一功能,通過(guò)Builder模式構(gòu)建復(fù)雜的Dialog對(duì)象,使得代碼更加靈活和易于維護(hù),文中提供了詳細(xì)的步驟和代碼注釋2024-10-10Android跳轉(zhuǎn)系統(tǒng)設(shè)置Settings的各個(gè)界面詳解
系統(tǒng)設(shè)置Settings中定義的一些常用的各界面ACTION常量,下面這篇文章主要給大家介紹了關(guān)于Android跳轉(zhuǎn)系統(tǒng)設(shè)置Settings的各個(gè)界面,文中介紹非常詳細(xì),需要的朋友可以參考下2023-01-01