轉載請註明出處:
http://blog.csdn.net/u014702332/article/details/53785136
本文出版[掃地僧的博客]
基礎部分
引入support包
要使用RecyclerView首先需要引入support包:compile ‘com.android.support:recyclerview-v7:25.1.0’
首先查看效果圖,依次是線性線性垂直佈局,水平佈局,網絡佈局,瀑布流佈局
在佈局文件中添加這個文件
然後把這個控件添加到佈局文件中
<?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:id="@+id/activity_my_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="king.com.recyler.RecyclerhorizontalActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</RelativeLayout>
代碼中使用並聲明這個控件
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
線性佈局
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); //水平佈局,可以左右滑動
layoutManager.setOrientation(LinearLayoutManager.VERTICAL); //垂直佈局,可以上下滑動,類似於ListView
//網絡佈局,
GridLayoutManager layoutManager = new GridLayoutManager(this,5); //後面這個參數,表示網格有多少列,當前表示5列
//瀑布流佈局
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL); // 前一個參數表示有多少列,後一個參數表示瀑布流的方式;我這裏表示有3列,瀑布流方向是垂直方向
recyclerView.setLayoutManager(layoutManager);
FruitAdapter adapter = new FruitAdapter(FruitBean.getFruitBeans(),R.layout.fruit_item_horizontal);
recyclerView.setAdapter(adapter);
構造數據
public class FruitBean{
public int id;
public String name;
public int resourceId;
/**
* 創建數據
* @return
*/
public static List<FruitBean> getFruitBeans(){
List<FruitBean> fruitBeanList = new ArrayList<>();
for(int i = 0;i<100;i++){
FruitBean bean = new FruitBean();
bean.id = i;
switch (i%10){
case 0:
bean.name = "apple";
bean.resourceId = R.drawable.apple_pic;
break;
case 1:
bean.name = "banana";
bean.resourceId = R.drawable.banana_pic;
break;
case 2:
bean.name = "cherry";
bean.resourceId = R.drawable.cherry_pic;
break;
case 3:
bean.name = "grape";
bean.resourceId = R.drawable.grape_pic;
break;
case 4:
bean.name = "mango";
bean.resourceId = R.drawable.mango_pic;
break;
case 5:
bean.name = "orange";
bean.resourceId = R.drawable.orange_pic;
break;
case 6:
bean.name = "pear";
bean.resourceId = R.drawable.pear_pic;
break;
case 7:
bean.name = "pineapple";
bean.resourceId = R.drawable.pineapple_pic;
break;
case 8:
bean.name = "strawberry";
bean.resourceId = R.drawable.strawberry_pic;
break;
case 9:
bean.name = "watermelon";
bean.resourceId = R.drawable.watermelon_pic;
break;
default:
bean.name = "apple";
bean.resourceId = R.drawable.apple_pic;
break;
}
fruitBeanList.add(bean);
}
return fruitBeanList;
}
/**
* 創建數據,流式佈局需要使用的。
* @return
*/
public static List<FruitBean> getStaggeredFruitBeans(){
List<FruitBean> fruitBeanList = new ArrayList<>();
for(int i = 0;i<100;i++){
FruitBean bean = new FruitBean();
bean.id = i;
switch (i%10){
case 0:
bean.name = "apple";
bean.resourceId = R.drawable.apple_pic;
break;
case 1:
bean.name = "banana";
bean.resourceId = R.drawable.banana_pic;
break;
case 2:
bean.name = "cherry";
bean.resourceId = R.drawable.cherry_pic;
break;
case 3:
bean.name = "grape";
bean.resourceId = R.drawable.grape_pic;
break;
case 4:
bean.name = "mango";
bean.resourceId = R.drawable.mango_pic;
break;
case 5:
bean.name = "orange";
bean.resourceId = R.drawable.orange_pic;
break;
case 6:
bean.name = "pear";
bean.resourceId = R.drawable.pear_pic;
break;
case 7:
bean.name = "pineapple";
bean.resourceId = R.drawable.pineapple_pic;
break;
case 8:
bean.name = "strawberry";
bean.resourceId = R.drawable.strawberry_pic;
break;
case 9:
bean.name = "watermelon";
bean.resourceId = R.drawable.watermelon_pic;
break;
default:
bean.name = "apple";
bean.resourceId = R.drawable.apple_pic;
break;
}
//這裏處理了一下名字長度,區別流式佈局
Random random = new Random();
int length = random.nextInt(10)+1;
StringBuilder sb = new StringBuilder();
for (int j=0;j<length;j++){
sb.append(bean.name);
}
bean.name = sb.toString();
fruitBeanList.add(bean);
}
return fruitBeanList;
}
創建數據適配器
public class FruitAdapter extends RecyclerView.Adapter {
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return null;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
}
@Override
public int getItemCount() {
return 0;
}
}
上面這些代碼其實很好理解,創建一個FruitAdapter 繼承自RecyclerView.Adapter,這個時候會提示我們需要實現三個方法:
- onCreateViewVolder看名字知道要創建一個ViewHolder,然後並返回
- onBindViewHolder 給ViewHolder 綁定數據
- getItemCount不需要做任何解釋。
接下來我們需要顯示ViewHolder中的控件
/**
* ViewHolder用於存儲列表項中顯示的控件,
*/
static class FruitViewHolder extends RecyclerView.ViewHolder{
ImageView ivImg;
TextView tvNo,tvName;
/**
* itemView 就是控件外層佈局,
* @param itemView
*/
public FruitViewHolder(View itemView) {
super(itemView);
ivImg = (ImageView) itemView.findViewById(R.id.iv_img);
tvNo = (TextView) itemView.findViewById(R.id.tv_no);
tvName = (TextView) itemView.findViewById(R.id.tv_name);
}
}
創建了一個FruitViewHolder 繼承自 RecyclerView.ViewHolder 然後這裏會提示需要實現一個構造方法。而構造方法中的View 正是我們要顯示控件的根佈局,
這裏我們就可以通過根佈局來創建相應的控件。
我們之前創建的FruiAdapter 中都是用RecyclerView.ViewHolder 這是所有ViewHolder的基類,如果我們需要綁定FruiBean的數據類型,就必須給FruitAdapter中ViewHolder 指定ViewHolder類型,所以我們的代碼更改後變成如下:
package king.com.recyler;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
/**
* Created by liuking on 16/12/20.
* 創建Adapter
*/
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.FruitViewHolder> {
private List<FruitBean> fruitBeanList;
private int mLayoutId;
public FruitAdapter(List<FruitBean> list,int layoutId){
this.fruitBeanList = list;
this.mLayoutId = layoutId;
}
/**
* 構建一個ViewHolder佈局
* @param parent
* @param viewType
* @return
*/
@Override
public FruitAdapter.FruitViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(mLayoutId,parent,false);
FruitViewHolder holder = new FruitViewHolder(view); //創建一個View對象
return holder;
}
/**
* 綁定數據
* @param holder
* @param position
*/
@Override
public void onBindViewHolder(FruitViewHolder holder, int position) {
final FruitBean bean = fruitBeanList.get(position);
holder.ivImg.setImageResource(bean.resourceId);
holder.tvName.setText(bean.name);
holder.tvNo.setText("編號:"+bean.id);
}
@Override
public int getItemCount() {
return fruitBeanList.size();
}
/**
* ViewHolder用於存儲列表項中顯示的控件,
*/
static class FruitViewHolder extends RecyclerView.ViewHolder{
ImageView ivImg;
TextView tvNo,tvName;
/**
* itemView 就是控件外層佈局,
* @param itemView
*/
public FruitViewHolder(View itemView) {
super(itemView);
ivImg = (ImageView) itemView.findViewById(R.id.iv_img);
tvNo = (TextView) itemView.findViewById(R.id.tv_no);
tvName = (TextView) itemView.findViewById(R.id.tv_name);
}
}
}
設置點擊事件
至此基礎工作已經全部完成了,但我們會發現有些問題。所有的item 都不能點擊,這是爲什麼呢,像ListView每個item都可以點擊,爲什麼RecyclerView就不能點擊? 其實Listview 的設計並不是很完美,如果我想點擊ListView中的某個item中的button呢,雖然可以 實現,但相對來說是比較麻煩的,而RecyclerView就直接放棄了這樣做法,讓開發者自己去設定控件每一個點擊事件,輕鬆實現點擊:
/**
* 構建一個ViewHolder佈局
* @param parent
* @param viewType
* @return
*/
@Override
public FruitAdapter.FruitViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(mLayoutId,parent,false);
final FruitViewHolder holder = new FruitViewHolder(view); //創建一個View對象
holder.mItemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int position = holder.getAdapterPosition();
FruitBean fb = fruitBeanList.get(position);
Toast.makeText(parent.getContext(), "click item"+position, Toast.LENGTH_SHORT).show();
}
});
holder.ivImg.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int position = holder.getAdapterPosition();
FruitBean fb = fruitBeanList.get(position);
Toast.makeText(parent.getContext(), "click item"+fb.name, Toast.LENGTH_SHORT).show();
}
});
return holder;
}
/**
* ViewHolder用於存儲列表項中顯示的控件,
*/
static class FruitViewHolder extends RecyclerView.ViewHolder{
View mItemView;
ImageView ivImg;
TextView tvNo,tvName;
/**
* itemView 就是控件外層佈局,
* @param itemView
*/
public FruitViewHolder(View itemView) {
super(itemView);
mItemView = itemView;
ivImg = (ImageView) itemView.findViewById(R.id.iv_img);
tvNo = (TextView) itemView.findViewById(R.id.tv_no);
tvName = (TextView) itemView.findViewById(R.id.tv_name);
}
}
我們發現這個Adapter裏面其實有很多冗餘代碼,如果我以後還要寫其它的adapter就會產生一堆冗餘代碼,因此我們需要封裝一層,以前我自己封裝過ListView中的Adapter,RecyclerView.Adapter也差不多,
打造常用RecyclerAdapter與RecyclerView.ViewHoder基類
1. recyclerView.ViewHoder類構建
首先我們需要創建一個繼承於RecyclerView.ViewHolder的類
public class RVViewHolder extends RecyclerView.ViewHolder {
private Context mContext;
public RVViewHolder(Context context, View itemView) {
super(itemView);
this.mContext = context;
}
/**
* 通過viewId獲取控件
*
* @param viewId
* @return
*/
public <T extends View> T getView(int viewId) {
View view = itemView.findViewById(viewId);
return (T) view;
}
public RVViewHolder setImageRes(int viewId, int imgRes) {
ImageView view = getView(viewId);
view.setImageResource(imgRes);
return this;
}
public RVViewHolder setImageUrl(int viewId, String imageUrl) {
ImageView view = getView(viewId);
return this;
}
public RVViewHolder setText(int viewId, String text) {
TextView tv = getView(viewId);
tv.setText(text);
return this;
}
public RVViewHolder setTextColor(int viewId, int textColor) {
TextView view = getView(viewId);
view.setTextColor(textColor);
return this;
}
public RVViewHolder setTextSize(int viewId, float size) {
TextView view = getView(viewId);
view.setTextSize(size);
return this;
}
public RVViewHolder setTextColorRes(int viewId, int textColorRes) {
TextView view = getView(viewId);
view.setTextColor(mContext.getResources().getColor(textColorRes));
return this;
}
public RVViewHolder setVisible(int viewId, boolean visible) {
View view = getView(viewId);
view.setVisibility(visible ? View.VISIBLE : View.GONE);
return this;
}
public RVViewHolder setVisiblePlaceHolder(int viewId, boolean visible) {
View view = getView(viewId);
view.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
return this;
}
/**
* 功能描述:設置各個子控件的點擊事件
* 作者: [email protected]
* 時間:2016/12/21 13:28
* 參數:
*/
public RVViewHolder setOnClickListener(int viewId, Object o, View.OnClickListener listener) {
View view = getView(viewId);
view.setTag(o);
view.setOnClickListener(listener);
return this;
}
}
上面代碼說明:
- 創建RecyclerView.ViewHolder子類
- 獲取每一個子類控件
- 常用的文字與圖片設置方法
- 子控件的點擊事件,我這裏傳了3個參數,控件的Id,對象,監聽,一般用到點擊事件的時候,都需要綁定數據
2. recyclerViewAdapter 基類構造
先上代碼
package king.com.recyler;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.List;
/**
* @work: 構建RecyclerView.Adapter基類
* @author: hg_liuzl(hg_liuzl@qq.com)
* @date: created at 2016/12/21 11:14
*/
public abstract class BaseRVAdapter<T> extends RecyclerView.Adapter<RVViewHolder> {
protected List<T> mList; //數據集合
protected int resLayout; //佈局資源
protected Context mContext;
protected IRecyclerViewListener recyclerViewListener;
public BaseRVAdapter(Context context, List<T> mList, int resLayout) {
this.mContext = context;
this.mList = mList;
this.resLayout = resLayout;
}
@Override
public RVViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(resLayout, parent, false);
final RVViewHolder viewHolder = new RVViewHolder(mContext, view);
return viewHolder;
}
@Override
public void onBindViewHolder(final RVViewHolder viewHolder, int position) {
bindAction(viewHolder, mList.get(viewHolder.getLayoutPosition()));
/**綁定事件 一定要在這裏綁定**/
if (null != recyclerViewListener) {
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
recyclerViewListener.onItemClick(viewHolder.itemView, viewHolder.getLayoutPosition());
}
});
viewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
recyclerViewListener.onItemLongClick(viewHolder.itemView, viewHolder.getLayoutPosition());
return false;
}
});
}
bindData(viewHolder, mList.get(position));
}
/**
* 綁定數據
**/
public abstract void bindData(RVViewHolder viewHolder, T bean);
/**
* 綁定動作
**/
public abstract void bindAction(RVViewHolder viewHolder, T bean);
@Override
public int getItemCount() {
if (null != mList) {
return mList.size();
}
return 0;
}
/**
* 設置點擊事件
*
* @param mIRecyclerViewListener
*/
public void setIRecyclerViewListener(IRecyclerViewListener mIRecyclerViewListener) {
this.recyclerViewListener = mIRecyclerViewListener;
}
public interface IRecyclerViewListener {
void onItemClick(View view, int position);
void onItemLongClick(View view, int position);
}
}
代碼解讀:
1. 首先創建RecyclerAdapter的子類的抽象類,把一些需要實現的方法給子類去實現
2. 添加構造方法,其中構造函數包括Context,list,佈局資源 這樣我們可以根據自己的需要傳入相應數據類型,以及佈局
3. 在onBindViewHolder中添加數據以及綁定事件,另外事件的回調函數一定要判斷不爲空纔可以調用回調方法
給Item 添加回調,使item可以點擊以及長按
3. recycler的getAdapterPostion與getLayoutPosition()坑點說明
- 根據官方說明getAdapterPosition是在變化的時候就能獲取到position,而getLayoutPosition是在佈局完成後可以獲取到position,一般我們需要獲取某個position的時候,肯定是在佈局完成後獲取,所以建議使用getLayoutPosition來獲取position
- 另外也不要在onCreateView中獲取某個實體類,很容易造成數組越界,因爲這個時候getAdapterPosition爲-1 在創建之前佈局還沒有完成,
在子類中繼承萬能RecyclerViewAdapter
package king.com.recyler;
import android.content.Context;
import android.view.View;
import android.widget.Toast;
import java.util.List;
/**
*
* @work: ${功能介紹}
* @author: hg_liuzl([email protected])
* @date: created at 2016/12/21 12:48
*/
public class FruitAdapter2 extends BaseRVAdapter implements View.OnClickListener {
public FruitAdapter2(Context context, List mList, int resLayout) {
super(context, mList, resLayout);
}
@Override
public void bindAction(RVViewHolder viewHolder, Object o) {
viewHolder.setOnClickListener(R.id.tv_name, o, this);
}
@Override
public void bindData(RVViewHolder viewHolder, Object o) {
final FruitBean bean = (FruitBean) o;
viewHolder.setText(R.id.tv_name, bean.name);
viewHolder.setText(R.id.tv_no, "編號:" + bean.id);
viewHolder.setImageRes(R.id.iv_img, bean.resourceId);
}
@Override
public void onClick(View view) {
final FruitBean bean = (FruitBean) view.getTag();
switch (view.getId()) {
case R.id.tv_name:
Toast.makeText(mContext, "你點擊了Item中的" + bean.id + "----name是" + bean.name, Toast.LENGTH_SHORT).show();
break;
}
}
}
代碼解讀:
- 首先基礎FruitAdapter繼承於BaseRVAdapter,並實現其2個抽象方法
- 在bindAction方法中綁定子控件的點擊事件,在bindData方法中綁定數據
- 另外實現OnClick接口,來完成點擊事件
最後使用我們創建的萬能基類實際調用:
/**
* 垂直佈局
*/
public class RecyclerVerticalActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_recycler_view);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
FruitAdapter2 adapter = new FruitAdapter2(this, FruitBean.getFruitBeans(), R.layout.fruit_item_vertical);
recyclerView.setAdapter(adapter);
adapter.setIRecyclerViewListener(recyclerViewListener);
}
private BaseRVAdapter.IRecyclerViewListener recyclerViewListener = new BaseRVAdapter.IRecyclerViewListener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(RecyclerVerticalActivity.this, position + " click",
Toast.LENGTH_SHORT).show();
}
@Override
public void onItemLongClick(View view, int position) {
Toast.makeText(RecyclerVerticalActivity.this, position + " longClick",
Toast.LENGTH_SHORT).show();
}
};
}
- 可以看到適配器用的是FruitAdapter2
- 給Adaapter綁定回調方法,使item可以點擊
最後再看看效果