当前位置:首页 > 科技数码 > 正文

RecyclerViewの通用适配(Adapter)

摘要: RecyclerViewの通用适配(Adapter)最佳答案53678位专家为你答疑解惑RecyclerViewの通用适配(Ad...

RecyclerViewの通用适配(Adapter)

最佳答案 53678位专家为你答疑解惑

RecyclerViewの通用适配(Adapter)

BaseQuickAdapter 导入(依赖)蓝奏云下载源码 密码:p5j0,添加 library 库到你项目当中BaseQuickAdapter 使用

mRecyclerView.setAdapter(mAdapter=new BaseQuickAdapter<String, BaseViewHolder>(R.layout.rv_item, getItemDatas()) {    @Override    protected void convert(final BaseViewHolder helper, String item) {        helper.setText(R.id.tv_item_text, item);           }});

Item点击,长按事件

//Item 点击事件setOnItemClickListener(int viewId, AdapterView.OnItemClickListener listener)//长按事件setOnItemLongClickListener(int viewId, AdapterView.OnItemLongClickListener listener)

子控件的点击事件

helper.setOnClickListener(R.id.tv_item_text, new View.OnClickListener() {       @Override       public void onClick(View v) {          //事件处理       }   });

添加头部,尾部

    //头部    mAdapter.addHeaderView();    //尾部    mAdapter.addFooterView();

以下是添加头部的代码:

View headerView=getLayoutInflater().inflate(R.layout.rv_header, null);headerView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT));mAdapter.addHeaderView(headerView);

尾部的添加与头部的添加类似。添加 Item动画

  //一行代码开启动画 默认CUSTOM动画  mAdapter.openLoadAnimation(BaseQuickAdapter.CUSTOMIN);

更换其他动画:

//ALPHAIN, SCALEIN, SLIDEIN_BOTTOM, SLIDEIN_LEFT, SLIDEIN_RIGHT, //SLIDEIN_LEFT_RIGHT, SLIDEIN_BOTTOM_TOP, CUSTOMINmAdapter.openLoadAnimation(BaseQuickAdapter.CUSTOMIN);

自定义 Item动画:

mAdapter.openLoadAnimation(new BaseAnimation[]{        new BaseAnimation() {            @Override            public Animator[] getAnimators(View view) {                return new Animator[]{ObjectAnimator.ofFloat(view, "alpha", 0.5f, 1.0f),                        ObjectAnimator.ofFloat(view, "scaleX", 0.5f, 1.0f)};            }        }});

设置加载更多只需要设置加载更多接口,就可以实现加载更多功能。

mAdapter.setOnLoadMoreListener(new BaseQuickAdapter.RequestLoadMoreListener() {        @Override        public void onLoadMoreRequested() {        }    });

以下代码正在加载中,加载失败点击重试,加载完成等状态:

mShowType++;    if (mShowType==2) {        mHandler.postDelayed(new Runnable() {            @Override            public void run() {                //加载失败,点击重试                mAdapter.loadMoreFail();            }        }, DELAY_MILLIS);    } else if (mShowType >=4) {        mHandler.post(new Runnable() {            @Override            public void run() {                //加载完成                mAdapter.loadMoreEnd();            }        });    } else {        mHandler.postDelayed(new Runnable() {            @Override            public void run() {                mAdapter.addData(addDatas());                //加载更多完成                mAdapter.loadMoreComplete();            }        }, DELAY_MILLIS);    }

可以设置加载更多类型,只需要添加一行代码,实现不同的加载更多动画:

  //设置加载更多 loadingview  setLoadMoreType(@LoadMoreType int loadMoreType)

这里有四种模式供你选择,APAY, BALL_BEAT, BALL_CLIP_ROTATE, BALL_SCALE,如果都不满足你的需求,你可以继承 BaseIndicator类从新加载视图。添加空布局

    View emptyView=getLayoutInflater().inflate(R.layout.rv_empty, null);    emptyView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,            ViewGroup.LayoutParams.MATCH_PARENT));    //添加空视图    mAdapter.setEmptyView(emptyView);        emptyView.setOnClickListener(new View.OnClickListener() {        @Override        public void onClick(View view) {            Snackbar.make(view, "your click empty", Snackbar.LENGTH_SHORT).show();        }    });

拖拽和侧滑

添加以下代码:

ItemTouchHelper.Callback callback=new SimpleItemTouchHelperCallback(mAdapter);ItemTouchHelper mItemTouchHelper=new ItemTouchHelper(callback);mAdapter.setItemTouchHelper(mItemTouchHelper);mAdapter.setDragViewId(R.id.iv_drag);mItemTouchHelper.attachToRecyclerView(mRecyclerView);

支持不同类型

mRecyclerView.setAdapter(mAdapter=new BaseMultiItemAdapter<MultiItem>(this, getMultiItemDatas()) {    @Override    protected void convert(BaseViewHolder helper, MultiItem item) {        switch (helper.getItemViewType()) {            case MultiItem.SEND:                helper.setText(R.id.chat_from_content, item.content);                //helper.setImageBitmap(R.id.chat_from_icon,getRoundCornerBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.chat_head), 16));                break;            case MultiItem.FROM:                helper.setText(R.id.chat_send_content, item.content);                //helper.setImageBitmap(R.id.chat_send_icon,getRoundCornerBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.from_head), 16));                break;        }    }    @Override    protected void addItemLayout() {        addItemType(MultiItem.SEND, R.layout.chat_send_msg);        addItemType(MultiItem.FROM, R.layout.chat_from_msg);    }});mAdapter.openLoadAnimation(true);btnSend.setOnClickListener(new View.OnClickListener() {    @Override    public void onClick(View view) {        MultiItem multiItem=new MultiItem();        multiItem.itemType=MultiItem.SEND;        multiItem.content=etChat.getText().toString();        mAdapter.add(multiItem);        mRecyclerView.smoothScrollToPosition(mAdapter.getItemCount());        //mRecyclerView.scrollToPosition(mAdapter.getItemCount() - 1);    }});

数据源:

public static List<MultiItem> getMultiItemDatas() {    List<MultiItem> list=new ArrayList<>();    for (int i=0; i < 100; i++) {        MultiItem multiItem=new MultiItem();        if (i % 2==0) {            multiItem.itemType=MultiItem.SEND;            multiItem.content="海,妹子约吗";        } else {            multiItem.itemType=MultiItem.FROM;            multiItem.content="大哥,你别怕";        }        list.add(multiItem);    }    return list;}

流式布局一行代码实现流式布局效果:

mRecyclerView.setLayoutManager(new FlowLayoutManager());

探探翻牌

类似流式效果,重写 LayoutManager 实现翻牌效果:

 mRecyclerView.setLayoutManager(new OverLayCardLayoutManager());//添加 itemTouchHelperfinal TanTanCallback callback=new TanTanCallback(mRecyclerView, mAdapter, mAdapter.getData());    //测试竖直滑动是否已经不会被移除屏幕    //callback.setHorizontalDeviation(Integer.MAX_VALUE);    final ItemTouchHelper itemTouchHelper=new ItemTouchHelper(callback);    itemTouchHelper.attachToRecyclerView(mRecyclerView);

淘宝商品列表切换

//线性        if (mIsLinearManager) {            mAdapter.setLayoutType(BaseQuickAdapter.TRANS_0_VIEW);            mRecyclerView.setLayoutManager(new LinearLayoutManager(this));        } else {            //网格            mAdapter.setLayoutType(BaseQuickAdapter.TRANS_1_VIEW);            mRecyclerView.setLayoutManager(new GridLayoutManager(this, 2));            //需要显示加载更多则加上下面这句   从新关联recycler            mAdapter.onAttachedToRecyclerView(mRecyclerView);        }        mIsLinearManager=!mIsLinearManager;

注意:最多支持3种不同类型的转换。

单选

实现单选,有好几位同仁都在问我,其实实现单选是比较简单的,方案也比较多,我这里就不在细讲了。下面我给出一种实现方案:

public void setItemChecked(int position) {        if (mLastCheckedPosition==position)            return;                mBooleanArray.put(position, true);                if (mLastCheckedPosition > -1) {            mBooleanArray.put(mLastCheckedPosition, false);            mAdapter.notifyItemChanged(mLastCheckedPosition);        }        mAdapter.notifyDataSetChanged();        mLastCheckedPosition=position;    }

mBooleanArray 存储是否被点击,把点击前的索引值置为 false , 点击索引置为 true 。

更好用的Adapter For RecyclerView

XCoreRecyclerAdapter:一种通用的Adapter For RecyclerView

背景

每当我们使用RecyclerView写一个列表的时候,都需要写类似的如下代码:

...

mTestRv=(RecyclerView) view.findViewById(R.id.test_home_rv);

mTestRecyclerAdapter=new TestRecyclerAdapter(context);

mLinearLayoutManager=new LinearLayoutManager(context);

mTestRv.setLayoutManager(mLinearLayoutManager);

mTestRv.setAdapter(mTestRecyclerAdapter);

...

那么,我们一般必不可少的是写一个Adapter,比如,为了支持多种类型,我们需要重写getItemViewType、onCreateViewHolder、onBindViewHolder等方法。比如:

public class TestRecyclerAdapter extends RecyclerView.Adapter<BaseViewHolder> {

... @Override

public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

View view=null; switch (viewType) { case Type.TYPE_HEADER:

view=LayoutInflater.from(mContext).inflate(R.layout.card_header,

parent, false); return new HeaderViewHolder(view); case Type.TYPE_HIS_HEAD:

view=LayoutInflater.from(mContext).inflate(R.layout.his_header,

parent, false); return new HeaderHisViewHolder(view);

... default: break;

} return new HeaderViewHolder(new RelativeLayout(mContext));

} @Override

public void onBindViewHolder(ViewHolder holder, int position) { switch (getItemViewType(position)) { case Type.TYPE_HEADER:

HeaderViewHolder headerViewHolder=(HeaderViewHolder) holder;

headerViewHolder.bindView(mDataSet.get(position)); break; case Type.TYPE_HIS_HEAD:

HeaderHisViewHolder headerHisViewHolder=(HeaderHisViewHolder) holder;

headerHisViewHolder.bindView(mDataSet.get(position), mOnHeaderItemClickListener); break;

... default: break;

}

} @Override

public int getItemViewType(int position) {

Data data=mDateSet.get(position);

String type=data.getType(); if("xxx".equals(type)){ return Type.TYPE_HEADER;

}else if("xxxxx".equals(type)){ return Type.TYPE_HIS_HEAD;

}else if(){

...

}

...

}

}

按照上述写法,每当我们需要增加一种类型的时候,都需要修改TestRecyclerAdapter的代码,在onCreateViewHolder等方法中添加对应的代码。后来大家觉得这个挺烦的,然后就把onCreateViewHolder中的switch语句放到工厂类当中,这一定层度上缓解了修改Adapter的频率。那么,我们能不能写一个相对通用的Adapter呢?

封装通用的插件式Adapter

我们希望做到如下目标:

1).Adapter通用化,无需每次新建Adapter

2).Item(Cell)的组件是插件式的,解耦,并可复用

用代码描述为:

...

//创建通用的Adapter

mXCoreRecyclerAdapter=new XCoreRecyclerAdapter(context); //Adapter注册item组件

mXCoreRecyclerAdapter.registerItemUIComponent(new TodoItemComponent()) .registerItemUIComponent(new TestItemComponent()); //设置数据源,完成展示

mXCoreRecyclerAdapter.setDataSet(List<IDataComponent> dataSet) ...

插件式的通用XCoreRecyclerAdapter的原理图如下:

每当我们需要添加一个新的类型时,只需要一下几步:

1.新建一个TodoItemUIComponent组件,继承自XCoreItemUIComponent

实现对应的方法;

2.注册新增的组件TodoItemUIComponent

调用XCoreRecyclerAdapter的registerItemUIComponent方法即可注册。

3.数据源实现IDataComponent接口

getViewType和组件TodoItemUIComponent的getViewType匹配即可;

这样,每次新增一种新的类型的Item时,无需修改Adapter的代码。并且,Item的组件是可复用的。

那么具体怎么设计的呢?

数据源

首先对数据源进行抽象:在Adapter列表中,需要根据数据源Data的不同类型,选择不同的ViewHolder。所以,数据源必须有一个getType的方法。

/**

* 数据源必须实现的接口

*/

public interface IDataComponent { String getViewType();

}

Item组件 ItemUIComponent

ItemUIComponent组件负责某一类type的管理。为了做到插件式,它必须实现的方法有:

onCreateView方法

该方法是用于生成Item的根View的。当Adapter调用onCreateViewHolder时,该方法会被调用。注意,子View的初始化,不要写在onCreateView中。需要写在onViewCreated方法中。

onViewCreated方法

创建完ItemComponent之后,就会立即回调该方法,View的初始化请写在这里。

getViewType方法

该方法是为了和数据源进行关联的,数据源的getViewType方法和组件的getViewType方法的值一致时,即完成匹配。

bindView方法

该方法会在Adapter回调onBindViewHolder

public class TodoItemComponent extends XCoreItemUIComponent { @Override

public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container) { //TODO 在这里指定item组件的布局

return inflater.inflate(R.layout.todo_item_layout,container,false);

} @Override

public void onViewCreated(View view) { //TODO 在这里写View初始化

view.findViewById();

...

} @Override

public String getViewType() { //与数据源的getViewType关联

return TodoItemComponent.class.getSimpleName();

} @Override

public void bindView(IXCoreComponent coreComponent,

XCoreRecyclerAdapter coreRecyclerAdapter,

XCoreRecyclerAdapter.IDataComponent data, int pos) { //TODO 在这里写绑定逻辑

}

}

数据源和Item组件都已经定义好,那么,核心的XCoreRecyclerAdapter源码应该怎么做呢?

XCoreRecyclerAdapter源码

package com.github.nuptboyzhb.xcore.adapter;import android.content.Context;import android.support.v7.widget.RecyclerView;import android.text.TextUtils;import android.util.SparseArray;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import com.github.nuptboyzhb.xcore.components.IXCoreComponent;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;/**

* @version mochuan.zhb on 2016/8/9.

* @Author Zheng Haibo

* @Blog github.com/nuptboyzhb

* @Company Alibaba Group

* @Description 通用的Adapter for RecyclerView

*/public class XCoreRecyclerAdapter extends RecyclerView.Adapter<XCoreRecyclerAdapter.CommonViewHolder> { private IXCoreComponent mIXCoreComponent;//外层UI组件

private List<IDataComponent> mDataSet=new ArrayList<IDataComponent>();//数据源

private SparseArray<XCoreItemUIComponent> mConfigurationSparseArray=new SparseArray<XCoreItemUIComponent>();//集合:type对应的Item组件

private Map<String, Integer> mViewTypeMap=new HashMap<String, Integer>();//type的string和int映射

public XCoreRecyclerAdapter() {

} public XCoreRecyclerAdapter(IXCoreComponent mIXCoreComponent) { this.mIXCoreComponent=mIXCoreComponent;

} @Override

public CommonViewHolder onCreateViewHolder(ViewGroup viewGroup, int type) { //根据数据类型获取对应的item组件

XCoreItemUIComponent XCoreItemUIComponent=mConfigurationSparseArray.get(type); if (XCoreItemUIComponent==null) {//如果未获取到,展示空item

return getDefaultViewHolder(viewGroup.getContext());

} try { //使用item组件创建一个新的View

View view=XCoreItemUIComponent.onCreateView(LayoutInflater.from(viewGroup.getContext()), viewGroup); //使用View构建内部的ViewHolder

CommonViewHolder commonViewHolder=new CommonViewHolder(view); //创建一个新的Item组件

XCoreItemUIComponent realItem=XCoreItemUIComponent.getClass().newInstance(); //将创建的View设置到真是的Item组件中

realItem.setItemView(view); //使用内部ViewHolder

commonViewHolder.setXCoreItemUIComponent(realItem); return commonViewHolder;

} catch (Throwable t) {

t.printStackTrace();

} return getDefaultViewHolder(viewGroup.getContext());

} @Override

public void onBindViewHolder(CommonViewHolder baseViewHolder, int pos) {

baseViewHolder.bindView(mIXCoreComponent, this, mDataSet.get(pos), pos);

} @Override

public int getItemViewType(int position) {

IDataComponent item=mDataSet.get(position);

Integer integer=mViewTypeMap.get(item.getViewType()); if (integer==null) { return -1;

} return integer;

} @Override

public int getItemCount() { if (mDataSet !=null) { return mDataSet.size();

} return 0;

} /**

* 获取Adapter的数据源

*

* @return

*/

public List<IDataComponent> getDataSet() { return mDataSet;

} /**

* 设置Adapter的数据源

*

* @param dataSet

*/

public void setDataSet(List<IDataComponent> dataSet) { this.mDataSet=dataSet;

notifyDataSetChanged();

} /**

* get the error view holder

*

* @param context

* @return

*/

protected CommonViewHolder getDefaultViewHolder(Context context) { return new CommonViewHolder(new View(context));

} /**

* get the unique int type

*

* @param name

* @return

*/

private int getUniqueIntType(String name) { if (TextUtils.isEmpty(name)) { return -1;

} int type=name.hashCode(); while (true) {

XCoreItemUIComponent old=mConfigurationSparseArray.get(type); if (old !=null) {

String oldName=old.getViewType(); if (!name.equals(oldName)) {

type=type + 1;

} else { return type;

}

} else { return type;

}

}

} /**

* 注册Item组件

*

* @param XCoreItemUIComponent

* @return

*/

public XCoreRecyclerAdapter registerItemUIComponent(XCoreItemUIComponent XCoreItemUIComponent) { if (XCoreItemUIComponent==null || TextUtils.isEmpty(XCoreItemUIComponent.getViewType())) { return this;

} int viewTypeInt=getUniqueIntType(XCoreItemUIComponent.getViewType());

mViewTypeMap.put(XCoreItemUIComponent.getViewType(), viewTypeInt);

mConfigurationSparseArray.put(viewTypeInt, XCoreItemUIComponent); return this;

} /**

* 注销配置

*

* @param XCoreItemUIComponent

* @return

*/

public XCoreRecyclerAdapter unregisterItemUIComponent(XCoreItemUIComponent XCoreItemUIComponent) { if (XCoreItemUIComponent==null || TextUtils.isEmpty(XCoreItemUIComponent.getViewType())) { return this;

} int index=mConfigurationSparseArray.indexOfValue(XCoreItemUIComponent); if (index==-1) { return this;

}

mConfigurationSparseArray.remove(index); return this;

} /**

* 数据源必须实现的接口

*/

public interface IDataComponent { String getViewType();

} /**

* 使用CommonViewHolder代理XCoreItemUIComponent组件

*/

public static class CommonViewHolder extends RecyclerView.ViewHolder { private XCoreItemUIComponent XCoreItemUIComponent; public void setXCoreItemUIComponent(XCoreItemUIComponent XCoreItemUIComponent) { this.XCoreItemUIComponent=XCoreItemUIComponent;

} public XCoreItemUIComponent getXCoreItemUIComponent() { return XCoreItemUIComponent;

} public CommonViewHolder(View itemView) { super(itemView);

} public void bindView(IXCoreComponent mIXCoreComponent,

XCoreRecyclerAdapter XCoreRecyclerAdapter,

XCoreRecyclerAdapter.IDataComponent data, int pos) { if (XCoreItemUIComponent==null) { return;

}

XCoreItemUIComponent.bindView(mIXCoreComponent, XCoreRecyclerAdapter, data

, pos);

} public void onViewDetachedFromWindow() { if (XCoreItemUIComponent !=null) {

XCoreItemUIComponent.onViewDetachedFromWindow();

}

}

} @Override

public void onViewDetachedFromWindow(CommonViewHolder holder) { super.onViewDetachedFromWindow(holder);

holder.onViewDetachedFromWindow();

}

}

XCoreItemUIComponent源码

package com.github.nuptboyzhb.xcore.adapter;

import android.support.annotation.Nullable;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;

import com.github.nuptboyzhb.xcore.components.IXCoreComponent;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

/**

* @version mochuan.zhb on 2016/8/12.

* @Author Zheng Haibo

* @Blog github.com/nuptboyzhb

* @Company Alibaba Group

* @Description item component 抽象类

*/

public abstract class XCoreItemUIComponent implements IXCoreComponent {

/**

* 创建View *

* @param inflater

* @param container

* @return

*/

public abstract View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container);

/**

* 在此做View的初始化操作 *

* @param view

*/

public abstract void onViewCreated(View view);

/**

* item组件支持的ViewType,与IDataComponent的getViewType对应 *

* @return

*/

public abstract String getViewType();

/**

* 绑定数据-频繁回调 *

* @param coreComponent 外出UI组件

* @param coreRecyclerAdapter Adapter

* @param data 对应的数据源

* @param pos item所在列表的位置

*/

public abstract void bindView(IXCoreComponent coreComponent,

XCoreRecyclerAdapter coreRecyclerAdapter,

XCoreRecyclerAdapter.IDataComponent data,

int pos);

/**

* item组件销毁时调用 */

public abstract void onViewDetachedFromWindow();

//必须有无参数的构造函数

public View itemView;

public void setItemView(View itemView) {

this.itemView=itemView;

onViewCreated(itemView);

}

}

后续

本篇博客主要是介绍通用的XCoreRecyclerAdapter引擎,极大提高了Adapter的复用率,让开发集中把精力集中在必要的业务代码上,提高开发效率。另外,与XCoreRecyclerAdapter引擎相配套的还有UI组件化、数据控制框架XCoreRedux、数据绑定等。请参考下一遍博客:《Android Redux实践与UI组件化:XCoreRedux框架》(ing...)

发表评论