Android的复合RecyclerView

来源:互联网 发布:源码授权是什么 编辑:程序博客网 时间:2024/05/02 00:41

很多时候我们想要把一个RecyclerView(或ListView等)融入到一个ScrollView里.

具体情况是这样的:一个页面可能由一些不属于ListView的item的view和一个ListView组成,而我们想要总体都有一个滑动的效果

对于ListView 解决方案可能是 将其他的View元素 作为ListView的HeaderView或FooterView 这是可以解决问题的


然而对于RecyclerView的话 它并没有直接提供Header或Footer的功能 需要自己带代码里进行判断(主要是利用viewType)

下面简单引入一个复合的RecyclerView.Adapter的实现 它的功能是可以将多个Adapter的实现类串起来 对外表现出一个整体

public class MyCompositeViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {//每个adapter最多有几个viewType 原始的adapter的viewType应该要介于  [0,10000)public static final int MAX_VIEW_TYPE_COUNT_PER_ADAPTER = 10000;//viewType的基数public static final int VIEW_TYPE_BASE = 0;private List<AdapterInfo> mAdapterInfos = new ArrayList<AdapterInfo>();private int mNextViewTypeBase = VIEW_TYPE_BASE;public void removeAdapter(RecyclerView.Adapter adapter) {AdapterInfo ai = null;for (AdapterInfo ai2 : mAdapterInfos) {if (ai2.mAdapter == adapter) {ai = ai2;break;}}if (ai == null)throw new IllegalArgumentException("找不到对应的adapter");mAdapterInfos.remove(ai);ai.mAdapter.unregisterAdapterDataObserver(ai.mObserver);//这里如果使用的是 notify removed 那么这些项会被当做是 移除 从而除法动作notifyItemRangeRemoved(ai.mItemPositionBase, ai.mAdapter.getItemCount());//如果是使用 chagned 那么不会触发删除动作 因为它并不知道哪些东西被删除了//notifyDataSetChanged();}public void addAdapter(final RecyclerView.Adapter adapter) {if (adapter == null)throw new IllegalArgumentException("adapter不能为null.");final AdapterInfo ai = new AdapterInfo();if (adapter instanceof AdapterInfoAware) {((AdapterInfoAware) adapter).setAdapterInfo(ai);}ai.mAdapter = adapter;ai.mViewTypeBase = mNextViewTypeBase;mNextViewTypeBase += MAX_VIEW_TYPE_COUNT_PER_ADAPTER;mAdapterInfos.add(ai);ai.mObserver = new RecyclerView.AdapterDataObserver() {@Overridepublic void onChanged() {MyCompositeViewAdapter.this.notifyDataSetChanged();}@Overridepublic void onItemRangeChanged(int positionStart, int itemCount) {updateAdapterInfos();MyCompositeViewAdapter.this.notifyItemRangeChanged(ai.mItemPositionBase + positionStart, itemCount);}@Overridepublic void onItemRangeInserted(int positionStart, int itemCount) {updateAdapterInfos();//TODO 这里的逻辑不知道对不对?MyCompositeViewAdapter.this.notifyItemRangeInserted(ai.mItemPositionBase + positionStart, itemCount);}@Overridepublic void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {//itemCount是1updateAdapterInfos();MyCompositeViewAdapter.this.notifyItemMoved(ai.mItemPositionBase + fromPosition, ai.mItemPositionBase + toPosition);}@Overridepublic void onItemRangeRemoved(int positionStart, int itemCount) {updateAdapterInfos();MyCompositeViewAdapter.this.notifyItemRangeRemoved(ai.mItemPositionBase + positionStart, itemCount);}};adapter.registerAdapterDataObserver(ai.mObserver);}/** * 更新所有的apdater */private void updateAdapterInfos() {int count = 0;for (AdapterInfo ai : mAdapterInfos) {ai.mItemPositionBase = count;count += ai.mAdapter.getItemCount();}}@Overridepublic int getItemViewType(int position) {//根据position找到aiAdapterInfo ai = findAdapterByPosition(position);return ai.mViewTypeBase + ai.mAdapter.getItemViewType(position);}private AdapterInfo findAdapterByViewType(int viewType) {for (AdapterInfo ai : mAdapterInfos) {if (ai.mViewTypeBase <= viewType && viewType < ai.mViewTypeBase + MAX_VIEW_TYPE_COUNT_PER_ADAPTER)return ai;}throw new IllegalStateException("找不到viewType" + viewType + "对应的adapter.");}@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {//找到对应的adapterAdapterInfo ai = findAdapterByViewType(viewType);//恢复viewType 并创建ViewHolderRecyclerView.ViewHolder viewHolder = ai.mAdapter.onCreateViewHolder(parent, viewType - ai.mViewTypeBase);return viewHolder;}@Overridepublic void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {AdapterInfo ai = findAdapterByPosition(position);ai.mAdapter.onBindViewHolder(holder, position - ai.mItemPositionBase);}/** * 通过position找出对应的AdapterInfo 顺便更新一下这个ai的mItemPositionBase * * @param position * @return */private AdapterInfo findAdapterByPosition(int position) {int count = 0;for (AdapterInfo ai : mAdapterInfos) {ai.mItemPositionBase = count;count += ai.mAdapter.getItemCount();if (position < count)return ai;}throw new IllegalStateException("找不到position=" + position + "对应的adapter.");}@Overridepublic int getItemCount() {//总数 相加int count = 0;for (AdapterInfo ai : mAdapterInfos)count += ai.mAdapter.getItemCount();return count;}@Overridepublic long getItemId(int position) {AdapterInfo ai = findAdapterByPosition(position);return ai.mAdapter.getItemId(position - ai.mItemPositionBase);}@Overridepublic void setHasStableIds(boolean hasStableIds) {//暂时不处理super.setHasStableIds(hasStableIds);}@Overridepublic void onViewRecycled(RecyclerView.ViewHolder holder) {int viewType = holder.getItemViewType();AdapterInfo ai = findAdapterByViewType(viewType);ai.mAdapter.onViewRecycled(holder);}}

下面是两个辅助的类或接口

/** * 当一个Adapter被套在我的MyCompositeAdapter里的时候 * 用来维护信息的一个结构 * Created by xzchaoo on 2015/9/22 0022. */public class AdapterInfo {int mViewTypeBase;RecyclerView.Adapter mAdapter;int mItemPositionBase;RecyclerView.AdapterDataObserver mObserver;/** * TODO * 可以获得这个adapter的实际开始下标 因为你在原来的adapter里获得的是你自己认为的position * 而实际上你的adapter是和别人混合在一起的 所以要加上一个偏移量 * * @return */public int getItemPositionBase() {return mItemPositionBase;}}

/** * 实现了这个接口的Adatper会被注入AdapterInfo 通过set方法 * Created by xzchaoo on 2015/9/22 0022. */public interface AdapterInfoAware {void setAdapterInfo(AdapterInfo ai);AdapterInfo getAdapterInfo();}

MyCompositeViewAdapter 里的实现思路很简单, 现在还不是很完备 等遇到问题了再去作补充
当我们需要添加一个HeaderView的时候 <span style="font-family: Arial, Helvetica, sans-serif;">我们可以这样:</span>
<span style="font-family: Arial, Helvetica, sans-serif;"></span><pre name="code" class="java">mMyCompositeViewAdapter.addAdapter(new RecyclerView.Adapter() {@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.header_1, parent, false);//初始化一下view 可以考虑写个函数或回调扔出去return new RecyclerView.ViewHolder(view) {};}@Overridepublic void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {//do nothing}@Overridepublic int getItemCount() {return 1;}});
当然你会发现上面有很多的的冗余 你可以自定义一个Adapter的基类 然后可以这样简洁:
<span style="font-family: Arial, Helvetica, sans-serif;"></span><pre name="code" class="java">new QuickAdapter(R.layout.area_rementuijian) {protected void onPostViewCreated(View view) {//初始化你的view
}}.appendTo(mMyCompositeViewAdapter);
这里目前只是解决了多个Adapter合并成一个的问题
                                             
0 0
原创粉丝点击