效果图:
参考文章:
Android 优雅的为RecyclerView添加HeaderView和FooterView
该功能主要是对adapter使用了装饰者模式,具体代码看下面的分析:
- HeaderAndFooterActivity.java
public class HeaderAndFooterActivity extends AppCompatActivity {
@BindView(R.id.recyclerView)
RecyclerView mRecyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_header_and_footer);
ButterKnife.bind(this);
setTitle("添加Header和Footer");
//创建处理正常数据的adapter
HeaderAndFotterAdapter adapter = new HeaderAndFotterAdapter(this);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
//创建负责处理Header和footer的adpapter
HeaderAndFooterWrapper headerAndFooterWrapper = new HeaderAndFooterWrapper(adapter);
TextView t1 = new TextView(this);
t1.setText("Header 1");
TextView t2 = new TextView(this);
t2.setText("Header 2");
TextView t3 = new TextView(this);
t3.setText("Header 3");
TextView t4 = new TextView(this);
t4.setText("Header 4");
headerAndFooterWrapper.addHeaderView(t1);
headerAndFooterWrapper.addHeaderView(t2);
headerAndFooterWrapper.addFooterView(t3);
headerAndFooterWrapper.addFooterView(t4);
mRecyclerView.setLayoutManager(linearLayoutManager);
//RecyclerView的Adapter被赋值为headerAndFooterWrapper,
// 而正常处理数据的adapter在headerAndFooterWrapper中被调用
mRecyclerView.setAdapter(headerAndFooterWrapper);
}
}
上面代码很简单,主要就是初始化两个adpater和创建头布局和底布局。
- HeaderAndFooterWrapper.java
public class HeaderAndFooterWrapper extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
public static final int BASE_ITEM_TYPE_HEADER = 100000;
public static final int BASE_ITEM_TYPE_FOOTER = 200000;
private SparseArrayCompat<View> mHeaderViews = new SparseArrayCompat<>();
private SparseArrayCompat<View> mFooterViews = new SparseArrayCompat<>();
private RecyclerView.Adapter mInnerAdapter;
public HeaderAndFooterWrapper(RecyclerView.Adapter innerAdapter) {
mInnerAdapter = innerAdapter;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (mHeaderViews.get(viewType) != null){
return new MyViewHolder(mHeaderViews.get(viewType));
}else if (mFooterViews.get(viewType) != null){
return new MyViewHolder(mFooterViews.get(viewType));
}
return mInnerAdapter.onCreateViewHolder(parent,viewType);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (isHeaderViewPos(position))
return;
if (isFooterViewPos(position))
return;
mInnerAdapter.onBindViewHolder(holder,position - getHeadersCount());
}
@Override
public int getItemCount() {
return getHeadersCount() + getFootersCount() + getRealItemCount();
}
@Override
public int getItemViewType(int position) {
if (isHeaderViewPos(position)){
return mHeaderViews.keyAt(position);
}else if (isFooterViewPos(position)){
return mFooterViews.keyAt(position - getHeadersCount() - getRealItemCount());
}
return mInnerAdapter.getItemViewType(position - getHeadersCount());
}
/**
* 获取正常数据的size
* @return
*/
private int getRealItemCount(){
return mInnerAdapter.getItemCount();
}
/**
* 判断是否是Header
* @param position
* @return
*/
private boolean isHeaderViewPos(int position){
return position < getHeadersCount();
}
/**
* 判断是否是Footer
* @param position
* @return
*/
private boolean isFooterViewPos(int position){
return position >= getHeadersCount() + getRealItemCount();
}
public void addHeaderView(View view){
mHeaderViews.put(mHeaderViews.size() + BASE_ITEM_TYPE_HEADER,view);
}
public void addFooterView(View view){
mFooterViews.put(mFooterViews.size() + BASE_ITEM_TYPE_FOOTER,view);
}
private int getHeadersCount(){
return mHeaderViews.size();
}
public int getFootersCount(){
return mFooterViews.size();
}
class MyViewHolder extends RecyclerView.ViewHolder{
public MyViewHolder(View itemView) {
super(itemView);
}
}
}
上面我们使用了线性布局,如果我们使用了网格和瀑布流,就有可能出问题咯,比如头布局不能占据一行的问题。
下面是关于适配网格和瀑布流布局的解决方案:
在HeaderAndFooterWrapper.java中重写下面的方法:
/**
* 适配网格布局
* @param recyclerView
*/
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
mInnerAdapter.onAttachedToRecyclerView(recyclerView);
final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager instanceof GridLayoutManager){
final GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
return isHeaderViewPos(position) || isFooterViewPos(position) ? ((GridLayoutManager) layoutManager).getSpanCount() : 1;
}
});
}
}
上面只解决了网格布局的,下面我们在HeaderAndFooterWrapper.java中可以重写onViewAttachedToWindow方法解决瀑布流的问题。
@Override
public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
mInnerAdapter.onViewAttachedToWindow(holder);
int position = holder.getLayoutPosition();
if (isHeaderViewPos(position)||isFooterViewPos(position)){
ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
if (lp != null && lp instanceof StaggeredGridLayoutManager.LayoutParams){
StaggeredGridLayoutManager.LayoutParams p = (StaggeredGridLayoutManager.LayoutParams) lp;
p.setFullSpan(true);
}
}
}