AndroidRecyclerView详解

Posted 寒小枫

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AndroidRecyclerView详解相关的知识,希望对你有一定的参考价值。

这里写图片描述

1.介绍

RecyclerView是比 ListView 更高级且更具灵活性的组件。 此组件是一个用于显示庞大数据集的容器,可通过保持有限数量的视图进行非常有效的滚动操作。 如果您有数据集合,其中的元素将因用户操作或网络事件而发生改变,请使用 RecyclerView 小组件。

RecyclerView使用起来很方便因为它:

  • 提供了一种插拔式的体验,高度的解耦,异常的灵活使用;
  • 显示的样式更丰富包括水平,竖直,Grid,瀑布显示方式;
  • 可以通过ItemDecoration自定义Item间的间隔;
  • 可以通过ItemAnimator自定义Item增、删动画(也可设置默认动画);
  • 代码内聚不需要手动创建ViewHolder;

2.基本使用

  • 首先要用这个控件,你需要在gradle文件中添加包的引用
compile 'com.android.support:recyclerview-v7:23.4.0'

  • 在xml中添加RecyclerView组件:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    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="test.wangkeke.com.androidrecyclerviewdemo.NormalActivity">

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

</RelativeLayout>
  • 构造适配器Adapter:
public class NormalRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
{
    private Context context;

    private final LayoutInflater mLayoutInflater;

    private String[] mTitles;

    public NormalRecyclerViewAdapter(Context context)
    {
        mTitles = context.getResources().getStringArray(R.array.sports);
        this.context = context;
        mLayoutInflater = LayoutInflater.from(context);
    }


    /**
     * 导入布局文件
     * @param parent
     * @param viewType
     * @return
     */
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
    {
        return new NormalTextViewHolder(mLayoutInflater.inflate(R.layout.item,parent,false));
    }

    /**
     * 绑定数据
     * @param holder
     * @param position
     */
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
    {
        ((NormalTextViewHolder)holder).text.setText(mTitles[position]);
        //将数据保存在itemView的Tag中,以便点击时进行获取
        ((NormalTextViewHolder)holder).layout.setTag(mTitles[position]);
    }

    @Override
    public int getItemCount()
    {
        return mTitles.length;
    }

    public class NormalTextViewHolder extends  RecyclerView.ViewHolder
    {
        private TextView text;

        private RelativeLayout layout;

        public NormalTextViewHolder(View itemView)
        {
            super(itemView);
            text = (TextView) itemView.findViewById(R.id.text);

            layout = (RelativeLayout) itemView.findViewById(R.id.layout);

            layout.setOnClickListener(new View.OnClickListener()
            {
                @Override
                public void onClick(View v)
                {
                    Toast.makeText(context, ""+v.getTag(), Toast.LENGTH_SHORT).show();
                }
            });

        }
    }
}

.RecyclerView.Adapter主要用于处理数据集合并负责绑定视图;ViewHolder持有item所有的用于绑定数据的View;

  • item.xml布局,就一个textView:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                    android:id="@+id/cardview"
                                    android:layout_width="match_parent"
                                    android:layout_height="wrap_content"
                                    android:layout_margin="0dp"
                                    >
    <RelativeLayout
        android:id="@+id/layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="10dp">
        <TextView
            android:id="@+id/text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:textSize="18sp"
            android:textStyle="bold"/>
    </RelativeLayout>
</LinearLayout>
  • activity中进行简单的设置:
recyclerView = (RecyclerView) findViewById(R.id.my_recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
adapter = new NormalRecyclerViewAdapter(this);
recyclerView.setAdapter(adapter);
  • 效果图:

这里写图片描述

recyclerview提供这些内置的布局管理器:

  • inearlayoutmanager 显示垂直滚动列表或水平的项目。
  • gridlayoutmanager 显示在一个网格项目。
  • staggeredgridlayoutmanager 显示在交错网格项目。

- 设置layoutmanager为gridlayoutmanager时,效果如下:

这里写图片描述

Staggeredgridlayoutmanager 这个效果大家可以自己试试,后面也会介绍;

2.添加分割线

突然发现列表出来是出来了,可是,没有分割线啊,不要惊慌,recyclerView需要单独处理分割线,提供了下面这个方法用来设置分割线的风格:

recyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL_LIST));

下面是官方给的分割线的例子,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);
            RecyclerView v = new RecyclerView(
                    parent.getContext());
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int top = child.getBottom() + params.bottomMargin;
            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;
            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);
        }
    }
}

效果图:

这里写图片描述

3.加载不同的View

开始之前,这里要注意的就是函数onCreateViewHolder(ViewGroup parent, int viewType)这里的第二个参数就是View的类型,可以根据这个类型判断去创建不同item的ViewHolder。我们可以通过重写 getItemViewType方法是用来获取当前项Item(position参数)是哪种类型的布局。

本例子结合cardview来实现,对cardview不熟悉的同学可以看下cardview讲解

  • 主xml布局很简单,就一个RecyclerView组件:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    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="test.wangkeke.com.androidrecyclerviewdemo.NormalActivity">

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

</RelativeLayout>
  • item布局文件item_image.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
                                    xmlns:app="http://schemas.android.com/apk/res-auto"
                                    android:id="@+id/cardview"
                                    android:layout_width="match_parent"
                                    android:layout_height="wrap_content"
                                    android:layout_margin="0dp"
                                    app:cardBackgroundColor="@android:color/white"
                                    app:cardCornerRadius="5dp"
                                    app:cardElevation="5dp"
                                    app:contentPadding="5dip">

    <LinearLayout
        android:id="@+id/layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_horizontal"
        android:orientation="vertical">


        <ImageView
            android:id="@+id/image"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:scaleType="fitXY"
            android:layout_centerInParent="true"/>

        <TextView
            android:id="@+id/image_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/image"
            android:layout_marginTop="10dp"
            android:text="test"
            android:textStyle="bold"
            android:gravity="center"/>

    </LinearLayout>
</android.support.v7.widget.CardView>
  • 关键的adapter代码:

public class TypeRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
{
    private String imageOne = "http://5.133998.com/2014/pic/000/363/18107c4b46aa8a182776746ff43e49bf.jpg";
    private String imageTwo = "http://img3.imgtn.bdimg.com/it/u=3954782107,4019560836&fm=21&gp=0.jpg";

    private boolean flag = false;

    public static enum ITEM_TYPE {
        ITEM_TYPE_IMAGE,
        ITEM_TYPE_TEXT
    }

    private Context context;

    private final LayoutInflater mLayoutInflater;

    private String[] mTitles;

    public TypeRecyclerViewAdapter(Context context)
    {
        mTitles = context.getResources().getStringArray(R.array.sports);
        this.context = context;
        mLayoutInflater = LayoutInflater.from(context);
    }


    /**
     * 导入布局文件
     * @param parent
     * @param viewType
     * @return
     */
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
    {
        if (viewType == ITEM_TYPE.ITEM_TYPE_IMAGE.ordinal()) {
            return new ImageViewHolder(mLayoutInflater.inflate(R.layout.item_image, parent, false));
        } else {
            return new NormalTextViewHolder(mLayoutInflater.inflate(R.layout.item_card, parent, false));
        }
    }


    /**
     * 绑定数据
     * @param holder
     * @param position
     */
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
    {
        //判断holder的类型,来显示不同的View
        if (holder instanceof NormalTextViewHolder) {
            ((NormalTextViewHolder) holder).text.setText(mTitles[position]);
            //将数据保存在itemView的Tag中,以便点击时进行获取
            ((NormalTextViewHolder) holder).layout.setTag(mTitles[position]);
        } else if (holder instanceof ImageViewHolder) {
            ((ImageViewHolder)holder).textView.setText(mTitles[position]);
            //将数据保存在itemView的Tag中,以便点击时进行获取
            ((ImageViewHolder)holder).layout.setTag(mTitles[position]);

            /**
             * 交叉加载图片,测试用
             */
            if(flag)
            {
                ImageLoader.getInstance().displayImage(imageOne,((ImageViewHolder)holder).imageView);
                flag = !flag;
            }
            else
            {
                ImageLoader.getInstance().displayImage(imageTwo,((ImageViewHolder)holder).imageView);
                flag = !flag;
            }
        }
    }

    @Override
    public int getItemCount()
    {
        return mTitles.length;
    }

    @Override
    public int getItemViewType(int position) {
        //返回类型标记
        return position % 2 == 0 ? ITEM_TYPE.ITEM_TYPE_IMAGE.ordinal() : ITEM_TYPE.ITEM_TYPE_TEXT.ordinal();
    }

    /**
     * 纯textView布局holder
     */
    public class NormalTextViewHolder extends  RecyclerView.ViewHolder
    {
        private TextView text;

        private RelativeLayout layout;

        public NormalTextViewHolder(View itemView)
        {
            super(itemView);
            text = (TextView) itemView.findViewById(R.id.text);

            layout = (RelativeLayout) itemView.findViewById(R.id.layout);

            layout.setOnClickListener(new View.OnClickListener()
            {
                @Override
                public void onClick(View v)
                {
                    Toast.makeText(context, ""+v.getTag(), Toast.LENGTH_SHORT).show();
                }
            });

        }
    }

    /**
     * 带图片的布局holder
     */
    public class ImageViewHolder extends  RecyclerView.ViewHolder
    {
        private TextView textView;

        private ImageView imageView;

        private LinearLayout layout;

        public ImageViewHolder(View itemView)
        {
            super(itemView);
            imageView = (ImageView) itemView.findViewById(R.id.image);
            textView = (TextView) itemView.findViewById(R.id.image_text);
            layout = (LinearLayout) itemView.findViewById(R.id.layout);

            layout.setOnClickListener(new View.OnClickListener()
            {
                @Override
                public void onClick(View v)
                {
                    Toast.makeText(context, ""+v.getTag(), Toast.LENGTH_SHORT).show();
                }
            });
        }
    }
}
  • Activity中进行绑定和显示:
recyclerView = (RecyclerView) findViewById(R.id.type_recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
adapter = new TypeRecyclerViewAdapter(this);
recyclerView.setAdapter(adapter);
  • 效果图:

这里写图片描述

4.插入删除动画

RecyclerView提供了设置动画的方法,我们只需要通过setItemAnimator方法即可设置Item插入和删除的动画;

//添加默认动画
recyclerView.setItemAnimator(new DefaultItemAnimator());

之前的数据源用的是个String数组,无法添加数据,转为list后添加;

/**
     * 添加数据
     * @param content
     * @param position
     */
    public void addItem(String content, int position) {
        titleList.add(position,content);
        notifyItemInserted(position); //Attention!
    }

    /**
     * 删除数据
     * @param content
     */
    public void removeItem(String content) {
        int position = titleList.indexOf(content);
        titleList.remove(position);
        notifyItemRemoved(position);//Attention!
    }
  • 效果图:

这里写图片描述

基本使用先介绍这么多,欢迎看官批评讨论!

源码下载


参考连接:

1.RecyclerView使用详解

2.RecyclerView点滴

3.RecyclerView使用介绍

以上是关于AndroidRecyclerView详解的主要内容,如果未能解决你的问题,请参考以下文章

详解Android WebView加载html片段

Android RecyclerView使用方法详解

androidrecyclerview+GalleryLayoutManager 实现广告画廊效果

14.VisualVM使用详解15.VisualVM堆查看器使用的内存不足19.class文件--文件结构--魔数20.文件结构--常量池21.文件结构访问标志(2个字节)22.类加载机制概(代码片段

Python中verbaim标签使用详解

AndroidRecyclerView中的Item嵌套RecyclerView使用实例