RecyclerView 添加头布局 和 底布局
RecyclerView 默认是没有添加头部和底部布局的方法。而在listView我们经常会用到list.addHreadView,list.addFooterView,而在recyclerView是不提供这种方法的,这可如何是好。于是观察了listView 是如何添加头布局和底部布局的,模拟listView 为recyclerView添加头部和底部布局。
我们先来查看一看ListView.addHeaderView源码
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;
// 如果适配器尚未包装,请将其包装
if (mAdapter != null) {
if (!(mAdapter instanceof HeaderViewListAdapter)) {
//对适配器进行包装
wrapHeaderListAdapterInternal();
}
// 如果要重新添加标题视图,或在以后添加标题视图,
// 我们需要通知.
if (mDataSetObserver != null) {
mDataSetObserver.onChanged();
}
}
}
我们跟着 wrapHeaderListAdapterInternal()点进去看看
/** @hide */
protected HeaderViewListAdapter wrapHeaderListAdapterInternal(
ArrayList<ListView.FixedViewInfo> headerViewInfos,
ArrayList<ListView.FixedViewInfo> footerViewInfos,
ListAdapter adapter) {
return new HeaderViewListAdapter(headerViewInfos, footerViewInfos, adapter);
}
/** @hide */
protected void wrapHeaderListAdapterInternal() {
mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, mAdapter);
}
listView添加头部和尾部的原理就是在 适配器adapter上添加了一个代理 (HeaderViewListAdapter),检查是否需要添加头部和尾部
listView添加头部布局和底部布局的核心代码
ListView.addHeaderView(){
// 如果适配器尚未包装,请将其包装
if (mAdapter != null) {
if (!(mAdapter instanceof HeaderViewListAdapter)) {
//对适配器进行包装
wrapHeaderListAdapterInternal();
}
// 如果要重新添加标题视图,或在以后添加标题视图,
// 我们需要通知.
if (mDataSetObserver != null) {
mDataSetObserver.onChanged();
}
}
};
ListView.setAdapter(){
if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
} else {
mAdapter = adapter;
}
}
listView添加头布局和脚布局的实现原理大致也就这样,我们模拟list编写一个recyclerView的头部和脚部布局,先看一下效果图
包装一个RecyclerView 含有添加头部和底部布局的方法
public class WrapRecyclerView extends RecyclerView {
private ArrayList<View> mHeaderViewInfos = new ArrayList<View>();
private ArrayList<View> mFooterViewInfos = new ArrayList<View>();
private Adapter mAdapter;
public WrapRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public void addHeaderView(View view){
mHeaderViewInfos.add(view);
if (mAdapter!=null){
if (!(mAdapter instanceof HeaderViewRecyclerAdapter)){
mAdapter=new HeaderViewRecyclerAdapter(mHeaderViewInfos,mFooterViewInfos,mAdapter);
}
}
}
public void addFooterView(View view){
mFooterViewInfos.add(view);
if (mAdapter!=null){
if (!(mAdapter instanceof HeaderViewRecyclerAdapter)){
mAdapter=new HeaderViewRecyclerAdapter(mHeaderViewInfos,mFooterViewInfos,mAdapter);
}
}
}
@Override
public void setAdapter(@Nullable Adapter adapter) {
if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
mAdapter = new HeaderViewRecyclerAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
} else {
mAdapter = adapter;
}
super.setAdapter(mAdapter);
}
}
创建中间条目recyclerView的适配器
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private List<String> list;
public MyAdapter(List<String> list) {
// TODO Auto-generated constructor stub
this.list = list;
}
public class ViewHolder extends RecyclerView.ViewHolder{
public TextView tv;
public ViewHolder(View view) {
super(view);
// TODO Auto-generated constructor stub
tv = (TextView) view.findViewById(R.id.tv);
}
}
@Override
public int getItemCount() {
// TODO Auto-generated method stub
return list.size();
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.tv.setText(list.get(position));
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view=View.inflate(parent.getContext(),R.layout.recycler_item,null);
ViewHolder holder = new ViewHolder(view);
return holder;
}
}
创建一个头部和底部的适配器
public class HeaderViewRecyclerAdapter extends RecyclerView.Adapter {
private RecyclerView.Adapter mAdapter;
ArrayList<View> mHeaderViewInfos;
ArrayList<View> mFooterViewInfos;
public HeaderViewRecyclerAdapter(ArrayList<View> headerViewInfos,
ArrayList<View> footerViewInfos, RecyclerView.Adapter adapter) {
mAdapter = adapter;
if (headerViewInfos == null) {
mHeaderViewInfos = new ArrayList<View>();
} else {
mHeaderViewInfos = headerViewInfos;
}
if (footerViewInfos == null) {
mFooterViewInfos = new ArrayList<View>();
} else {
mFooterViewInfos = footerViewInfos;
}
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
//header
if(viewType==RecyclerView.INVALID_TYPE){
return new HeaderViewHolder(mHeaderViewInfos.get(0));
}else if(viewType==RecyclerView.INVALID_TYPE-1){//footer
return new HeaderViewHolder(mFooterViewInfos.get(0));
}
// Footer (off-limits positions will throw an IndexOutOfBoundsException)
return mAdapter.onCreateViewHolder(viewGroup, viewType);
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
//也要划分三个区域
int numHeaders = getHeadersCount();
if (position < numHeaders) {//是头部
return ;
}
//adapter body
final int adjPosition = position - numHeaders;
int adapterCount = 0;
if (mAdapter != null) {
adapterCount = mAdapter.getItemCount();
if (adjPosition < adapterCount) {
mAdapter.onBindViewHolder(viewHolder, adjPosition);
return ;
}
}
//footer
}
@Override
public int getItemCount() {
if (mAdapter != null){
return mFooterViewInfos.size()+mHeaderViewInfos.size()+mAdapter.getItemCount();
}else{
return mFooterViewInfos.size()+mHeaderViewInfos.size();
}
}
@Override
public int getItemViewType(int position) {
//判断当前条目是什么类型的---决定渲染什么视图给什么数据
int numHeaders = getHeadersCount();
if (position < numHeaders) {//是头部
return RecyclerView.INVALID_TYPE;
}
//正常条目部分
// Adapter
final int adjPosition = position - numHeaders;
int adapterCount = 0;
if (mAdapter != null) {
adapterCount = mAdapter.getItemCount();
if (adjPosition < adapterCount) {
return mAdapter.getItemViewType(adjPosition);
}
}
//footer部分
return RecyclerView.INVALID_TYPE-1;
}
public int getHeadersCount() {
return mHeaderViewInfos.size();
}
public int getFootersCount() {
return mFooterViewInfos.size();
}
private static class HeaderViewHolder extends RecyclerView.ViewHolder {
public HeaderViewHolder(View view) {
super(view);
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.hz.recyclerviewheaderfooter.WrapRecyclerView
android:id="@+id/rv_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</com.hz.recyclerviewheaderfooter.WrapRecyclerView>
</LinearLayout>
recycler_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="itemxxx"
android:textColor="#000"
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
private WrapRecyclerView rvView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rvView = (WrapRecyclerView) findViewById(R.id.rv_view);
// View headerView = View.inflate(this, resource, root);
TextView headerView = new TextView(this);
// TextView tv = headerView.findViewById(id);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
headerView.setLayoutParams(params);
headerView.setText("我是HeaderView");
rvView.addHeaderView(headerView);
TextView footerView = new TextView(this);
params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
footerView.setLayoutParams(params);
footerView.setText("我是FooterView");
rvView.addFooterView(footerView);
List<String> list = new ArrayList<>();
for (int i = 0; i < 30; i++) {
list.add("item "+i);
}
MyAdapter adapter = new MyAdapter(list);
rvView.setLayoutManager(new LinearLayoutManager(this));
rvView.setAdapter(adapter);
}
}
好了,简简单单的为RecyclerView添加了头部和脚部的布局。skr