3.Android RecyclerView快速添加头部和底部 删除错位

请问:简书怎么可以把代码格式调整?我贴出来换格式了。你们直接去Github下载工程!

今天开始讲RecycleView的系列教程。分割线,分组,局部刷新,动态添加,缓存原理,抖音效果,瀑布流。嵌套,动画等等



实现方案一:RecyclerView实现添加HeaderView和FooterView的核心就是在Adapter里面的onCreateViewHolder根据viewType来判断是列表项还是HeaderView来分别加载不同的布局文件。

bug:添加头部之后没法进行局部刷新。有bug

原因:添加头和尾部之后,position就变了!!!

实现方案二:  装饰设计模式:传入一个引用,让它的功能变的更强!!!!

比如:文件流读取:fileInputstream -----inpuststeeam

模仿listView添加头部和尾部

重点:

1.adapter套一层adapter

2.自定义recycleView

3.通过观察者模式监听

mAdapter =new MyRecycleViewAdapter(mList);

mRecyAttendanceAdapter =new WrapRecyclerAdapter(mAdapter);

private void initView() {

mRecyclerView = findViewById(R.id.rv_list);

    mLinearLayoutManager =new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);

    //设置布局管理器

    mRecyclerView.setLayoutManager(mLinearLayoutManager);

    mAdapter =new MyRecycleViewAdapter(mList);

    mRecyAttendanceAdapter =new WrapRecyclerAdapter(mAdapter);

    // 设置适配器

    mRecyclerView.setAdapter(mRecyAttendanceAdapter);

    // 实例化头部View

    View headerView = LayoutInflater.from(this).inflate(R.layout.header, mRecyclerView, false);

    // 添加头部

    mRecyclerView.addHeaderView(headerView);

    mAdapter.setOnItemClickListener(new RecyAttendanceAdapter.OnItemClickListener() {

@Override

        public void onItemClick(View view, int position) {

mAdapter.removeItem(position);

            Toast.makeText(MyHeadActivity.this, "position:" + position, Toast.LENGTH_LONG);

        }

@Override

        public void onItemLongClick(View view, int position) {

}

});

}

public class WrapRecyclerAdapterextends RecyclerView.Adapter {

private final static StringTAG ="WrapRecyclerAdapter";

    // 用来存放底部和头部View的集合  比Map要高效一些

    // 可以点击进入看一下官方的解释

    /**

* SparseArrays map integers to Objects.  Unlike a normal array of Objects,

* there can be gaps in the indices.  It is intended to be more memory efficient

* than using a HashMap to map Integers to Objects, both because it avoids

* auto-boxing keys and its data structure doesn't rely on an extra entry object

* for each mapping.

*/

    private SparseArraymHeaderViews;

    private SparseArraymFooterViews;

    // 基本的头部类型开始位置  用于viewType

    private static int BASE_ITEM_TYPE_HEADER =10000000;

    // 基本的底部类型开始位置  用于viewType

    private static int BASE_ITEM_TYPE_FOOTER =20000000;

    // 列表的Adapter

    private RecyclerView.AdaptermAdapter;

    public WrapRecyclerAdapter(RecyclerView.Adapter adapter) {

this.mAdapter = adapter;

        mHeaderViews =new SparseArray<>();

        mFooterViews =new SparseArray<>();

    }

@Override

    public RecyclerView.ViewHolderonCreateViewHolder(ViewGroup parent, int viewType) {

// viewType 可能就是 SparseArray 的key

        if (isHeaderViewType(viewType)) {

View headerView =mHeaderViews.get(viewType);

            return createHeaderFooterViewHolder(headerView);

        }

if (isFooterViewType(viewType)) {

View footerView =mFooterViews.get(viewType);

            return createHeaderFooterViewHolder(footerView);

        }

return mAdapter.onCreateViewHolder(parent, viewType);

    }

/**

    * 是不是底部类型

    */

    private boolean isFooterViewType(int viewType) {

int position =mFooterViews.indexOfKey(viewType);

        return position >=0;

    }

/**

    * 创建头部或者底部的ViewHolder

*/

    private RecyclerView.ViewHoldercreateHeaderFooterViewHolder(View view) {

return new RecyclerView.ViewHolder(view) {

};

    }

/**

    * 是不是头部类型

    */

    private boolean isHeaderViewType(int viewType) {

int position =mHeaderViews.indexOfKey(viewType);

        return position >=0;

    }

@Override

    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

if (isHeaderPosition(position) || isFooterPosition(position)) {

return;

        }

// 计算一下位置

        position = position -mHeaderViews.size();

        mAdapter.onBindViewHolder(holder, position);

    }

@Override

    public int getItemViewType(int position) {

if (isHeaderPosition(position)) {

// 直接返回position位置的key

            return mHeaderViews.keyAt(position);

        }

if (isFooterPosition(position)) {

// 直接返回position位置的key

            position = position -mHeaderViews.size() -mAdapter.getItemCount();

            return mFooterViews.keyAt(position);

        }

// 返回列表Adapter的getItemViewType

        position = position -mHeaderViews.size();

        return mAdapter.getItemViewType(position);

    }

/**

    * 是不是底部位置

    */

    private boolean isFooterPosition(int position) {

return position >= (mHeaderViews.size() +mAdapter.getItemCount());

    }

/**

    * 是不是头部位置

    */

    private boolean isHeaderPosition(int position) {

return position

    }

@Override

    public int getItemCount() {

// 条数三者相加 = 底部条数 + 头部条数 + Adapter的条数

        return mAdapter.getItemCount() +mHeaderViews.size() +mFooterViews.size();

    }

/**

    * 获取列表的Adapter

*/

    private RecyclerView.AdaptergetAdapter() {

return mAdapter;

    }

// 添加头部

    public void addHeaderView(View view) {

int position =mHeaderViews.indexOfValue(view);

        if (position <0) {

mHeaderViews.put(BASE_ITEM_TYPE_HEADER++, view);

        }

notifyDataSetChanged();

    }

// 添加底部

    public void addFooterView(View view) {

int position =mFooterViews.indexOfValue(view);

        if (position <0) {

mFooterViews.put(BASE_ITEM_TYPE_FOOTER++, view);

        }

notifyDataSetChanged();

    }

// 移除头部

    public void removeHeaderView(View view) {

int index =mHeaderViews.indexOfValue(view);

        if (index <0)return;

        mHeaderViews.removeAt(index);

        notifyDataSetChanged();

    }

// 移除底部

    public void removeFooterView(View view) {

int index =mFooterViews.indexOfValue(view);

        if (index <0)return;

        mFooterViews.removeAt(index);

        notifyDataSetChanged();

    }

/**

    * 解决GridLayoutManager添加头部和底部不占用一行的问题

    *

    * @param recycler

    * @version 1.0

*/

    public void adjustSpanSize(RecyclerView recycler) {

if (recycler.getLayoutManager()instanceof GridLayoutManager) {

final GridLayoutManager layoutManager = (GridLayoutManager) recycler.getLayoutManager();

            layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {

@Override

                public int getSpanSize(int position) {

boolean isHeaderOrFooter =

isHeaderPosition(position) || isFooterPosition(position);

                    return isHeaderOrFooter ?layoutManager.getSpanCount() :1;

                }

});

        }

}

}

public class WrapRecyclerViewextends RecyclerView {

// 包裹了一层的头部底部Adapter

    private WrapRecyclerAdaptermWrapRecyclerAdapter;

    // 这个是列表数据的Adapter

    private RecyclerView.AdaptermAdapter;

    public WrapRecyclerView(Context context) {

super(context);

    }

public WrapRecyclerView(Context context, @Nullable AttributeSet attrs) {

super(context, attrs);

    }

public WrapRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

    }

@Override

    public void setAdapter(Adapter adapter) {

// 为了防止多次设置Adapter

        if (mAdapter !=null) {

mAdapter.unregisterAdapterDataObserver(mDataObserver);

            mAdapter =null;

        }

this.mAdapter = adapter;

        if (adapterinstanceof WrapRecyclerAdapter) {

mWrapRecyclerAdapter = (WrapRecyclerAdapter) adapter;

        }else {

mWrapRecyclerAdapter =new WrapRecyclerAdapter(adapter);

        }

super.setAdapter(mWrapRecyclerAdapter);

        // 注册一个观察者

        mAdapter.registerAdapterDataObserver(mDataObserver);

        // 解决GridLayout添加头部和底部也要占据一行

        mWrapRecyclerAdapter.adjustSpanSize(this);

    }

// 添加头部

    public void addHeaderView(View view) {

// 如果没有Adapter那么就不添加,也可以选择抛异常提示

        // 让他必须先设置Adapter然后才能添加,这里是仿照ListView的处理方式

        if (mWrapRecyclerAdapter !=null) {

mWrapRecyclerAdapter.addHeaderView(view);

        }

}

// 添加底部

    public void addFooterView(View view) {

if (mWrapRecyclerAdapter !=null) {

mWrapRecyclerAdapter.addFooterView(view);

        }

}

// 移除头部

    public void removeHeaderView(View view) {

if (mWrapRecyclerAdapter !=null) {

mWrapRecyclerAdapter.removeHeaderView(view);

        }

}

// 移除底部

    public void removeFooterView(View view) {

if (mWrapRecyclerAdapter !=null) {

mWrapRecyclerAdapter.removeFooterView(view);

        }

}

private AdapterDataObservermDataObserver =new AdapterDataObserver() {

@Override

        public void onChanged() {

if (mAdapter ==null)return;

            // 观察者  列表Adapter更新 包裹的也需要更新不然列表的notifyDataSetChanged没效果

            if (mWrapRecyclerAdapter !=mAdapter)

mWrapRecyclerAdapter.notifyDataSetChanged();

        }

@Override

        public void onItemRangeRemoved(int positionStart, int itemCount) {

if (mAdapter ==null)return;

            // 观察者  列表Adapter更新 包裹的也需要更新不然列表的notifyDataSetChanged没效果

            if (mWrapRecyclerAdapter !=mAdapter)

mWrapRecyclerAdapter.notifyItemRemoved(positionStart);

        }

@Override

        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {

if (mAdapter ==null)return;

            // 观察者  列表Adapter更新 包裹的也需要更新不然列表的notifyItemMoved没效果

            if (mWrapRecyclerAdapter !=mAdapter)

mWrapRecyclerAdapter.notifyItemMoved(fromPosition, toPosition);

        }

@Override

        public void onItemRangeChanged(int positionStart, int itemCount) {

if (mAdapter ==null)return;

            // 观察者  列表Adapter更新 包裹的也需要更新不然列表的notifyItemChanged没效果

            if (mWrapRecyclerAdapter !=mAdapter)

mWrapRecyclerAdapter.notifyItemChanged(positionStart);

        }

@Override

        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {

if (mAdapter ==null)return;

            // 观察者  列表Adapter更新 包裹的也需要更新不然列表的notifyItemChanged没效果

            if (mWrapRecyclerAdapter !=mAdapter)

mWrapRecyclerAdapter.notifyItemChanged(positionStart,payload);

        }

@Override

        public void onItemRangeInserted(int positionStart, int itemCount) {

if (mAdapter ==null)return;

            // 观察者  列表Adapter更新 包裹的也需要更新不然列表的notifyItemInserted没效果

            if (mWrapRecyclerAdapter !=mAdapter)

mWrapRecyclerAdapter.notifyItemInserted(positionStart);

        }

};

}

}


demo地址:https://github.com/pengcaihua123456/shennandadao

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容