Android 高级UI2 RecyclerView添加head
2018-04-07 本文已影响389人
香沙小熊
前言
RecyclerView通过其高度的可定制性深受大家的青睐,也有非常多的使用者开始对它进行封装或者改造,从而满足越来越多的需求,广泛应用于各类App开发中。
如果你对RecyclerView不陌生的话,你一定遇到过这样的情况,我想给RecyclerView加个headerView或者footerView,当你敲出.addHeaderView,你会发现并没有添加头部或者底部View的相关API。
但是我们可以参考ListView中addHeaderView()和addFooterView()方法,在RecyclerView中实现相同功能。
1.ListView添加HeaderView和FooterView源码分析
ListView关于HeaderView和FooterView的源码
public void addHeaderView(View v) {
addHeaderView(v, null, true);
}
public void addHeaderView(View v, Object data, boolean isSelectable) {
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) {
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();
}
}
}
public void addFooterView(View v, Object data, boolean isSelectable) {
final FixedViewInfo info = new FixedViewInfo();
info.view = v;
info.data = data;
info.isSelectable = isSelectable;
mFooterViewInfos.add(info);
mAreAllItemsSelectable &= isSelectable;
// Wrap the adapter if it wasn't already wrapped.
if (mAdapter != null) {
if (!(mAdapter instanceof HeaderViewListAdapter)) {
wrapHeaderListAdapterInternal();
}
// In the case of re-adding a footer view, or adding one later on,
// we need to notify the observer.
if (mDataSetObserver != null) {
mDataSetObserver.onChanged();
}
}
}
public void addFooterView(View v) {
addFooterView(v, null, true);
}
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);
}
HeaderViewListAdapter 作用源码分析
public class HeaderViewListAdapter implements WrapperListAdapter, Filterable {
private final ListAdapter mAdapter;
// These two ArrayList are assumed to NOT be null.
// They are indeed created when declared in ListView and then shared.
ArrayList<ListView.FixedViewInfo> mHeaderViewInfos;
ArrayList<ListView.FixedViewInfo> mFooterViewInfos;
// Used as a placeholder in case the provided info views are indeed null.
// Currently only used by some CTS tests, which may be removed.
static final ArrayList<ListView.FixedViewInfo> EMPTY_INFO_LIST =
new ArrayList<ListView.FixedViewInfo>();
boolean mAreAllFixedViewsSelectable;
private final boolean mIsFilterable;
public HeaderViewListAdapter(ArrayList<ListView.FixedViewInfo> headerViewInfos,
ArrayList<ListView.FixedViewInfo> footerViewInfos,
ListAdapter adapter) {
mAdapter = adapter;
mIsFilterable = adapter instanceof Filterable;
if (headerViewInfos == null) {
mHeaderViewInfos = EMPTY_INFO_LIST;
} else {
mHeaderViewInfos = headerViewInfos;
}
if (footerViewInfos == null) {
mFooterViewInfos = EMPTY_INFO_LIST;
} else {
mFooterViewInfos = footerViewInfos;
}
mAreAllFixedViewsSelectable =
areAllListInfosSelectable(mHeaderViewInfos)
&& areAllListInfosSelectable(mFooterViewInfos);
}
public int getHeadersCount() {
return mHeaderViewInfos.size();
}
public int getFootersCount() {
return mFooterViewInfos.size();
}
public boolean isEmpty() {
return mAdapter == null || mAdapter.isEmpty();
}
private boolean areAllListInfosSelectable(ArrayList<ListView.FixedViewInfo> infos) {
if (infos != null) {
for (ListView.FixedViewInfo info : infos) {
if (!info.isSelectable) {
return false;
}
}
}
return true;
}
public boolean removeHeader(View v) {
for (int i = 0; i < mHeaderViewInfos.size(); i++) {
ListView.FixedViewInfo info = mHeaderViewInfos.get(i);
if (info.view == v) {
mHeaderViewInfos.remove(i);
mAreAllFixedViewsSelectable =
areAllListInfosSelectable(mHeaderViewInfos)
&& areAllListInfosSelectable(mFooterViewInfos);
return true;
}
}
return false;
}
public boolean removeFooter(View v) {
for (int i = 0; i < mFooterViewInfos.size(); i++) {
ListView.FixedViewInfo info = mFooterViewInfos.get(i);
if (info.view == v) {
mFooterViewInfos.remove(i);
mAreAllFixedViewsSelectable =
areAllListInfosSelectable(mHeaderViewInfos)
&& areAllListInfosSelectable(mFooterViewInfos);
return true;
}
}
return false;
}
public int getCount() {
if (mAdapter != null) {
return getFootersCount() + getHeadersCount() + mAdapter.getCount();
} else {
return getFootersCount() + getHeadersCount();
}
}
public boolean areAllItemsEnabled() {
if (mAdapter != null) {
return mAreAllFixedViewsSelectable && mAdapter.areAllItemsEnabled();
} else {
return true;
}
}
public boolean isEnabled(int position) {
// Header (negative positions will throw an IndexOutOfBoundsException)
int numHeaders = getHeadersCount();
if (position < numHeaders) {
return mHeaderViewInfos.get(position).isSelectable;
}
// Adapter
final int adjPosition = position - numHeaders;
int adapterCount = 0;
if (mAdapter != null) {
adapterCount = mAdapter.getCount();
if (adjPosition < adapterCount) {
return mAdapter.isEnabled(adjPosition);
}
}
// Footer (off-limits positions will throw an IndexOutOfBoundsException)
return mFooterViewInfos.get(adjPosition - adapterCount).isSelectable;
}
public Object getItem(int position) {
// Header (negative positions will throw an IndexOutOfBoundsException)
int numHeaders = getHeadersCount();
if (position < numHeaders) {
return mHeaderViewInfos.get(position).data;
}
// Adapter
final int adjPosition = position - numHeaders;
int adapterCount = 0;
if (mAdapter != null) {
adapterCount = mAdapter.getCount();
if (adjPosition < adapterCount) {
return mAdapter.getItem(adjPosition);
}
}
// Footer (off-limits positions will throw an IndexOutOfBoundsException)
return mFooterViewInfos.get(adjPosition - adapterCount).data;
}
public long getItemId(int position) {
int numHeaders = getHeadersCount();
if (mAdapter != null && position >= numHeaders) {
int adjPosition = position - numHeaders;
int adapterCount = mAdapter.getCount();
if (adjPosition < adapterCount) {
return mAdapter.getItemId(adjPosition);
}
}
return -1;
}
public boolean hasStableIds() {
if (mAdapter != null) {
return mAdapter.hasStableIds();
}
return false;
}
public View getView(int position, View convertView, ViewGroup parent) {
// Header (negative positions will throw an IndexOutOfBoundsException)
int numHeaders = getHeadersCount();
if (position < numHeaders) {
return mHeaderViewInfos.get(position).view;
}
// Adapter
final int adjPosition = position - numHeaders;
int adapterCount = 0;
if (mAdapter != null) {
adapterCount = mAdapter.getCount();
if (adjPosition < adapterCount) {
return mAdapter.getView(adjPosition, convertView, parent);
}
}
// Footer (off-limits positions will throw an IndexOutOfBoundsException)
return mFooterViewInfos.get(adjPosition - adapterCount).view;
}
public int getItemViewType(int position) {
int numHeaders = getHeadersCount();
if (mAdapter != null && position >= numHeaders) {
int adjPosition = position - numHeaders;
int adapterCount = mAdapter.getCount();
if (adjPosition < adapterCount) {
return mAdapter.getItemViewType(adjPosition);
}
}
return AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
}
public int getViewTypeCount() {
if (mAdapter != null) {
return mAdapter.getViewTypeCount();
}
return 1;
}
public void registerDataSetObserver(DataSetObserver observer) {
if (mAdapter != null) {
mAdapter.registerDataSetObserver(observer);
}
}
public void unregisterDataSetObserver(DataSetObserver observer) {
if (mAdapter != null) {
mAdapter.unregisterDataSetObserver(observer);
}
}
public Filter getFilter() {
if (mIsFilterable) {
return ((Filterable) mAdapter).getFilter();
}
return null;
}
public ListAdapter getWrappedAdapter() {
return mAdapter;
}
}
源码分析总结:
- 调用
HeaderView
和FooterView
都将调用wrapHeaderListAdapterInternal
使mAdapter
变为HeaderViewListAdapter
类型。 -
HeaderViewListAdapter
实际作用是对ListAdapter
进行代理操作返回的还是ListAdapter
类型。
2.参照ListView设计思路为RecyclerView添加head和footer
2.1.封装RecyclerView
public class WrapRecyclerView extends RecyclerView {
private ArrayList<View> mHeaderViewInfos = new ArrayList<>();
private ArrayList<View> mFooterViewInfos = new ArrayList<>();
private RecyclerView.Adapter mAdapter;
public WrapRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public void addHeaderView(View v) {
mHeaderViewInfos.add(v);
if (mAdapter != null) {
if (!(mAdapter instanceof HeaderViewRecyclerAdapter)) {
mAdapter = new HeaderViewRecyclerAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
}
}
}
public void addFooterView(View v) {
mFooterViewInfos.add(v);
if (mAdapter != null) {
if (!(mAdapter instanceof HeaderViewRecyclerAdapter)) {
mAdapter = new HeaderViewRecyclerAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
}
}
}
@Override
public void setAdapter(RecyclerView.Adapter adapter) {
this.mAdapter = adapter;
if (mHeaderViewInfos.size() > 0 || mFooterViewInfos.size() > 0) {
Log.d("http:","9999");
mAdapter = new HeaderViewRecyclerAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
} else {
mAdapter = adapter;
}
super.setAdapter(mAdapter);
}
}
2.2.MyAdapter
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private List<String> list;
public MyAdapter(List<String> list) {
this.list = list;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
View view = layoutInflater.inflate(R.layout.listitem, parent, false);
ViewHolder holder = new ViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.tv.setText(list.get(position));
}
@Override
public int getItemCount() {
return list.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView tv;
public ViewHolder(View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(R.id.tv);
}
}
}
2.3.HeaderViewRecyclerAdapter 代理MyAdapter
public class HeaderViewRecyclerAdapter extends RecyclerView.Adapter {
private RecyclerView.Adapter mAdapter;
private ArrayList<View> mHeaderViewInfos;
private ArrayList<View> mFooterViewInfos;
public HeaderViewRecyclerAdapter(ArrayList<View> headerViewInfos, ArrayList<View> footerViewInfos, RecyclerView.Adapter adapter) {
mAdapter = adapter;
if (headerViewInfos == null) {
mHeaderViewInfos = new ArrayList<>();
} else {
mHeaderViewInfos = headerViewInfos;
}
if (footerViewInfos == null) {
mFooterViewInfos = new ArrayList<>();
} else {
mFooterViewInfos = footerViewInfos;
}
}
@Override
public int getItemCount() {
if (mAdapter != null) {
Log.i("http:Headers", "" + getHeadersCount() + getFootersCount() + mAdapter.getItemCount());
return getHeadersCount() + getFootersCount() + mAdapter.getItemCount();
} else {
Log.i("http:", "" + getFootersCount() + getHeadersCount());
return getFootersCount() + getHeadersCount();
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, 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(holder, adjPosition);
return;
}
}
//footer
}
@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;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//header
if (viewType == RecyclerView.INVALID_TYPE) {
return new HeaderViewHolder(mHeaderViewInfos.get(0));
} else if (viewType == RecyclerView.INVALID_TYPE - 1) {
return new HeaderViewHolder(mFooterViewInfos.get(0));
}
return mAdapter.onCreateViewHolder(parent, viewType);
}
private int getHeadersCount() {
return mHeaderViewInfos.size();
}
private int getFootersCount() {
return mFooterViewInfos.size();
}
private static class HeaderViewHolder extends RecyclerView.ViewHolder {
public HeaderViewHolder(View itemView) {
super(itemView);
}
}
}
2.4.MainActivity
public class MainActivity extends AppCompatActivity {
WrapRecyclerView recyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = (WrapRecyclerView) findViewById(R.id.recyclerView);
TextView headerView = new TextView(this);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
headerView.setLayoutParams(params);
headerView.setText("我是HeaderView");
recyclerView.addHeaderView(headerView);
TextView footerView = new TextView(this);
params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
footerView.setLayoutParams(params);
footerView.setText("我是FooterView");
recyclerView.addFooterView(footerView);
List<String> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add("item " + i);
}
MyAdapter adapter = new MyAdapter(list);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
}
}
2.5.布局文件
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.haocai.wraprecyclerview.WrapRecyclerView
android:id="@+id/recyclerView"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</RelativeLayout>
listitem.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="40dp"
android:orientation="vertical" >
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>