很多时候我们想要把一个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 {
//每个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 mAdapterInfos = new ArrayList();
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的时候 我们可以这样:
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的基类 然后可以这样简洁:
new QuickAdapter(R.layout.area_rementuijian) {
protected void onPostViewCreated(View view) {
//初始化你的view
}
}.appendTo(mMyCompositeViewAdapter);
这里目前只是解决了多个Adapter合并成一个的问题