Android的复合RecyclerView

很多时候我们想要把一个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() {
			@Override
			public void onChanged() {
				MyCompositeViewAdapter.this.notifyDataSetChanged();
			}

			@Override
			public void onItemRangeChanged(int positionStart, int itemCount) {
				updateAdapterInfos();
				MyCompositeViewAdapter.this.notifyItemRangeChanged(ai.mItemPositionBase + positionStart, itemCount);
			}

			@Override
			public void onItemRangeInserted(int positionStart, int itemCount) {
				updateAdapterInfos();
				//TODO 这里的逻辑不知道对不对?
				MyCompositeViewAdapter.this.notifyItemRangeInserted(ai.mItemPositionBase + positionStart, itemCount);
			}

			@Override
			public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {//itemCount是1
				updateAdapterInfos();
				MyCompositeViewAdapter.this.notifyItemMoved(ai.mItemPositionBase + fromPosition, ai.mItemPositionBase + toPosition);
			}

			@Override
			public 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();
		}
	}

	@Override
	public int getItemViewType(int position) {
		//根据position找到ai
		AdapterInfo 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.");
	}

	@Override
	public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
		//找到对应的adapter
		AdapterInfo ai = findAdapterByViewType(viewType);
		//恢复viewType 并创建ViewHolder
		RecyclerView.ViewHolder viewHolder = ai.mAdapter.onCreateViewHolder(parent, viewType - ai.mViewTypeBase);
		return viewHolder;
	}


	@Override
	public 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.");
	}

	@Override
	public int getItemCount() {
		//总数 相加
		int count = 0;
		for (AdapterInfo ai : mAdapterInfos)
			count += ai.mAdapter.getItemCount();
		return count;
	}

	@Override
	public long getItemId(int position) {
		AdapterInfo ai = findAdapterByPosition(position);
		return ai.mAdapter.getItemId(position - ai.mItemPositionBase);
	}

	@Override
	public void setHasStableIds(boolean hasStableIds) {
		//暂时不处理
		super.setHasStableIds(hasStableIds);
	}

	@Override
	public 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() {
	@Override
	public 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) {
		};
	}

	@Override
	public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
		//do nothing
	}
		@Override
	public 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值