RecyclerView 不支持头部和底部(仿造ListView);
查看ListView源码是如何支持头部和底部的(参考HeaderViewListAdapter.java的源码,用了装饰设计模式)。
让ListAdapter变得更加强大,让其支持头部Header和底部Footer的添加。
装饰者模式,添加RecyclerView的Header和Footer。
源码中用到的装饰者模式;
UML内部类怎么画。StarUML。
模板设计模式,手写OkHttp的Dispatcher。
一. 装饰者模式
装饰设计模式也称包装设计模式,用来动态的扩展对象的功能,也是继承关系的的一种替代方案之一。
说个大白话就是,在不使用的继承的方式下,采用装饰设计模式可以扩展一个对象的功能,可以使一个对象变得越来越强大。
源码中用到的装饰者设计模式 Eg:
ListView支持头部、底部。
ContextWrapper ;
IO的输入输出流;
private void testIO() {
try {
FileReader fr = new FileReader("xx.file");
// is.read();
BufferedReader br = new BufferedReader(fr);
br.readLine(); // 拓展FileReader,让其支持读取一行。
} catch (Exception e) {
e.printStackTrace();
}
}
// 普通的写法:new一个对象,然后调用对象的方法
// TeacherEat teacherEat = new TeacherEat();
// teacherEat.eat();
// 装饰者模式,一般是把类对象作为构造参数传递。
PersonEat personEat = new PersonEat();
TeacherEat teacherEat = new TeacherEat(personEat);
teacherEat.eat();
二. 研究ListView源码。
public void addHeaderView(View v, Object data, boolean isSelectable) {
if (v.getParent() != null && v.getParent() != this) {
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "The specified child already has a parent. "
+ "You must call removeView() on the child's parent first.");
}
}
final FixedViewInfo info = new FixedViewInfo();
info.view = v;
info.data = data;
info.isSelectable = isSelectable;
// 添加到头部集合中
mHeaderViewInfos.add(info);
mAreAllItemsSelectable &= isSelectable;
// Wrap the adapter if it wasn't already wrapped.
if (mAdapter != null) { // adapter不为空的时候,采取包裹(才能添加Header)
// 判断当前Adapter是否为空。在原来的Adapter上,进行一层包装。让它支持Header和Footer.
if (!(mAdapter instanceof HeaderViewListAdapter)) {
wrapHeaderListAdapterInternal();
}
// In the case of re-adding a header view, or adding one later on,
// we need to notify the observer.
if (mDataSetObserver != null) {
mDataSetObserver.onChanged();
}
}
}
/** @hide */
protected void wrapHeaderListAdapterInternal() {
mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, mAdapter);
}
/** @hide */
protected HeaderViewListAdapter wrapHeaderListAdapterInternal(
ArrayList headerViewInfos,
ArrayList footerViewInfos,
ListAdapter adapter) {
return new HeaderViewListAdapter(headerViewInfos, footerViewInfos, adapter);
}
ListView本身并不支持添加头部和底部,系统在原来的基础上包装了一层。系统帮我们写好了。
最重要的,要知道HeaderViewListAdapter是怎么写的。
HeaderViewListAdapter 中,
getCount() 、getItem(int position) 、getView(int position, View convertView, ViewGroup parent)、getItemViewType(int position).
在原来的基础上,包一层。形成新的Adapter,WrapperAdapter,里面的是原始的适配器(mAdapter)。
三. 基本使用,每次都包装一下. 相对麻烦。
RecyclerView recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
mAdapter = new RecyclerAdapter(this, mData);
// 加一层数据, 每次都要加一层。
final WrapRecyclerAdapter adapter = new WrapRecyclerAdapter(mAdapter);
recyclerView.setAdapter(adapter);
View header = LayoutInflater.from(this).inflate(R.layout.layout_header_footer, recyclerView,false);
adapter.addHeaderView(header);
header.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
adapter.removeHeaderView(header);
}
});
四. 自定义RecyclerView的实现
package com.example.recyclerview01.wrapper;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
/**
* 自定义,包裹效果的RV:
* 每次使用的时候,不需要再包装一下。
* 观察者模式:
*/
public class WrapRecyclerView extends RecyclerView {
private WrapRecyclerAdapter mWrapRecyclerAdapter;
// 带注册的观察者.
private AdapterDataObserver mAdapterObserver = new AdapterDataObserver() {
// 观察者的回调.
@Override
public void onChanged() {
mWrapRecyclerAdapter.notifyDataSetChanged();
Log.d("TAG", "onChanged ");
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
mWrapRecyclerAdapter.notifyItemRangeChanged(positionStart, itemCount);
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
mWrapRecyclerAdapter.notifyItemRangeChanged(positionStart, itemCount, payload);
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
mWrapRecyclerAdapter.notifyItemRangeInserted(positionStart, itemCount);
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
Log.d("TAG", "onItemRangeRemoved " + positionStart + " " + itemCount);
mWrapRecyclerAdapter.notifyItemRangeRemoved(positionStart, itemCount);
}
@Override
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
mWrapRecyclerAdapter.notifyItemMoved(fromPosition, toPosition);
}
};
public WrapRecyclerView(Context context) {
this(context, null);
}
public WrapRecyclerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public WrapRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public void setAdapter(RecyclerView.Adapter adapter) {
if (adapter instanceof WrapRecyclerAdapter) {
mWrapRecyclerAdapter = (WrapRecyclerAdapter) adapter;
} else {
// 删除的bug: adapter中数据已经修改了。但是mWrapRecyclerAdapter中的数据没修改。
// 所以需要关联---观察者.
mWrapRecyclerAdapter = new WrapRecyclerAdapter(adapter);
// 注册观察者
adapter.registerAdapterDataObserver(mAdapterObserver);
}
super.setAdapter(mWrapRecyclerAdapter);
}
// 添加header、footer的方法
public void addHeaderView(View view) {
// 要先setAdapter,mWrapRecyclerAdapter才不为空.
if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.addHeaderView(view);
}
}
public void addFooterView(View view) {
if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.addFooterView(view);
}
}
public void removeHeaderView(View view) {
if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.removeHeaderView(view);
}
}
public void removeFooterView(View view) {
if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.removeFooterView(view);
}
}
}
xml中使用自定义RecyclerView
// layout_header_footer.xml
xml中使用自定义RecyclerView
/**
* RecyclerView头部和底部的包装类。
*/
public class WrapRecyclerAdapter extends RecyclerView.Adapter {
// 数据列表原始的适配器,它不包含头部
private RecyclerView.Adapter mAdapter;
// 头部和底部的集合ArrayList不好区分是否已经添加过了;使用Map集合,进行标识是头部还是底部?
// 如果key是int,value是Object对象,建议使用SparseArray
private SparseArray mHeaders, mFooters;
private static int BASE_HEADER_KEY = 1000000;
private static int BASE_FOOTER_KEY = 2000000;
public WrapRecyclerAdapter(RecyclerView.Adapter adapter) {
this.mAdapter = adapter;
mHeaders = new SparseArray();
mFooters = new SparseArray();
}
@Override
public int getItemViewType(int position) {
// 根据当前位置position,返回当前viewType.
// 头部;mAdapter中间的数据;底部.
int numHeaders = mHeaders.size();
if (position < numHeaders) {
return mHeaders.keyAt(position);
}
// mAdapter
final int adjPosition = position - numHeaders; // 位置修正
int adapterCount = 0;
if (mAdapter != null) {
adapterCount = mAdapter.getItemCount();
if (adjPosition < adapterCount) {
return mAdapter.getItemViewType(adjPosition);
}
}
// footer
int footerPosition = adjPosition - adapterCount;
return mFooters.keyAt(footerPosition);
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// 根据viewType,来区分是头部/底部,还是列表中元素。返回对应的key。
if (mHeaders.indexOfKey(viewType) >= 0) {
// 有头部,获取到对应头部,并包装成ViewHolder.
return createHeaderFooterViewHolder(mHeaders.get(viewType));
} else if (mFooters.indexOfKey(viewType) >= 0) {
// 底部
return createHeaderFooterViewHolder(mFooters.get(viewType));
}
// 中间列表
return mAdapter.createViewHolder(parent, viewType);
}
// 将header,footer包装成ViewHolder。
private RecyclerView.ViewHolder createHeaderFooterViewHolder(View view) {
return new RecyclerView.ViewHolder(view) {
};
}
/**
* 是不是底部类型
*/
private boolean isFooterViewType(int viewType) {
int position = mFooters.indexOfKey(viewType);
return position >= 0;
}
/**
* 是不是头部类型
*/
private boolean isHeaderViewType(int viewType) {
int position = mHeaders.indexOfKey(viewType);
return position >= 0;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
// 头部和底部,不用绑定数据
int numHeaders = mHeaders.size();
if (position < numHeaders) {
return;
}
// mAdapter
final int adjPosition = position - numHeaders; // 位置修正
if (mAdapter != null) {
int adapterCount = mAdapter.getItemCount(); // 总数量
if (adjPosition < adapterCount) {
// 绑定数据,使用矫正后的位置adjPosition
mAdapter.onBindViewHolder(holder, adjPosition);
}
}
}
@Override
public int getItemCount() {
int totalCount = mAdapter.getItemCount() + mHeaders.size() + mFooters.size();
return totalCount;
}
// 添加移除,头部和底部
public void addHeaderView(View view) {
// 不能重复添加
/* if (mHeaders.indexOfValue(view) == -1) {
// 集合中没有,就添加。不能重复添加。
mHeaders.put(BASE_HEADER_KEY++, view);
}*/
int position = mHeaders.indexOfValue(view);
if (position < 0) {
mHeaders.put(BASE_HEADER_KEY++, view);
}
Log.d("TAG", "mHeaders:" + mHeaders.size());
notifyDataSetChanged();
}
public void addFooterView(View view) {
if (mFooters.indexOfValue(view) == -1) {
// 集合中没有,就添加。不能重复添加。
mFooters.put(BASE_FOOTER_KEY++, view);
notifyDataSetChanged();
}
}
public void removeHeaderView(View view) {
if (mHeaders.indexOfValue(view) >= 0) {
mHeaders.removeAt(mHeaders.indexOfValue(view));
notifyDataSetChanged();
}
}
public void removeFooterView(View view) {
if (mFooters.indexOfValue(view) >= 0) {
mFooters.removeAt(mFooters.indexOfValue(view));
notifyDataSetChanged();
}
}
}
使用。
private RecyclerAdapter mListAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_base_use);
initData();
WrapRecyclerView mRecyclerView = (WrapRecyclerView) findViewById(R.id.recyclerView);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mListAdapter = new RecyclerAdapter(this, mData);
mRecyclerView.setAdapter(mListAdapter);
View header = LayoutInflater.from(this).inflate(R.layout.layout_header_footer, mRecyclerView, false);
mRecyclerView.addHeaderView(header);
header.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mRecyclerView.removeHeaderView(header);
}
});
mRecyclerView.addItemDecoration(new GridLayoutItemDecoration(this, R.drawable.item_divider_01));
// 添加点击事件。
mListAdapter.setOnItemClickListener(new RecyclerAdapter.ItemClickListener() {
@Override
public void onItemClick(int position) {
Toast.makeText(BaseUseActivity.this, "item:" + mData.get(position), Toast.LENGTH_SHORT).show();
mData.remove(position);
mListAdapter.notifyDataSetChanged(); // 回调观察者的onChanged方法
// mListAdapter.notifyItemRemoved(position); // 回调观察者的onItemRangeRemoved方法
}
});
}