特別聲明:
一、前言
- 話說RecyclerView已經(jīng)面市很久邢滑,也在很多應(yīng)用中得到廣泛的使用带到,在整個(gè)開發(fā)者圈子里面也擁有很不錯(cuò)的口碑压固,那說明RecyclerView擁有比ListView,GridView之類控件有很多的優(yōu)點(diǎn)逗威,例如:數(shù)據(jù)綁定,Item View創(chuàng)建,View的回收以及重用等機(jī)制愕难。那么今天開始我們來重點(diǎn)學(xué)習(xí)一下RecyclerView控件哈垢,本系列文章會包括到以下三個(gè)部分:
1. RecyclerView控件的基本使用,包括基礎(chǔ),進(jìn)階,高級部分,動(dòng)畫之類
2. RecyclerView控件的實(shí)戰(zhàn)實(shí)例
3. RecyclerView控件集合AA(Android Annotations)注入框架實(shí)例
- 今天使我們本系列文章的第二講主要是我們通過RecyclerView來打造一個(gè)新版類似Gallery控件的效果妻柒。本次講解所有用的Demo例子已經(jīng)全部更新到下面的項(xiàng)目中了,歡迎大家star和fork耘分。
FastDev4Android框架項(xiàng)目地址:https://github.com/jiangqqlmj/FastDev4Android
二举塔、基本實(shí)現(xiàn)
上一講我們已經(jīng)對于RecyclerView的基本使用和進(jìn)階部分做了講解(點(diǎn)擊進(jìn)入),下面我們一步步的來打造一個(gè)新版Gallery效果控件。
先來看一下和RecyclerView相關(guān)類:
類名 | 說明 |
---|---|
RecyclerView.Adapter | 可以托管數(shù)據(jù)集合求泰,為每一項(xiàng)Item創(chuàng)建視圖并且綁定數(shù)據(jù) |
RecyclerView.ViewHolder | 承載Item視圖的子布局 |
RecyclerView.LayoutManager | 負(fù)責(zé)Item視圖的布局的顯示管理 |
RecyclerView.ItemDecoration | 給每一項(xiàng)Item視圖添加子View,可以進(jìn)行畫分隔線之類的東西 |
RecyclerView.ItemAnimator | 負(fù)責(zé)處理數(shù)據(jù)添加或者刪除時(shí)候的動(dòng)畫效果 |
- 那如果要實(shí)現(xiàn)Gallery的效果央渣,里面的Item是橫向滑動(dòng)的,也就是說我們的RecyclerView可以支持橫向滑動(dòng)渴频,這邊我們直接采用了LinearLayoutManager布局管理器芽丹,同時(shí)設(shè)置方向?yàn)?HORIZONTAL(水平)
下面來具體看代碼:
1、作為RecyclerView控件卜朗,我們需要設(shè)置每一項(xiàng)Item的布局:
<?xmlversionxmlversion="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:androidLinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:gravity="center"
android:padding="8.0dip">
<ImageView
android:id="@+id/item_img"
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="fitXY"
android:adjustViewBounds="true"
android:src="@drawable/ic_item_gallery"/>
<TextView
android:id="@+id/item_tv"
android:text="標(biāo)題1"
android:layout_marginTop="5dp"
android:textSize="15sp"
android:layout_gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
- 這個(gè)布局中我們比較簡單拔第,定義了一個(gè)圖片和一個(gè)標(biāo)題,垂直方向布局场钉。
2蚊俺、間接著,和ListView寫法差不多逛万,需要自定義適配器泳猬,來創(chuàng)建每一項(xiàng)布局視圖以及把數(shù)據(jù)和視圖綁定起來,所以這邊繼承RecyclerView.Adapter類創(chuàng)建一個(gè)自定義適配器GalleryRecyclerAdapter.java
- 那么需要實(shí)現(xiàn)基類中的三個(gè)方法:
- onCreateViewHolder(ViewGroup parent,int viewType) :創(chuàng)建Item View然后通過ViewHolder來承載
onBindViewHolder(ViewHolder holder,int position):進(jìn)行視圖和數(shù)據(jù)綁定
getItemCount():獲取列表中視圖Item的數(shù)量
具體GallerRecyclerAdapter實(shí)現(xiàn)代碼如下:
public class GalleryRecyclerAdapter extends RecyclerView.Adapter<GalleryRecyclerAdapter.ViewHolder> {
private List<GalleryModel> models;
private LayoutInflater mInflater;
public GalleryRecyclerAdapter(Context context){
models=new ArrayList<GalleryModel>();
for (int i=0;i<20;i++){
int index=i+1;
models.add(new GalleryModel(R.drawable.ic_item_gallery,"Item"+index));
}
mInflater=LayoutInflater.from(context);
}
/**
* 創(chuàng)建Item View 然后使用ViewHolder來進(jìn)行承載
* @param parent
* @param viewType
* @return
*/
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view=mInflater.inflate(R.layout.item_gallery_recycler,parent,false);
ViewHolder viewHolder=new ViewHolder(view);
return viewHolder;
}
/**
* 進(jìn)行綁定數(shù)據(jù)
* @param holder
* @param position
*/
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.item_img.setImageResource(models.get(position).getImgurl());
holder.item_tv.setText(models.get(position).getTitle());
}
@Override
public int getItemCount() {
return models.size();
}
//自定義的ViewHolder,持有每個(gè)Item的的所有界面元素
public static class ViewHolder extends RecyclerView.ViewHolder {
private ImageView item_img;
private TextView item_tv;
public ViewHolder(View view){
super(view);
item_img=(ImageView)view.findViewById(R.id.item_img);
item_tv=(TextView)view.findViewById(R.id.item_tv);
}
}
}
3得封、注意看上面的代碼埋心,我們繼承了RecyclerView.ViewHolder實(shí)現(xiàn)一個(gè)自定義類ViewHolder,這個(gè)用來承載我們的子Item視圖呛每,現(xiàn)在Google已經(jīng)要求開發(fā)者必須要使用ViewHolder了踩窖。在ViewHolder中我們進(jìn)行控件的初始化工作坡氯,然后保存View視圖晨横。
//自定義的ViewHolder,持有每個(gè)Item的的所有界面元素
public static class ViewHolder extends RecyclerView.ViewHolder {
private ImageView item_img;
private TextView item_tv;
public ViewHolder(View view){
super(view);
item_img=(ImageView)view.findViewById(R.id.item_img);
item_tv=(TextView)view.findViewById(R.id.item_tv);
}
}
4箫柳、最后在Activity中控件設(shè)置,例如布局管理器,Adapter綁定即可,完整代碼如下:
public class RecyclerGalleryActivity extends BaseActivity {
private RecyclerView gallery_recycler;
@Override
protected void onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.recycler_gallery_layout);
//初始化RecyclerView控件
gallery_recycler=(RecyclerView)this.findViewById(R.id.gallery_recycler);
//固定高度
gallery_recycler.setHasFixedSize(true);
//創(chuàng)建布局管理器
LinearLayoutManager linearLayoutManager=new LinearLayoutManager(this);
//設(shè)置橫向
linearLayoutManager.setOrientation(OrientationHelper.HORIZONTAL);
//設(shè)置布局管理器
gallery_recycler.setLayoutManager(linearLayoutManager);
//創(chuàng)建適配器
GalleryRecyclerAdapter adapter=new GalleryRecyclerAdapter(this);
//綁定適配器
gallery_recycler.setAdapter(adapter);
}
class CustomOnClickListener implements View.OnClickListener{
@Override
public void onClick(View v) {
RecyclerGalleryActivity.this.finish();
}
}
}
5手形、在看運(yùn)行效果之前,我們先來看下上面的代碼悯恍,上面的代碼基本注釋已經(jīng)全部加了库糠,相應(yīng)大家可以看的懂,不過我們需要來講一下上面的LayoutManager(布局管理器)涮毫。
- 在上一講中我們也講到了瞬欧,LayoutManger(布局管理器)該類負(fù)責(zé)將每一個(gè)Item視圖在RecyclerView中的布局。目前RecyclerView已經(jīng)給我們提供三個(gè)內(nèi)置管理器:
LinearLayoutManger
GridLayoutManger
StaggeredGridLayoutManager
- 這邊的例子中我們是采用LinearLayoutManger而且設(shè)置了橫向水平布局了罢防。當(dāng)然LinearLayoutManger還給我們提供了以下幾個(gè)方法來讓開發(fā)者方便的獲取到屏幕上面的頂部item和頂部item相關(guān)的信息:
1艘虎、findFirstVisibleItemPosition()
2、findFirstCompletlyVisibleItemPosition()
3咒吐、findLastVisibleItemPosition()
4野建、findLastCompletlyVisibleItemPosition()
- 這邊的具體設(shè)置代碼如下:
//創(chuàng)建布局管理器
LinearLayoutManagerlinearLayoutManager=new LinearLayoutManager(this);
//設(shè)置橫向
inearLayoutManager.setOrientation(OrientationHelper.HORIZONTAL);
//設(shè)置布局管理器
allery_recycler.setLayoutManager(linearLayoutManager);
6、初步運(yùn)行效果如下:
三恬叹、升級加入點(diǎn)擊事件
通過上面的方式我們顯示了一個(gè)類似于Gallery的效果候生,但是還遠(yuǎn)遠(yuǎn)不如實(shí)際Gallery的效果,現(xiàn)在只是可以有多項(xiàng)Item以及可以左右滑動(dòng)绽昼,但是沒有點(diǎn)擊事件唯鸭,下面我們來加入點(diǎn)擊事件操作。
對于ListView來講硅确,我們可以為ListView加入setOnItemClickListener監(jiān)聽事件目溉,但是對于RecyclerView控件來講,RecyclerView已經(jīng)不再負(fù)載Item視圖的布局和顯示疏魏,這些工作已經(jīng)交給了LayoutManger來做了停做。所以RecyclerView也沒有給我們提供類似onItemClick事件,這樣如果非得要實(shí)現(xiàn)類似的功能大莫,我們開發(fā)者也可以自定義模擬實(shí)現(xiàn)蛉腌。來,我們繼續(xù)往下看….
1、我們最終要實(shí)現(xiàn)點(diǎn)擊列表上面每一項(xiàng)Item來回調(diào)點(diǎn)擊方法烙丛,那么我們可以在Adapter中的每一項(xiàng)View上面做文章舅巷,首先我們來看一下Adapter中的onCreateViewHolder()方法:
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Viewview=mInflater.inflate(R.layout.item_gallery_recycler,parent,false);
ViewHolder viewHolder=new ViewHolder(view);
return viewHolder;
}
2、該方法創(chuàng)建出了Item 視圖,然后通過ViewHolder來進(jìn)行承載了河咽,既然這樣那我們可以在View加載出來之后給它設(shè)置一些屬性例如:顏色钠右,大小,當(dāng)然也可以是點(diǎn)擊事件等等忘蟹。那這邊我們給View添加onClick事件飒房,然后在onClick方法把View點(diǎn)擊觸發(fā)的事件回調(diào)出去,同時(shí)可以回調(diào)一些參數(shù)內(nèi)容出去。OK媚值,那么我們這邊就需要一個(gè)自定義的接口了狠毯,我們創(chuàng)建一個(gè)GallerRecyclerAdapter的內(nèi)部類接口:
(注意:內(nèi)部類接口)
/**
* 類似ListView的 onItemClickListener接口
*/
public interface OnRecyclerViewItemClickListener{
/**
* Item View發(fā)生點(diǎn)擊回調(diào)的方法
* @param view 點(diǎn)擊的View
* @paramposition 具體Item View的索引
*/
void onItemClick(View view,intposition);
}
3、然后定義接口褥芒,同時(shí)提供set和get方法嚼松,來讓外部傳入該接口,初始化:
private OnRecyclerViewItemClickListener onRecyclerViewItemClickListener;
public OnRecyclerViewItemClickListener getOnRecyclerViewItemClickListener() {
return onRecyclerViewItemClickListener;
}
public void setOnRecyclerViewItemClickListener(OnRecyclerViewItemClickListener onRecyclerViewItemClickListener) {
this.onRecyclerViewItemClickListener =onRecyclerViewItemClickListener;
}
4锰扶、現(xiàn)在開始在onCreateViewHolder()方法中給View添加一個(gè)onClick事件献酗,然后相應(yīng)處理,判斷onRecyclerViewItemClickListener是否存在坷牛,把事件回調(diào)出去:
view.setOnClickListener(newView.OnClickListener() {
@Override
public void onClick(View v) {
if(onRecyclerViewItemClickListener!=null){
onRecyclerViewItemClickListener.onItemClick(view,(int)view.getTag());
}
}
});
5罕偎、上面的代碼中大家可能注意到onItemClick()方法中的第二個(gè)參數(shù),獲取了tag漓帅,因?yàn)檫@邊的position索引值是在onBindViewHolder()方法中設(shè)置的:
public void onBindViewHolder(ViewHolder holder, int position) {
holder.item_img.setImageResource(models.get(position).getImgurl());
holder.item_tv.setText(models.get(position).getTitle());
holder.itemView.setTag(position);
}
6锨亏、OK這邊我們搞定了一個(gè)Item點(diǎn)擊監(jiān)聽方法,接下去就是使用了:
adapter.setOnRecyclerViewItemClickListener(new GalleryRecyclerAdapter.OnRecyclerViewItemClickListener() {
@Override
public void onItemClick(View view,int position) {
Toast.makeText(RecyclerGalleryActivity.this,"您點(diǎn)擊的Item的索引為:"+position,Toast.LENGTH_SHORT).show();
}
});
7忙干、現(xiàn)在該功能代碼整完了器予,運(yùn)行效果如下:
四、升級之加入分割線
上面我們已經(jīng)給每一項(xiàng)Item加入了點(diǎn)擊回調(diào)事件,但是總感覺還缺少點(diǎn)什么東西,例如分隔線。很遺憾的是乾蓬,RecyclerView沒有提供ListView控件這樣設(shè)置分割線的方法,不過它給我們提供了ItemDecoration類反浓。這個(gè)ItemDecoration可以使得每一個(gè)Item在視覺上面進(jìn)行分隔開來。RecyclerView沒有要求ItemDecoration必須要設(shè)置赞哗,同樣作為開發(fā)者可以選擇不設(shè)置或者設(shè)置多個(gè)Decoration雷则。然后RecyclerView會進(jìn)行相應(yīng)的繪制。
我們這邊定義了一個(gè)TestDecoration類肪笋,該類繼承自RecyclerView.Decoration月劈。只需要實(shí)現(xiàn)一下的兩個(gè)方法即可:
onDraw(Canvas c,RecyclerView parent,RecyclerView.State state)
getItemOffset(Rect outRect,int itemPosition,RecyclerView parent)
- 具體實(shí)現(xiàn)代碼如下:
public class TestDecoration extends RecyclerView.ItemDecoration {
//采用系統(tǒng)內(nèi)置的風(fēng)格的分割線
private static final int[] attrs=newint[]{android.R.attr.listDivider};
private Drawable mDivider;
public TestDecoration(Context context) {
TypedArray typedArray=context.obtainStyledAttributes(attrs);
mDivider=typedArray.getDrawable(0);
typedArray.recycle();
}
/**
* 進(jìn)行自定義繪制
* @param c
* @param parent
* @param state
*/
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
int top=parent.getPaddingTop();
int bottom=parent.getHeight()-parent.getPaddingBottom();
int childCount=parent.getChildCount();
for(int i=0;i<childCount;i++){
View child=parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams=(RecyclerView.LayoutParams)child.getLayoutParams();
intleft=child.getRight()+layoutParams.rightMargin;
intright=left+mDivider.getIntrinsicWidth();
mDivider.setBounds(left,top,right,bottom);
mDivider.draw(c);
}
}
@Override
public void getItemOffsets(Rect outRect,View view, RecyclerView parent, RecyclerView.State state) {
outRect.set(0,0,mDivider.getIntrinsicWidth(),0);
}
}
- 最后給RecyclerView添加該分隔線即可:
//設(shè)置分割線
gallery_recycler.addItemDecoration(new TestDecoration(this));
-
運(yùn)行效果大致如下:
五度迂、升級之分割線改造
仔細(xì)看上面的運(yùn)行效果,我們會發(fā)現(xiàn)一個(gè)問題猜揪,那就是分割線垂直分布惭墓,但是沒有自適應(yīng)控件的高度,直接延伸到界面的底部了而姐。重新檢查了有關(guān)的所有布局文件發(fā)現(xiàn)腊凶,高度都設(shè)置成了warp_content,但是實(shí)際的效果還是沒有自適應(yīng)拴念。原來在哪里呢钧萍?
真正的原因是因?yàn)镽ecyclerView控件已經(jīng)不負(fù)責(zé)每一項(xiàng)VIew的顯示了,那我們來看LayoutManger(布局管理器)該進(jìn)行負(fù)責(zé)Item的布局顯示了丈莺,所以我們需要進(jìn)行實(shí)現(xiàn)一個(gè)LayoutManger划煮,然后重寫里邊的onMeasure()方法。計(jì)算高度即可缔俄,具體代碼如下:
public class CustomLinearLayoutManager extends LinearLayoutManager {
public CustomLinearLayoutManager(Context context) {
super(context);
}
@Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
Viewview=recycler.getViewForPosition(0);
if(view!=null){
measureChild(view,widthSpec,heightSpec);
int mWidth=View.MeasureSpec.getSize(widthSpec);
intmHeight=view.getMeasuredHeight();
setMeasuredDimension(mWidth,mHeight);
}
}
}
-
然后RecyclerView使用CustomLinearLayoutManger即可,運(yùn)行效果如下:
六、升級之添加刪除Item動(dòng)畫
- RecyclerView控件的一個(gè)優(yōu)美之處就是當(dāng)里邊Item發(fā)生變化的時(shí)候可以加入相應(yīng)的動(dòng)畫效果器躏,涉及的類為RecyclerView.ItemAnimatior俐载。一般當(dāng)存在以下三種操作的時(shí)候可以加入動(dòng)畫效果:
Item 刪除
Item 添加
Item 移動(dòng)
- 當(dāng)我們的數(shù)據(jù)變化,或者移動(dòng)的時(shí)候登失,用Adapter給我們提供的以下兩個(gè)方法即可:
notifyItemInserted(int position)
notifyItemRemoved(int position)
- 那我們可以在Adapter中加入兩個(gè)方法遏佣,分別為添加Item和刪除Item的方法:
//添加數(shù)據(jù)
public void addItem(GalleryModel model, int position) {
models.add(position, model);
notifyItemInserted(position);
}
//刪除數(shù)據(jù)
public void removeItem(int position) {
models.remove(position);
notifyItemRemoved(position);
}
- 然后在外部進(jìn)行調(diào)用這兩個(gè)方法:
//添加數(shù)據(jù)
adapter.addItem(new GalleryModel(R.drawable.ic_item_gallery,"Item Add"),3);
//移除數(shù)據(jù)
dapter.removeItem(2);
- 最后千萬不要忘記給RecyclerView設(shè)置動(dòng)畫效果,我這邊就直接采用默認(rèn)動(dòng)畫了揽浙。
//設(shè)置動(dòng)畫
gallery_recycler.setItemAnimator(new DefaultItemAnimator());
-
最終運(yùn)行效果如下:
七状婶、最后總結(jié)
今天通過實(shí)例帶大家又重新把RecyclerView的相關(guān)使用講解了一遍,實(shí)現(xiàn)類似Gallery效果馅巷,當(dāng)然實(shí)例中還有很多缺點(diǎn)膛虫,需要進(jìn)一步優(yōu)化,后面的文章中也會繼續(xù)更新的~
本次具體實(shí)例注釋過的全部代碼已經(jīng)上傳到FastDev4Android項(xiàng)目中了钓猬。同時(shí)歡迎大家去Github站點(diǎn)進(jìn)行clone或者下載瀏覽:https://github.com/jiangqqlmj/FastDev4Android 同時(shí)歡迎大家star和fork整個(gè)開源快速開發(fā)框架項(xiàng)目~下一講我們會進(jìn)行RecyclerView集合AA(Android Annotations)注入框架來實(shí)現(xiàn)實(shí)例稍刀,敬請期待!
再次聲明:本文轉(zhuǎn)載自【江清清的博客】http://blog.csdn.net/developer_jiangqq/article/details/49946589