美文网首页
View-RecyclerView支持添加头尾View的Adap

View-RecyclerView支持添加头尾View的Adap

作者: isLJli | 来源:发表于2020-05-05 23:35 被阅读0次

    ListView中可以通过以下这两个方法设置头部和尾部的view,我们研究listView的源码来为rv添加同样的方法。

    listView.addHeaderView();
    listView.addFooterView();
    

    添加头部

    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;
          //1.添加到头部集合
          mHeaderViewInfos.add(info);
          mAreAllItemsSelectable &= isSelectable;
    
          // Wrap the adapter if it wasn't already wrapped.
          //2.适配器不为null
          if (mAdapter != null) {
              if (!(mAdapter instanceof HeaderViewListAdapter)) {
                  //3.在原来的Adapter上进行一层包装
                  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();
              }
          }
      }
    
    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);
      }
    
    public void addFooterView(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;
        //1. 添加到尾部集合
          mFooterViewInfos.add(info);
          mAreAllItemsSelectable &= isSelectable;
    
          // Wrap the adapter if it wasn't already wrapped.
          //2.适配器不为null
          if (mAdapter != null) {
              if (!(mAdapter instanceof HeaderViewListAdapter)) {
                 //3.在原来的Adapter上进行一层包装
                  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 class HeaderViewListAdapter implements WrapperListAdapter, Filterable {
    
      @UnsupportedAppUsage
      private final ListAdapter mAdapter;
    
      // These two ArrayList are assumed to NOT be null.
      // They are indeed created when declared in ListView and then shared.
      @UnsupportedAppUsage
      ArrayList<ListView.FixedViewInfo> mHeaderViewInfos;
      @UnsupportedAppUsage
      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;
      }
    }
    

    大致原理:在原来的mAdapter的基础上,分别向头部和尾部添加view集合,然后重新计算和返回位置和大小等。

    2.实现可添加首尾部的基类Adapter

    思路:在基类的adapter中增加HeaderAndFooterAdapter属性,如果用户想添加首尾view则设置为HeaderAndFooterAdapter,如果不添加则直接设置为Adapter。

    public abstract class RecyclerViewAdapter<M, VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {
    
      protected List<M> mDataList = new ArrayList<>();
      private HeaderAndFooterAdapter mHeaderAndFooterAdapter;
      private Context mContext;
    
      public RecyclerViewAdapter(Context context) {
          this.mContext = context;
      }
    
      @Override
      public int getItemCount() {
          return mDataList.size();
      }
    
      /**
       * @return 获取指定索引位置的数据模型
       */
      public M getItem(int position) {
          if (mDataList != null && position >= 0 && mDataList.size() > position) {
              return mDataList.get(position);
          } else {
              return null;
          }
      }
    
      /**
       * @return 获取数据集合
       */
      public List<M> getDataList() {
          return mDataList;
      }
    
      /**
       * @return 数据列表是否为空
       */
      public boolean isDataListEmpty() {
          return (mDataList == null || mDataList.size() <= 0);
      }
    
      /**
       * @param data 在指定位置添加新的数据
       */
      public void addData(M data, int position) {
          if (data != null && mDataList.size() >= position) {
              mDataList.add(position, data);
              notifyDataSetChangedWrapper();
          }
      }
    
      /**
       * @param data 在集合尾部添加新的数据
       */
      public void addMoreData(M data) {
          if (data != null) {
              mDataList.add(data);
              notifyDataSetChangedWrapper();
          }
      }
    
      /**
       * @param list 在集合尾部添加更多数据集合
       */
      public void addMoreData(List<M> list) {
          if (list != null && list.size() >= 0) {
              int positionStart = mDataList.size();
              mDataList.addAll(positionStart, list);
              notifyItemRangeInsertedWrapper(positionStart, list.size());
          }
      }
    
      /**
       * @param list 在集合头部部添加更多数据集合
       */
      public void addHeadData(List<M> list) {
          if (list != null && list.size() >= 0) {
              mDataList.addAll(0, list);
              notifyItemRangeInsertedWrapper(0, list.size());
          }
      }
    
      /**
       * @param data 设置全新的数据集合,如果传入空(null或空表),则清空数据列表
       *             (第一次从服务器加载数据,或者下拉刷新当前界面数据表)
       */
      public void setData(List<M> data) {
          mDataList.clear();
          if (data != null && data.size() >= 0) {
              mDataList.addAll(data);
          }
          notifyDataSetChangedWrapper();
      }
    
      /**
       * 清空数据列表
       */
    
      public void clear() {
          mDataList.clear();
          notifyDataSetChangedWrapper();
      }
    
      public Context getContext() {
          return mContext;
      }
    
      private void notifyItemRangeInsertedWrapper(int positionStart, int itemCount) {
          if (mHeaderAndFooterAdapter == null) {
              notifyItemRangeInserted(positionStart, itemCount);
          } else {
              mHeaderAndFooterAdapter.notifyItemRangeInserted(mHeaderAndFooterAdapter.getHeadersCount() + positionStart, itemCount);
          }
      }
    
      private void notifyItemRangeChangedWrapper(int positionStart, int itemCount) {
          if (mHeaderAndFooterAdapter == null) {
              notifyItemRangeChanged(positionStart, itemCount);
          } else {
              mHeaderAndFooterAdapter.notifyItemRangeChanged(mHeaderAndFooterAdapter.getHeadersCount() + positionStart, itemCount);
          }
      }
    
      //刷新数据区域,即不刷Header和Footer
      protected void notifyDataItemsChanged() {
          try {
              notifyItemRangeChangedWrapper(0, getItemCount());
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
    
      public final void notifyDataSetChangedWrapper() {
          //如果没有头尾view的Adapter,则直接更新
          if (mHeaderAndFooterAdapter == null) {
              notifyDataSetChanged();
          } else {
              //否则,要通知头尾view的Adapter更新
              mHeaderAndFooterAdapter.notifyDataSetChanged();
          }
      }
    
      public final void notifyItemChangedWrapper(int position) {
          if (mHeaderAndFooterAdapter == null) {
              notifyItemChanged(position);
          } else {
              mHeaderAndFooterAdapter.notifyItemChanged(mHeaderAndFooterAdapter.getHeadersCount() + position);
          }
      }
    
      public void addHeaderView(View headerView) {
          getHeaderAndFooterAdapter().addHeaderView(headerView);
      }
    
      public void addFooterView(View footerView) {
          getHeaderAndFooterAdapter().addFooterView(footerView);
      }
    
      public int getHeadersCount() {
          return mHeaderAndFooterAdapter == null ? 0 : mHeaderAndFooterAdapter.getHeadersCount();
      }
    
      public int getFootersCount() {
          return mHeaderAndFooterAdapter == null ? 0 : mHeaderAndFooterAdapter.getFootersCount();
      }
    
      public HeaderAndFooterAdapter getHeaderAndFooterAdapter() {
          if (mHeaderAndFooterAdapter == null) {
              synchronized (RecyclerViewAdapter.this) {
                  if (mHeaderAndFooterAdapter == null) {
                      mHeaderAndFooterAdapter = new HeaderAndFooterAdapter(this);
                  }
              }
          }
          return mHeaderAndFooterAdapter;
      }
    }
    

    可添加首尾view的Adapter

    public class HeaderAndFooterAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    
      private static final int BASE_ITEM_TYPE_HEADER = 2048;
      private static final int BASE_ITEM_TYPE_FOOTER = 4096;
    
      private SparseArrayCompat<View> mHeaderViews = new SparseArrayCompat<>();
      private SparseArrayCompat<View> mFooterViews = new SparseArrayCompat<>();
      private RecyclerView.Adapter mAdapter;
    
      public HeaderAndFooterAdapter(@NonNull RecyclerView.Adapter adapter) {
          mAdapter = adapter;
      }
    
      @NonNull
      @Override
      public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
          if (mHeaderViews.get(viewType) != null) {
              return new RecyclerView.ViewHolder(mHeaderViews.get(viewType)) {
              };
          } else if (mFooterViews.get(viewType) != null) {
              return new RecyclerView.ViewHolder(mFooterViews.get(viewType)) {
              };
          } else {
              return mAdapter.onCreateViewHolder(parent, viewType);
          }
    
      }
    
      @Override
      public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
          //如果是header 或 footer就不绑定数据
          if (isHeaderViewOrFooterView(position)) {
              return;
          }
    
          mAdapter.onBindViewHolder(holder, getRealItemPosition(position));
      }
    
      @Override
      public int getItemViewType(int position) {
          if (isHeaderView(position)) {
              return mHeaderViews.keyAt(position);
          } else if (isFooterView(position)) {
              return mFooterViews.keyAt(position - getHeadersCount() - getRealItemCount());
          } else {
              return mAdapter.getItemViewType(getRealItemPosition(position));
          }
      }
    
      @Override
      public int getItemCount() {
          return getHeadersCount() + getFootersCount() + getRealItemCount();
      }
    
      @Override
      public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
          mAdapter.onAttachedToRecyclerView(recyclerView);
    
          RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
    
          if (layoutManager instanceof GridLayoutManager) {
              final GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
              final GridLayoutManager.SpanSizeLookup spanSizeLookup = gridLayoutManager.getSpanSizeLookup();
    
              gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                  @Override
                  public int getSpanSize(int position) {
                      if (isHeaderViewOrFooterView(position)) {
                          return gridLayoutManager.getSpanCount();
                      } else {
                          if (spanSizeLookup != null) {
                              return spanSizeLookup.getSpanSize(position);
                          }
                      }
                      return 1;
                  }
              });
          }
      }
    
      @Override
      public void onViewAttachedToWindow(@NonNull RecyclerView.ViewHolder holder) {
          mAdapter.onViewAttachedToWindow(holder);
          int position = holder.getLayoutPosition();
          if (isHeaderViewOrFooterView(position)) {
              ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
              if (lp != null && lp instanceof StaggeredGridLayoutManager.LayoutParams) {
                  StaggeredGridLayoutManager.LayoutParams p = (StaggeredGridLayoutManager.LayoutParams) lp;
                  p.setFullSpan(true);
              }
          }
      }
    
      private boolean isHeaderViewOrFooterView(int position) {
          return isHeaderView(position) || isFooterView(position);
      }
    
      private int getRealItemPosition(int position) {
          return position - getHeadersCount();
      }
    
      public boolean isHeaderView(int position) {
          return position < getHeadersCount();
      }
    
      public boolean isFooterView(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);
      }
    
      public int getRealItemCount() {
          return mAdapter.getItemCount();
      }
    
      public int getHeadersCount() {
          return mHeaderViews.size();
      }
    
      public int getFootersCount() {
          return mFooterViews.size();
      }
    }
    

    使用

    public class MainActivity extends AppCompatActivity {
    
      private RecyclerView mRecyclerView;
      private RvAdapter mAdapter;
      private List<String> mData = new ArrayList<>();
    
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
          for (int i = 0; i < 23; i++) {
              mData.add("" + i);
          }
          mRecyclerView = findViewById(R.id.rv);
          mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
          mAdapter = new RvAdapter(this);
          View header = LayoutInflater.from(this).inflate(R.layout.header_layout,mRecyclerView,false);
          View footer = LayoutInflater.from(this).inflate(R.layout.footer_layout,mRecyclerView,false);
          mAdapter.setData(mData);
          mAdapter.addHeaderView(header);
         // mAdapter.addHeaderView(footer);
          mAdapter.addFooterView(footer);
    
          //如果添加了首尾view,则设置这个adapter
          mRecyclerView.setAdapter(mAdapter.getHeaderAndFooterAdapter());
    
          // 没有添加首尾view,则直接adapter
          // mRecyclerView.setAdapter(mAdapter);
    
      }
    }
    

    效果

    image.png image.png

    代码地址

    相关文章

      网友评论

          本文标题:View-RecyclerView支持添加头尾View的Adap

          本文链接:https://www.haomeiwen.com/subject/nrlyghtx.html