美文网首页
MaterialDesign-RecyleView探究及使用【三

MaterialDesign-RecyleView探究及使用【三

作者: heiyl | 来源:发表于2018-06-07 08:54 被阅读0次

    概述

    1、目的:为RecyclerView添加头部和底部视图

    2、分析:RecyclerView在使用的过程中,没有发现想ListView中addHeadView()、addFooterView()的方法

    3、实现思路:模仿ListView的方式实现,研究ListView的添加头部和底部视图的实现源码,依照同样的模式实现

    ListView添加头部和底部的功能源码解析

    1、查看一下ListView的addHeaderView()方法:

    从如下的方法中查看关键信息,会看到mHeaderViewInfos.add(info);就是将传入的参数view通过mHeaderViewInfos进行存储,存放要添加的headerview

    接着看到一个判断语句 if (!(mAdapter instanceof HeaderViewListAdapter)),这个判断中有一个方法wrapHeaderListAdapterInternal();进去看到的是mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, mAdapter);

    意思是如果这个mAdapte不是HeaderViewListAdapter,那么就把它转化成一个HeaderViewListAdapter,同时把这个原本的mAdapter通过构造方法传递到HeaderViewListAdapter这个类中,传入adapter的方式肯定是setAdapter方法,接着看setAdapter

    addHeaderView源码:

    publicvoidaddHeaderView(View v, Object data,booleanisSelectable){

    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.");

    }

    }

    finalFixedViewInfo info =newFixedViewInfo();

    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(!(mAdapterinstanceofHeaderViewListAdapter)) {

    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();

    }

    }

    }

    protectedvoidwrapHeaderListAdapterInternal(){

    mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, mAdapter);

    }

    2、查看一下ListView的setAdapter()方法:

    从这个方法看到如下关键代码块:

    if(mHeaderViewInfos.size() >0|| mFooterViewInfos.size() >0) {

    mAdapter= wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter);

    }else{

    mAdapter= adapter;

    }

    就是说setAdapter会先去判断是否你前面添加过headerView或者添加过footerView,以此来判断应该给你转化为一个HeaderViewListAdapter还是直接给你 mAdapter = adapter。

    这里体现了偷梁换柱的概念,就是说你添加了headerView或者footerView就用一个新的Adapter来替代你传入的adapter,这个在设计模式上使用了装饰设计模式,接下来查看一下HeaderViewListAdapter

    setAdapter源码:

    @Override

    publicvoidsetAdapter(ListAdapter adapter){

    if(mAdapter !=null&& mDataSetObserver !=null) {

    mAdapter.unregisterDataSetObserver(mDataSetObserver);

    }

    resetList();

    mRecycler.clear();

    if(mHeaderViewInfos.size() >0|| mFooterViewInfos.size() >0) {

    mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter);

    }else{

    mAdapter = adapter;

    }

    mOldSelectedPosition = INVALID_POSITION;

    mOldSelectedRowId = INVALID_ROW_ID;

    // AbsListView#setAdapter will update choice mode states.

    super.setAdapter(adapter);

    if(mAdapter !=null) {

    mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();

    mOldItemCount = mItemCount;

    mItemCount = mAdapter.getCount();

    checkFocus();

    mDataSetObserver =newAdapterDataSetObserver();

    mAdapter.registerDataSetObserver(mDataSetObserver);

    mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());

    intposition;

    if(mStackFromBottom) {

    position = lookForSelectablePosition(mItemCount -1,false);

    }else{

    position = lookForSelectablePosition(0,true);

    }

    setSelectedPositionInt(position);

    setNextSelectedPositionInt(position);

    if(mItemCount ==0) {

    // Nothing selected

    checkSelectionChanged();

    }

    }else{

    mAreAllItemsSelectable =true;

    checkFocus();

    // Nothing selected

    checkSelectionChanged();

    }

    requestLayout();

    }

    3、查看HeaderViewListAdapter关键源码:

    分析看到getView()方法有三处判断,分别是返回了三种视图,头部视图、内容视图、底部视图;发现头部视图和底部视图专门处理,内容视图直接复用了传入的adapter的getView()方法。

    publicViewgetView(intposition, View convertView, ViewGroup parent){

    // Header (negative positions will throw an IndexOutOfBoundsException)

    intnumHeaders = getHeadersCount();

    if(position < numHeaders) {

    returnmHeaderViewInfos.get(position).view;

    }

    // Adapter

    finalintadjPosition = position - numHeaders;

    intadapterCount =0;

    if(mAdapter !=null) {

    adapterCount = mAdapter.getCount();

    if(adjPosition < adapterCount) {

    returnmAdapter.getView(adjPosition, convertView, parent);

    }

    }

    // Footer (off-limits positions will throw an IndexOutOfBoundsException)

    returnmFooterViewInfos.get(adjPosition - adapterCount).view;

    }

    publicintgetItemViewType(intposition){

    intnumHeaders = getHeadersCount();

    if(mAdapter !=null&& position >= numHeaders) {

    intadjPosition = position - numHeaders;

    intadapterCount = mAdapter.getCount();

    if(adjPosition < adapterCount) {

    returnmAdapter.getItemViewType(adjPosition);

    }

    }

    returnAdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;

    }

    综上分析,要实现RecyleView添加头部和底部功能,

    1、需要自定义一个RecyleView,添加addHeaerView()、addFooterView()

    2、其次自定义一个HeaderViewRecyleViewAdapter,用于实现添加头部和底部视图的展示

    自定义RecyleView实现WrapRecyleView类:

    publicclassWrapRecyleViewextendsRecyclerView{

    privateArrayList mHeaderViewInfos =newArrayList<>();

    privateArrayList  mFooterViewInfos =newArrayList<>();

    privateAdapter mAdapter;

    publicWrapRecyleView(Context context,AttributeSet attrs){

    super(context, attrs);

    }

    publicvoidaddHeaderView(View v){

    mHeaderViewInfos.add(v);

    // Wrap the adapter if it wasn't already wrapped.

    if(mAdapter !=null) {

    if(!(mAdapterinstanceofHeaderViewRecyleViewAdapter)) {

    mAdapter =newHeaderViewRecyleViewAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);

    }

    }

    }

    publicvoidaddFooterView(View v){

    mFooterViewInfos.add(v);

    // Wrap the adapter if it wasn't already wrapped.

    if(mAdapter !=null) {

    if(!(mAdapterinstanceofHeaderViewRecyleViewAdapter)) {

    mAdapter =newHeaderViewRecyleViewAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);

    }

    }

    }

    @Override

    publicvoidsetAdapter(Adapter adapter){

    if(mHeaderViewInfos.size() >0|| mFooterViewInfos.size() >0) {

    mAdapter =newHeaderViewRecyleViewAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);

    }else{

    mAdapter = adapter;

    }

    super.setAdapter(mAdapter);

    }

    }

    HeaderViewRecyleViewAdapter类实现:

    publicclassHeaderViewRecyleViewAdapterextendsRecyclerView.Adapter{

    privateArrayList mHeaderViewInfos;

    privateArrayList  mFooterViewInfos;

    privateRecyclerView.Adapter mAdapter;

    publicHeaderViewRecyleViewAdapter(ArrayList headerViewInfos, ArrayList footerViewInfos, RecyclerView.Adapter adapter){

    mAdapter = adapter;

    if(headerViewInfos ==null) {

    mHeaderViewInfos =newArrayList<>();

    }else{

    mHeaderViewInfos = headerViewInfos;

    }

    if(footerViewInfos ==null) {

    mFooterViewInfos =newArrayList<>();

    }else{

    mFooterViewInfos = footerViewInfos;

    }

    }

    @NonNull

    @Override

    publicRecyclerView.ViewHolderonCreateViewHolder(@NonNull ViewGroup parent,intviewType){

    //header

    if(viewType==RecyclerView.INVALID_TYPE){

    returnnewHeaderViewHolder(mHeaderViewInfos.get(0));

    }elseif(viewType==RecyclerView.INVALID_TYPE-1){//footer

    returnnewHeaderViewHolder(mFooterViewInfos.get(0));

    }

    // Footer (off-limits positions will throw an IndexOutOfBoundsException)

    returnmAdapter.onCreateViewHolder(parent, viewType);

    }

    @Override

    publicvoidonBindViewHolder(@NonNull RecyclerView.ViewHolder holder,intposition){

    //划分头部、正常部分、尾部

    intnumHeaders = getHeadersCount();

    //头部

    if(position < numHeaders) {

    return;

    }

    //adapter bod正常部分

    finalintadjPosition = position - numHeaders;

    intadapterCount =0;

    if(mAdapter !=null) {

    adapterCount = mAdapter.getItemCount();

    if(adjPosition < adapterCount) {

    mAdapter.onBindViewHolder(holder, adjPosition);

    return;

    }

    }

    //footer

    }

    @Override

    publicintgetItemCount(){

    if(mAdapter !=null) {

    returngetFootersCount() + getHeadersCount() + mAdapter.getItemCount();

    }else{

    returngetFootersCount() + getHeadersCount();

    }

    }

    @Override

    publicintgetItemViewType(intposition){

    //判断当前条目是什么类型,就渲染什么视图给什么数据

    intnumHeaders = getHeadersCount();

    //头部

    if(position < numHeaders) {

    returnRecyclerView.INVALID_TYPE;

    }

    //正常条目部分

    // Adapter

    finalintadjPosition = position - numHeaders;

    intadapterCount =0;

    if(mAdapter !=null) {

    adapterCount = mAdapter.getItemCount();

    if(adjPosition < adapterCount) {

    returnmAdapter.getItemViewType(adjPosition);

    }

    }

    //footer部分

    returnRecyclerView.INVALID_TYPE-1;

    }

    privatestaticclassHeaderViewHolderextendsRecyclerView.ViewHolder{

    publicHeaderViewHolder(View view){

    super(view);

    }

    }

    publicintgetHeadersCount(){

    returnmHeaderViewInfos.size();

    }

    publicintgetFootersCount(){

    returnmFooterViewInfos.size();

    }

    }

    接下来就是应用,需要使用WrapRecyleView 来代替RecyleView ,其它都是正常的实现

    setContentView(R.layout.activity_rvheader);

    recyclerView = (WrapRecyleView) findViewById(R.id.recyclerView);

    addHeaderView();

    addFooterView();

    List list =newArrayList<>();

    for(inti =0; i <6; i++) {

    list.add("item "+i);

    }

    MyAdapter adapter =newMyAdapter(list);

    recyclerView.setLayoutManager(newLinearLayoutManager(this));

    recyclerView.setAdapter(adapter);

    效果图如下:

    效果图如下:

    源码地址:https://github.com/heiyl/recyleview

    微信公众号:

    图注:Android进化之路公众号

    相关文章

      网友评论

          本文标题:MaterialDesign-RecyleView探究及使用【三

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