RecycleView简单使用

本文详细介绍RecyclerView的基本使用流程,包括设置布局管理器、Adapter等,并提供HeadView与FootView的封装方法,以及如何去除滑动结束时的下拉动画。此外还讲解了复杂布局的简化技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简介

A flexible view for providing a limited window into a large data set.

一个展示大量数据、灵活的View控件。

个人理解为ListView、GridView的官方升级版本,将ViewHolder、复用itemView等ListView优化方案吸收,更加灵活、解耦的展示大量数据。

2017.11.20 增加推荐点击事件处理、HeadView+FootView的封装.

基本使用流程

1.导包

使用RecycleView需要引入v7包对应。

// 版本号跟v7包版本号一致就行
compile 'com.android.support:recyclerview-v7:xx.x.x'

2.设置布局管理器

RecycleView.setLayoutManager(LayoutManager);

自带的三个布局管理器:

StaggeredGridLayoutManager : 流式布局管理器

A LayoutManager that lays out children in a staggered grid formation.
It supports horizontal & vertical layout as well as an ability to layout children in reverse..

以交错网格的形式布局,支持水平和垂直布局,同时支持反向。

LinearLayoutManager:线性布局管理器

similar functionality to {@link android.widget.ListView}

官方解释:功能和ListView类似。。。

GridLayoutManager:表格布局管理器

By default, each item occupies 1 span. You can change it by providing a custom
{@link SpanSizeLookup} instance via {@link #setSpanSizeLookup(SpanSizeLookup)}.

默认每个Item占据一个跨度(span:表格的列数既定之后,每个item的宽度),但是你可以setSpanSizeLookup()方法修改每个item的跨度。

功能类似于GridView,继承了LinearLayoutManager,但是官方的解释上面是重点,通过上面的功能能够实现更加灵活的表格布局,将往常只能嵌套使用的布局在不嵌套的情况展示。

3.设置Adapter

RecycleView.setAdapter();

adapter必须重写方法如下:

new RecyclerView.Adapter() {

    /**
     * 根据viewType返回想要的ViewHolder,强行绑定ListView中ViewHolder模式。
     *
     * 每个Item都会调用仅且调用一次,即每个Item的ViewHolder对象都不被其他Item(即使类型相同)复用。
     * 这里与ListView使用ViewHolder有所区别。
     *
     * 当Item需要被复用,调用方法{@link #onBindViewHolder(RecyclerView.ViewHolder, int)}
     */
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item1, parent, false);
        RecyclerViewAdapter1.ViewHolder viewHolder = new RecyclerViewAdapter1.ViewHolder(view);
        return viewHolder;
    }

    /**
     * 根据加载位置进行数据展示
     * 当Item被展示到屏幕上时调用,进行Item展示、更新
     */
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    }

    /**
     * 返回总的Item数量
     */
    @Override
    public int getItemCount() {
        return 0;
    }
});

4. 添加分割线

RecycleView.addItemDecoration(RecyclerView.ItemDecoration);

以前添加分割线的方法不再有效。

android:divider="#ff0000"
android:dividerHeight="1dp"

自定义分割线

new RecyclerView.ItemDecoration() {
    /**
     * 调用在绘制Item之前
     * 一般情况两个Draw方法选一个重写即可
     */
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
    }

    /**
     * 调用在绘制Item之后
     */
    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
    }

    /**
     * 设置偏移量
     */
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        // 对Item进行偏移
        outRect.set(0,0,0,8);
    }
});

5. 设置动画

RecyclerView.setItemAnimator(new DefaultItemAnimator());

这里使用默认的系统动画。

同时调用如下方法进行Item的add和remove:

public void addItem(int position, String s){
    arrayList.add(position,s);
    notifyItemInserted(position);
}

public void remove(int position){
    arrayList.remove(position);
    notifyItemRemoved(position);
}

6.点击事件

方式一:由于RecycleView没有直接给出点击事件的接口,可以将事件监听接口放到Adapter中,在布局容器进行实现,容器中调用就好了,这里不做实现。

方式二(推荐使用):RxJava PublishSubject实现监听事件的传递。

没有使用RxJava不能使用!!!

adapter实现:

private final PublishSubject<Model> onClickSubject = PublishSubject.create();

/**
* 点击事件 进行订阅
*
* @author fengzhen
* @version v6.3.0, 2017/11/18 17:43
*/
public PublishSubject<Model> getOnClickSubject() {
   return onClickSubject;
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    Model model = mListData.get(position);
    ViewHolder viewHolder = (ViewHolder) holder;
    viewHolder.mTvContent.setOnClickListener(v -> onClickSubject.onNext(model));
}

外部订阅:

mXXXAdapter.getOnClickSubject().subscribe(new DefaultSubscriber<Model>() {
            @Override
            public void onNext(Modeldata) {
                // 点击事件处理
                showShortToast(data.getString());
            }
        });

好,就酱!

7.HeadView + FootView

从ListView到RecyclerView怎么能把很常用的HeadView、FootView给忘了。
这里直接贴封装的一个简单Adapter,使用直接继承My Adapter 就可以直接用了。

import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;

/**
 * 扩展RecyclerView.Adapter
 * 添加HeadView、FootView
 *
 * @author fengzhen
 * @version v6.3.0, 2017/11/20
 */
public abstract class RecyclerViewExtendAdapter extends RecyclerView.Adapter {
    public static final int ITEM_TYPE_HEADVIEW = 999;
    public static final int ITEM_TYPE_FOOTVIEW = 998;

    private View mHeadview;
    private View mFootview;

    private boolean hasHeadView = false;

    public void setHeadview(View headview) {
        hasHeadView = headview != null;
        this.mHeadview = headview;
        notifyDataSetChanged();
    }

    public void setFootview(View footview) {
        this.mFootview = footview;
        notifyDataSetChanged();
    }

    public View getmHeadview() {
        return mHeadview;
    }

    public View getmFootview() {
        return mFootview;
    }

    /**
     * 不可重写,
     * 重写方法{@link RecyclerViewExtendAdapter#getNormalItemViewType}实现具体子类操作
     *
     * @author fengzhen
     * @version v6.3.0, 2017/11/20 15:57
     */
    @Override
    public final int getItemViewType(int position) {
        if (mHeadview != null && position < 1) {
            return ITEM_TYPE_HEADVIEW;
        } else if (mFootview != null && position == getItemCount() - 1) {
            return ITEM_TYPE_FOOTVIEW;
        } else {
            return getNormalItemViewType(hasHeadView ? position - 1 : position);
        }
    }

    /**
     * 子类实现此方法,等同于直接实现{@link RecyclerView.Adapter#getItemViewType(int)} ()}
     *
     * @return The view type of this ViewHolder.
     * @author fengzhen
     * @version v6.3.0, 2017/11/20 15:52
     */
    protected int getNormalItemViewType(int position) {
        return super.getItemViewType(position);
    }

    /**
     * 不可重写
     * 子类实现{@link RecyclerViewExtendAdapter#getNormalItemCount()}
     *
     * @author fengzhen
     * @version v6.3.0, 2017/11/20 16:10
     */
    @Override
    public final int getItemCount() {
        return getNormalItemCount() + (mHeadview == null ? 0 : 1) + (mFootview == null ? 0 : 1);
    }

    /**
     * 子类实现此方法,等同于实现{@link RecyclerView.Adapter#getItemCount()}
     *
     * @author fengzhen
     * @version v6.3.0, 2017/11/20 16:06
     */
    protected abstract int getNormalItemCount();

    /**
     * 不可重写
     * 子类实现{@link RecyclerViewExtendAdapter#onNormalCreateViewHolder(ViewGroup, int)}
     *
     * @author fengzhen
     * @version v6.3.0, 2017/11/20 16:10
     */
    @Override
    public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        switch (viewType) {
            case ITEM_TYPE_HEADVIEW:
                return new HeadViewHolder(mHeadview);
            case ITEM_TYPE_FOOTVIEW:
                return new FootViewHolder(mFootview);
            default:
                return onNormalCreateViewHolder(parent, viewType);
        }
    }

    /**
     * 子类实现此方法,等同于实现{@link RecyclerView.Adapter#onCreateViewHolder(ViewGroup, int)}
     *
     * @author fengzhen
     * @version v6.3.0, 2017/11/20 16:06
     */
    protected abstract RecyclerView.ViewHolder onNormalCreateViewHolder(ViewGroup parent, int viewType);

    /**
     * 不可重写
     * 子类实现{@link RecyclerViewExtendAdapter#onNormalBindViewHolder(RecyclerView.ViewHolder, int, int)}
     *
     * @author fengzhen
     * @version v6.3.0, 2017/11/20 16:10
     */
    @Override
    public final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        int itemViewType = getItemViewType(position);
        switch (itemViewType) {
            case ITEM_TYPE_HEADVIEW:
            case ITEM_TYPE_FOOTVIEW:
                break;
            default:
                onNormalBindViewHolder(holder, hasHeadView ? position - 1 : position, itemViewType);
                break;
        }
    }

    /**
     * 子类实现此方法,等同于实现{@link RecyclerView.Adapter#onBindViewHolder(RecyclerView.ViewHolder, int)}
     *
     * @author fengzhen
     * @version v6.3.0, 2017/11/20 16:06
     */
    protected abstract void onNormalBindViewHolder(RecyclerView.ViewHolder holder, int position, int itemViewType);

    private class HeadViewHolder extends RecyclerView.ViewHolder {
        public HeadViewHolder(View itemView) {
            super(itemView);
        }
    }

    private class FootViewHolder extends RecyclerView.ViewHolder {
        public FootViewHolder(View view) {
            super(view);
        }
    }
}

7.去掉滑动收尾的下拉动画

设置属性:
android:overScrollMode="never"

复杂布局层数简化

1. 布局要求

实现如下的布局:

布局要求

2. 布局解析

实现方式一: 直接硬布局,最外层用一个ScrollView,里面用 TextView + ListView + TextView + GridView。还需要对ListView和GridView进行重写测量方法,才能实现效果。

实现方式二: 整个作为一个ListView,Title、ListView 、GridView都看成是父ListView 的一个Item。但是对于其中的交互逻辑的代码不要控制,写出来肯定很不“优美”。

实现方式三: 使用RecycleView的GridLayoutManager布局,通过item type 设置,将Title、ListVite item和GridView item都作为RecycleView 的item,使用一层布局,这也是推荐的做法。

3. 布局实现

实现布局的重点在于GridLayoutManager的setSpanSizeLookup()方法。

设置布局管理器:

// 设置布局管理器
private void initLayout() {
    // 这里的4,取所需列数的最小公倍数
    mRecyclerView1.setLayoutManager(new GridLayoutManager(this, 4));
}

Adapter实现:

/**
 * Created by fengzhen.
 * 
 * recyclerView 适配器
 */
public class RecyclerViewAdapter1 extends RecyclerView.Adapter<RecyclerViewAdapter1.ViewHolder> {
    // 布局类型
    public static final int TITLE_1 = 0x0;
    public static final int TITLE_2 = 0x1;
    public static final int LINEAR_1 = 0x2;
    public static final int GRID_2 = 0x3;

    // 数据集
    private List<String> arrayList;

    public RecyclerViewAdapter1(List<String> list) {
        arrayList = list;
    }

    /**
     * 查找item布局,强制绑定ViewHolder
     */
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // 没做类型判定 需要时判断 viewType
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item1, parent, false);
        return new ViewHolder(view);
    }

    /**
     * 根据加载位置进行数据展示
     */
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        // 没做类型判定 需要时判断 viewType
        holder.textView1.setText(arrayList.get(position));
    }

    /**
     * 实现布局的重点
     */
    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);

        /**
         * 处理GridLayoutManager布局
         * LayoutManager列数设置为 所有可能出现的列数的最小公倍数
         * 具体的列数,返回在总列数的比例
         *  例:可能出现的列数有:  1、3、4、7
         *      LayoutManager的列数设置为 : 1*3*4*7 = 84;
         *      对应列数的返回值: 84、 28、21、12;
         */
        RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
        if(manager instanceof GridLayoutManager) {
            final GridLayoutManager gridManager = ((GridLayoutManager) manager);
            gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                @Override
                public int getSpanSize(int position) {
                    int type = getItemViewType(position);
                    switch (type){
                        case TITLE_1:
                        case TITLE_2:
                            return 4;
                        case LINEAR_1:
                            return 1;
                        case GRID_2:
                            return 2;
                        default:
                            return 4;
                    }
                }
            });
        }
    }

    /**
     * 返回不同的类型
     *
     * @author fengzhen
     * @version v1.0, 2017/8/23 15:47
     */
    @Override
    public int getItemViewType(int position) {
        if (position == 0) {
            return TITLE_1;
        } else if (position < 8) {
            return LINEAR_1;
        } else if (position == 8) {
            return TITLE_2;
        } else {
            return GRID_2;
        }
    }

    /**
     * 类似listview中的方法,获取总item数
     */
    @Override
    public int getItemCount() {
        return arrayList.size();
    }

    /**
     * 继承RecycleView的ViewHolder,对item控件进行初始化
     */
    public static class ViewHolder extends RecyclerView.ViewHolder {
        public TextView textView1;

        public ViewHolder(View itemView) {
            super(itemView);
            textView1 = (TextView) itemView.findViewById(R.id.textView1);
        }
    }
}

4. 最终效果展示

最终效果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值