<Android 基础(四)> RecyclerView

介绍

RecyclerView是ListView的豪华增强版。它主要包含以下几处新的特性,如ViewHolder,ItemDecorator,LayoutManager,SmothScroller以及增加或删除item时item动画等。官方推荐我们采用RecyclerView来取代ListView。

相对优势

  • ViewHolder

    ListView需要自己实现ViewHolder来提高性能,或者不使用ViewHolder,但是使用ViewHolder来绑定对象是一个很好的习惯。RecyclerView很好的帮我们解决了这个问题,RecyclerView.ViewHolder在使用RecyclerView过程中必须实现,因为它是一个抽象类无法直接创建,需要自己完成对应子类的建立然后使用


  • LayoutManager

    ListView只能在垂直方向上滚动,不支持其他的滚动方式,当然开发者有很多自定义的方式完成这些功能,这里就不做争辩,从设计的角度上看,ListView设计之初应该就没有想过让它完成这些复杂的功能,只是为了单纯的列表显示。但是RecyclerView相较于ListView,在滚动上面的功能扩展了许多。它可以支持多种类型列表的展示要求,主要如下:

    GridLayoutManager ,支持网格展示,可以水平或者竖直滚动,如展示图片的画廊。

    LinearLayoutManager ,可以支持水平和竖直方向上滚动的列表。

    StaggeredGridLayoutManager ,可以支持交叉网格风格的列表,类似于瀑布流或者Pinterest。


  • ItemAnimation

    ItemAnimation是RecyclerView中子项在增加,删除或者移动的情况下显示的动画效果,Google越来越重视用户体验,从属性动画的推出开始,这就是一个趋势,开发者在这里可以自己实现自己想要添加的动画效果,当然,如果你是个懒汉,请使用new DefaultItemAnimator()


  • ItemDecoration

    ItemDecoration,名字起的很文艺,子项的装饰,RecyclerView在默认情况下并不在item之间展示间隔符。如果你想要添加间隔符,你必须使用RecyclerView.ItemDecoration类来实现。懒汉请使用DividerItemDecoration.java

Recycler示例

实际效果图

上面这个效果图是使用的StaggeredGridLayoutManager

代码层面

布局文件

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/root_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@color/colorMainBackground">

    <include layout="@layout/toolbar"></include>

    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".model.View.MainActivity">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/rv_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

        </android.support.v7.widget.RecyclerView>

        <android.support.design.widget.FloatingActionButton
            android:id="@+id/fab_add"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|right"
            android:layout_marginBottom="30dp"
            android:layout_marginRight="30dp"
            android:src="@drawable/addone" />

        <android.support.design.widget.FloatingActionButton
            android:id="@+id/fab_del"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|left"
            android:layout_marginBottom="30dp"
            android:layout_marginLeft="30dp"
            android:src="@drawable/delone" />
    </FrameLayout>
</LinearLayout>

recycler_item.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

        <android.support.v7.widget.CardView
            android:layout_margin="10dp"
            android:id="@+id/cv_bg"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:cardCornerRadius="20dp"
            app:cardElevation="5dp">
                <TextView
                    android:id="@+id/tv_name"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:gravity="center" />

        </android.support.v7.widget.CardView>

</FrameLayout>

主Activity

public class MainActivity extends AppCompatActivity {
    @Bind(R.id.rv_content)
    RecyclerView rvContent;
    @Bind(R.id.fab_add)
    FloatingActionButton fabAdd;
    @Bind(R.id.fab_del)
    FloatingActionButton fabDel;
    @Bind(R.id.root_layout)
    LinearLayout rootLayout;

    private LayoutManager mLayoutManager; //LayoutManager
    private RVAdapter recyclerAdapter;    //RecyclerView对应的Adapter
    private ArrayList<String> mContentList; //内容list这里只是使用了字符串,当然也可以替换成其他的JavaBean类
    private Random mRandom = new Random(); //用于产生随机字符串
    private int mSum = 50; //初始化子项数目

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);  //ButterKnife注入,减少代码负担

        mContentList = new ArrayList<String>();
        for (int i = 0; i < mSum; i++) {
            mContentList.add(getRandomString());
        } //随机生成数据
        recyclerAdapter = new RVAdapter(mContentList);
        mLayoutManager = new StaggeredGridLayoutManager(3, LinearLayoutManager.VERTICAL); //3列纵向
        rvContent.setAdapter(recyclerAdapter);
        rvContent.setLayoutManager(mLayoutManager);
        rvContent.setItemAnimator(new DefaultItemAnimator()); //设置动画效果,可以看下上面的效果图,动画效果还是比较明显的
    }

    @OnClick({R.id.fab_add, R.id.fab_del})
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.fab_add:
                recyclerAdapter.addData(1); //加一个
                makeSnackBar(rootLayout, "添加一个 :)", null, null);//显示一个Snackbar
                break;
            case R.id.fab_del:
                recyclerAdapter.removeData(1); //去掉一个
                makeSnakeBar(rootLayout, "删除一个 :(", null, null); //显示一个Snackbar
                break;
            default:
                break;
        }
    }
    //用于产生随机字符串的方法
    public String getRandomString() {
        String src = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
        StringBuilder dst = new StringBuilder(4);
        for (int i = 0; i < 4; i++) {
            dst.append(src.charAt(mRandom.nextInt(62)));
        }
        return dst.toString();
    }

    //构建一个Snackbar并显示出来
    private void makeSnackBar(View view, String message, String buttonText, View.OnClickListener onClickListener) {
        Snackbar.make(view, message, Snackbar.LENGTH_SHORT)
                .setAction(buttonText, onClickListener)
                .show();
    }
}

适配器

public class RVAdapter extends RecyclerView.Adapter<RVAdapter.MyViewHolder>{
    ArrayList<String> mContentList;
    Random mRandom = new Random();
    //ViewHolder继承自RecyclerView.ViewHolder  子View拿到方便后面访问
    public class MyViewHolder  extends RecyclerView.ViewHolder {
        TextView textView;
        CardView cardView;
        public MyViewHolder(View itemView) {
            super(itemView);
            textView = (TextView) itemView.findViewById(R.id.tv_name);
            cardView = (CardView) itemView.findViewById(R.id.cv_bg);
        }
    }
    public RVAdapter(ArrayList<String> mContentList) {
        this.mContentList = mContentList;
    }
    //创建ViewHolder,由于RecyclerView.ViewHolder是一个抽象类无法实例化,所以必须实现一个子类才能使用,这里自己尝试的过程中走了一些弯路,注意inflate最后一个参数设置成false不然可能会出现crash
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_item, parent, false);
        return new MyViewHolder(view);
    }

    //onBindView  用于设置需要显示的View中的内容和一些属性值
    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        holder.cardView.setCardBackgroundColor(getRandomColor());
        holder.textView.setText(mContentList.get(position));
        holder.itemView.getLayoutParams().height = getRandomHeight(200,400);//产生随机高度,看上去像瀑布
    }

    @Override
    public int getItemCount() {
        return mContentList.size();
    }

    //产生随机颜色
    private int getRandomColor() {
        return (0xff000000|mRandom.nextInt(0x00ffffff));
    }
    //产生随机高度
    private int getRandomHeight(int min , int max) {
        return (mRandom.nextInt(max - min) + min );
    }
    //添加一个子项
    public void addData(int position) {
        mContentList.add(position, "Insert One");
        notifyItemInserted(position);//不调用这个没有动画效果
    }
    //删除一个子项
    public void removeData(int position) {
        mContentList.remove(position);
        notifyItemRemoved(position);//不调用这个没有动画效果
    }
}

需要注意的几个地方:

1. 实现自己的ViewHolder继承recyclerView,ViewHolder,因为抽象类不能实例化

2. inflate子项的时候,最后一个参数设置成false

3. 动画效果需要在Adapter中调用notifyItem***方法才行

StaggeredGridLayoutManager的效果图上面已经有显示了


其他效果

LinearLayoutManager效果图

对应修改代码

mLayoutManager = new LinearLayoutManager(this);
rvContent.addItemDecoration(new DividerItemDecoration(this, StaggeredGridLayoutManager.VERTICAL));



GridLayoutManager效果图

对应修改代码

mLayoutManager = new GridLayoutManager(this, 3);//3列
rvContent.addItemDecoration(new DividerItemDecoration(this, StaggeredGridLayoutManager.VERTICAL));

StaggeredGridLayoutManager.HORIZONTAL横向的效果图

对应修改代码

mLayoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.HORIZONTAL);

RVAdapter.java中onBindViewHolder
holder.itemView.getLayoutParams().width = getRandomHeight(200,400);

关于ItemDecoration

这里附上Google Sample中的DividerItemDecoration.java代码 希望可以帮助到大家

public class DividerItemDecoration extends RecyclerView.ItemDecoration {

    private static final int[] ATTRS = new int[]{
            android.R.attr.listDivider
    };
    public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
    public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
    private Drawable mDivider;
    private int mOrientation;

    public DividerItemDecoration(Context context, int orientation) {
        final TypedArray a = context.obtainStyledAttributes(ATTRS);
        mDivider = a.getDrawable(0);
        a.recycle();
        setOrientation(orientation);
    }

    public void setOrientation(int orientation) {
        if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
            throw new IllegalArgumentException("invalid orientation");
        }
        mOrientation = orientation;
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent) {
        if (mOrientation == VERTICAL_LIST) {
            drawVertical(c, parent);
        } else {
            drawHorizontal(c, parent);
        }

    }

    public void drawVertical(Canvas c, RecyclerView parent) {
        final int left = parent.getPaddingLeft();
        final int right = parent.getWidth() - parent.getPaddingRight();

        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int top = child.getBottom() + params.bottomMargin +
                    Math.round(ViewCompat.getTranslationY(child));
            final int bottom = top + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    public void drawHorizontal(Canvas c, RecyclerView parent) {
        final int top = parent.getPaddingTop();
        final int bottom = parent.getHeight() - parent.getPaddingBottom();

        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int left = child.getRight() + params.rightMargin +
                    Math.round(ViewCompat.getTranslationX(child));
            final int right = left + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    @Override
    public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
        if (mOrientation == VERTICAL_LIST) {
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
        } else {
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
        }
    }
}

备注

Android源码中有很多关于这些View的使用方法,大家可以查阅并参考使用。

博文中部分代码:

http://download.csdn.net/detail/poorkick/9541001

时间: 2024-09-27 20:16:25

<Android 基础(四)> RecyclerView的相关文章

Android零基础入门第64节:揭开RecyclerView庐山真面目

大家还记得之前在第38期~第50期都在学习列表控件吗,其中用了8期讲ListView的使用,相信都已经掌握好了吧.那么本期一起来学习Android 5.X新增的一个列表组件,那就是RecyclerView的使用. 一.RecyclerView概述 从前面的学习我们知道,ListView的功能非常强大,几乎绝大部分应用程序都会使用到,虽然也学会一些方法技巧来提升ListView的效率,但其性能还是不是很完美. 另外ListView的可扩展性相对来说比较弱,以前要实现每个列表项的高度不同的界面,或者

RecyclerView动态添加、删除及点击事件

上一节讲解了RecyclerView的三种显示方式,本节将主要研究一下RecyclerView的动态添加.删除及其单击和长按事件的处理.我们在上一节代码的基础上进行相关操作. 一.修改适配器类MyAdapter,加入添加和删除这两个方法: public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> { private Context context; private List<String>

安卓易学,爬坑不易——腾讯老司机的RecyclerView局部刷新爬坑之路

针对手游的性能优化,腾讯WeTest平台的Cube工具提供了基本所有相关指标的检测,为手游进行最高效和准确的测试服务,不断改善玩家的体验.目前功能还在免费开放中. 点击地址:http://wetest.qq.com/cube立即体验! 作者:Hoolly,腾讯移动客户端开发工程师. 商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处 WeTest导读 安卓开发者都知道,RecyclerView比ListView要灵活的多,但不可否认的里面的坑也同样埋了不少人.下面让我们看看腾讯开发工程

RecyclerVIew 下拉刷新上拉加载

步骤: 寻找RecyclerView 上拉刷新下拉加载开源库 加入到项目中 解决整合进来的bug 待续...

【Android】RecyclerView详解(一)

1.介绍 RecyclerView是比 ListView 更高级且更具灵活性的组件. 此组件是一个用于显示庞大数据集的容器,可通过保持有限数量的视图进行非常有效的滚动操作. 如果您有数据集合,其中的元素将因用户操作或网络事件而发生改变,请使用 RecyclerView 小组件. RecyclerView使用起来很方便因为它: 提供了一种插拔式的体验,高度的解耦,异常的灵活使用; 显示的样式更丰富包括水平,竖直,Grid,瀑布显示方式; 可以通过ItemDecoration自定义Item间的间隔;

【FastDev4Android框架开发】实例解析之SwipeRefreshLayout+RecyclerView+CardView(三十五)

转载请标明出处: http://blog.csdn.net/developer_jiangqq/article/details/50087873 本文出自:[江清清的博客] (一).前言: 作为Android L开始,Google更新了新控件RecyclerView和CardView,这两个控件在之前的文章中已经做了详细介绍和使用,同时在前面还对下拉刷新组件SwipeRefreshLayout进行相关讲解.本来该专题不在更新了,正好昨天有一个群友问到了怎么样结合SwipeRefreshLayou

Android RecyclerView设置item间距

RecyclerView没有可以直接设置间距的属性,但可以用ItemDecoration来装饰一个item,所以继承重写ItemDecoration就可以实现间距了 public class SpaceItemDecoration extends RecyclerView.ItemDecoration{ private int space; public SpaceItemDecoration(int space) { this.space = space; } @Override public

《android recyclerview的简单使用》

首先是主要布局 <android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/swipeRefreshLayout" android:layout_width="match_parent" android:layout_height="match_pa

明日之星——RecyclerView(一)

在Listview中使用了很久的viewholder.setTag.getTag来实现Item复用后, Google大大终于看不下去了.推出了明日之星RecyclerView,现已加入support.v7豪华套餐.顾名思义,RecyclerView本身是不管怎么布局的.它本身只负责,加载看得见的viewHolder,释放看不见的viewHolder.这一功能就代替掉了,ListView中麻烦的Item复用设计.而对于布局的控制,RecyclerView则依赖于LayoutManager.下面,我

RecyclerView使用

RecyclerView是android5.0提供的新组件(最新的support.v7中也提供了该组件),类似于ListView,但是比ListView更灵活.更先进,我觉得主要表现在以下几个方面: 1.  把ViewHolder的实现封装起来,规范了ViewHolder,把item的view写入ViewHolder中,通过复用ViewHolder来实现view的复用. 2.  RecyclerView.Adapter中把view的回收和内容改变等操作分开解耦了,比传统的Adapter更为灵活.