RecyclerView 添加头部 尾部 中间插入View 底部监听

以前写过一篇关于RecyclerView的头部尾部的添加
也有一个Demo 不过Linear Grid Stagge三个是分开的 如下图1 今天的是一个整合的DRecyclerView 如下图二 讲一下实现过程

以前的分开的

这是整合的 使用更加方便


现在的整合的

先看效果图

1.gif
  • 首先来分析最简单的 也就是LinearLayoutManager情况下的 添加头部 尾部 和中间插入
    在继承RecyclerView的使用中 两个关键方法
    onCreateViewHolder 和 onBindViewHolder
    onCreateViewHolder 创建布局View
    onBindViewHolder 绑定数据
    所以我们要在显示不同的布局上做处理 就只能在 onCreateViewHolder 创建的时候做处理
    观察 onCreateViewHolder 这个方法 通过该方法参数
    onCreateViewHolder(ViewGroup parent, int viewType) {---}
    发现 我们能做处理的 只有viewType 这个字段 看字面意思也能猜到是不同view的类型
    然后我们看源码
Paste_Image.png
Paste_Image.png

然后 getItemType(int position) 这个方法上面说了 用来返回不同的View的type 也就是说 onCreateViewHolder中的ViewType来自这个方法 默认是返回0 我们在自己的处理中重写这个方法就行了

我们就返回 当前进来的position

Paste_Image.png

然后在onCreateViewHolder中自己处理

好 现在在分析 整个类 要具备的一些条件
一个HeadViews 的集合 缓存所有的HeadView
一个FootViews的集合 缓存所有的FootView
一个中间插入的View的 集合 缓存所有的插入View[这个View就是数据列表中不同的类型做不同的布局 一般滑动很多显示一个 推荐 等等]

整个DRecyclerViewAdapater 代码如下

public class DRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
public static final String Tag = DRecyclerViewAdapter.class.getName();
private RecyclerView.Adapter mInnerAdapter;
public DRecyclerViewAdapter(DBaseRecyclerViewAdapter adapter) {
    setAdapter(adapter);
}
public void setAdapter(DBaseRecyclerViewAdapter myAdapter) {
    if (myAdapter != null) {
        if (!(myAdapter instanceof RecyclerView.Adapter))
            throw new RuntimeException("your adapter must be a RecyclerView.Adapter");
    }
    this.mInnerAdapter = myAdapter;
    myAdapter.setDRecyclerViewAdapter(this);
}

/**
 * head foot list cache
 */
private List<View> mHeadViews = new ArrayList<View>();
private List<View> mFootViews = new ArrayList<View>();
private List<View> mRandomViews = new ArrayList<View>();
private SparseArray<Integer> mRandomViews_position = new SparseArray<Integer>();

/**
 * addHead to recyclerview
 *
 * @param view
 */
public void addHeadView(View view) {
    mHeadViews.add(view);
}

/**
 * addFoot to RecyclerView
 *
 * @param view
 */
public void addFootView(View view) {
    mFootViews.add(view);
}

/**
 * 使用一次 存下来 后续 好查找
 */
private int index=0;
public void addRandomView(View view, int posistion) {
    mRandomViews_position.append(posistion,index);
    index++;
    mRandomViews.add(view);
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    DRecyclerViewHolder dRecyclerViewHolder;
    Log.e(Tag, "当前type " + viewType);
    if (viewType < mHeadViews.size()) {
        Log.e(Tag, "headView" + viewType);
        return new DRecyclerViewHolder(mHeadViews.get(viewType));

    } else if (viewType >= mHeadViews.size() && viewType < mHeadViews.size() + mInnerAdapter.getItemCount()) {

        if (mRandomViews_position.get(viewType - mHeadViews.size())!=null) {
            View view = mRandomViews.get(mRandomViews_position.get(viewType - mHeadViews.size()));
            return new DRecyclerViewHolder(view);
        }
        return mInnerAdapter.onCreateViewHolder(parent, viewType - mHeadViews.size());
    } else {
        Log.e(Tag, "FootView" + viewType);
        int position = viewType - mHeadViews.size() - mInnerAdapter.getItemCount();
        if (position >= 0 && position < mFootViews.size()) {
            return new DRecyclerViewHolder(mFootViews.get(position));
        } else {
            return null;
        }
    }
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    if (position >= mHeadViews.size() && position < mHeadViews.size() + mInnerAdapter.getItemCount()) {
        //不包括那些插入的
        if (mRandomViews_position.get(position - mHeadViews.size())==null)
            mInnerAdapter.onBindViewHolder(holder, position - mHeadViews.size());

    } else {
        /**
         * 瀑布流的设置处理
         */
        ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
        if (layoutParams instanceof StaggeredGridLayoutManager.LayoutParams) {
            ((StaggeredGridLayoutManager.LayoutParams) layoutParams).setFullSpan(true);
        }
    }
}

@Override
public int getItemCount() {
    return mHeadViews.size() + mInnerAdapter.getItemCount() + mFootViews.size();
}

@Override
public int getItemViewType(int position) {

    return position;
}

public int getFootSize() {
    return mFootViews.size();
}

public int getHeadSize() {
    return mHeadViews.size();
}

public boolean isHeader(int position) {
    return position < mHeadViews.size() ? true : false;
}

public boolean isFooter(int position) {
    return position >= mHeadViews.size() + mInnerAdapter.getItemCount() ? true : false;
}

public boolean isRandom(int position){
    Log.e("isRandom",position+"  "+mRandomViews_position.get(position-mHeadViews.size()));
    return mRandomViews_position.get(position-mHeadViews.size())!=null?true:false;
}

static class DRecyclerViewHolder extends RecyclerView.ViewHolder {

    public DRecyclerViewHolder(View itemView) {
        super(itemView);
    }
}
}

可以看到 逻辑处理在onCreateViewHolder中
根据进来的ViewType 也就是position
整套罗不难 就是根据position的位置来区分来区分属于哪个地方的View
mRandomViews_position 这个是处理中间数据不同的记录 用的SparseArray 类似Map 效率高些 节约内存 查找内部二分查找 不多介绍
目的是 记录 列表数据中的 那些位置是插入的 不同的View

上面是LinearLayoutManager的情况 属于最简单的情况

分析一下GridLayoutManager 这个 主要方法就是 这个方法 源码里面有

Paste_Image.png

设置每个Item 所占得位置 默认是1 如果你的2列 这里可以在添加头部的时候 设置为2 就会占一整行了

所以 封装一个 类 继承GridLayoutManager.SpanSizeLookup 是头部 尾部 插入位置 都占用一个大格

  /**
   * Created by Daemon on 2015/11/10.
   */
  public class DSpanSizeLookup extends GridLayoutManager.SpanSizeLookup {

private DRecyclerViewAdapter adapter;
private int mSpanSize = 1;

public DSpanSizeLookup(DRecyclerViewAdapter adapter, int spanSize) {
    this.adapter = adapter;
    this.mSpanSize = spanSize;
}

@Override
public int getSpanSize(int position) {
    boolean isHeaderOrFooterOrRandom = adapter.isHeader(position) || adapter.isFooter(position)
    || adapter.isRandom(position);
    return isHeaderOrFooterOrRandom ? mSpanSize : 1;
}
}

所以在使用的时候 设置这个就行了 其余的不变

在看看 StaggeredGridLayoutManager 这里就要用到

  LayoutParams lp = (LayoutParams)   
  view.getLayoutParams();lp.setFullSpan(true);

这种方法 来设置 不同的占用列数 重写onMeasure方法 处理

Paste_Image.png

这样就是整个的使用原理
至于其中的DBaseRecyclerViewAdapter DBaseRecyclerViewHolder 等等都是一些辅助类 更加方便使用 和添加头部 尾部 没得啥具体关系 不介绍了

MainActivity的使用代码

  public class MainActivity extends AppCompatActivity implements View.OnClickListener {

private DRecyclerViewAdapter dRecyclerViewAdapter;
private LinearLayoutManager linearLayoutManager;
private Button bt_linear;
private Button bt_grid;
private Button bt_stagge;
private MyAdpater myAdpater;
private GridLayoutManager gridLayoutManager;

private DStaggeredGridLayoutManager staggeLayoutManager;

@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.bt_linear:

            if (linearLayoutManager == null) {
                linearLayoutManager = new LinearLayoutManager(MainActivity.this);
            }
            rcv_list.setLayoutManager(linearLayoutManager);
            dRecyclerViewAdapter.notifyDataSetChanged();

            rcv_list.addOnScrollListener(new DRecyclerViewScrollListener() {
                @Override
                public void onLoadNextPage(RecyclerView view) {
                    Toast.makeText(MainActivity.this,"底部",Toast.LENGTH_SHORT).show();
                }
            });

            break;

        case R.id.bt_grid:

            if (gridLayoutManager == null) {
                gridLayoutManager = new GridLayoutManager(MainActivity.this, 2);
                gridLayoutManager.setSpanSizeLookup(new DSpanSizeLookup(dRecyclerViewAdapter, gridLayoutManager.getSpanCount()));
            }
            rcv_list.setLayoutManager(gridLayoutManager);
            dRecyclerViewAdapter.notifyDataSetChanged();
            rcv_list.addOnScrollListener(new DRecyclerViewScrollListener() {
                @Override
                public void onLoadNextPage(RecyclerView view) {
                    Toast.makeText(MainActivity.this,"底部",Toast.LENGTH_SHORT).show();
                }
            });

            break;

        case R.id.bt_stagge:

            Intent intent=new Intent(MainActivity.this,Main2Activity.class);

            startActivity(intent);

            break;

    }
}

private RecyclerView rcv_list;
private List<Data> list;

public static class Data{
    public String value;
    public int type;
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    rcv_list = (RecyclerView) findViewById(R.id.rcv_list);

    bt_linear = (Button) findViewById(R.id.bt_linear);
    bt_grid = (Button) findViewById(R.id.bt_grid);
    bt_stagge = (Button) findViewById(R.id.bt_stagge);

    list = new ArrayList<Data>();

    bt_linear.setOnClickListener(this);
    bt_grid.setOnClickListener(this);
    bt_stagge.setOnClickListener(this);
    for (int i = 0; i < 20; i++) {
        Data data=new Data();
        data.value="Daemon data " + i;
        data.type=0;

        list.add(data);
        //一种判断标示 可以根据实际需求来做标识
        if (i % 5 == 0) {
             data.type=1;
        }
    }

    myAdpater = new MyAdpater(list, this);
    dRecyclerViewAdapter = new DRecyclerViewAdapter(myAdpater);

    linearLayoutManager = new LinearLayoutManager(MainActivity.this);

    rcv_list.setLayoutManager(linearLayoutManager);

    addHeadViews();
    addFootviews();
    addRandomView();

    myAdpater.setOnClickItemListsner(new DBaseRecyclerViewAdapter.OnClickItemListsner() {
        @Override
        public void onClick(int poisiton) {
            Toast.makeText(MainActivity.this, poisiton + "", Toast.LENGTH_SHORT).show();
        }
    });

    rcv_list.setAdapter(dRecyclerViewAdapter);

    rcv_list.addOnScrollListener(new DRecyclerViewScrollListener() {
        @Override
        public void onLoadNextPage(RecyclerView view) {
            Toast.makeText(MainActivity.this,"底部",Toast.LENGTH_SHORT).show();
        }
    });
}

/**
 * 添加全部的Views 中间插入的广告栏 等等
 */
private void addRandomView() {
    for(int i=0 ; i<list.size();i++){
        if(list.get(i).type==1){
            View view = LayoutInflater.from(this).inflate(R.layout.random_item, rcv_list, false);
            dRecyclerViewAdapter.addRandomView(view,i);
        }
    }
}

/**
 * add HeadView
 */
private void addHeadViews() {
    View head = LayoutInflater.from(this).inflate(R.layout.head, rcv_list, false);
    View head1 = LayoutInflater.from(this).inflate(R.layout.head, rcv_list, false);
    dRecyclerViewAdapter.addHeadView(head);
    dRecyclerViewAdapter.addHeadView(head1);

}

/**
 * add Footview
 */
private void addFootviews() {
    View foot = LayoutInflater.from(this).inflate(R.layout.foot, rcv_list, false);
    View foot1 = LayoutInflater.from(this).inflate(R.layout.foot, rcv_list, false);

    dRecyclerViewAdapter.addFootView(foot);
    dRecyclerViewAdapter.addFootView(foot1);
}

/**
 * 继承封装过的DBaseRecyclerViewAdapter
 */
 class MyAdpater extends DBaseRecyclerViewAdapter<Data> {

    public MyAdpater(List<Data> mDatas, Context mContext) {
        super(mDatas, mContext);
    }

    @Override
    protected DBaseRecyclerViewHolder onCreateViewHolder1(ViewGroup parent, int viewType) {
        return new MyViewHoder(parent, R.layout.item, this);
    }

}

/**
 * 继承封装过的 DBaseRecyclerViewHolder
 */
class MyViewHoder extends DBaseRecyclerViewHolder<Data> implements View.OnClickListener {

    private TextView tv_content;

    public MyViewHoder(ViewGroup parent, @LayoutRes int res, DBaseRecyclerViewAdapter dBaseRecyclerViewAdapter) {
        super(parent, res, dBaseRecyclerViewAdapter);
        tv_content = $(R.id.tv_content);

        itemView.setOnClickListener(this);
    }

    @Override
    public void setData(Data data, int position) {

        //每次默认初始化 因为Stagge会改变高度
        ViewGroup.LayoutParams layoutParams1 = tv_content.getLayoutParams();
        layoutParams1.height = 80;
        tv_content.setLayoutParams(layoutParams1);

        tv_content.setText(data.value);
    }

    @Override
    public void onClick(View v) {
        if (getOnClickItemListsner() != null) {
            getOnClickItemListsner().onClick(getAdapterItemPosition());
        }
    }
}
}

放在Github上面 整个Demo

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

推荐阅读更多精彩内容