<Android 基础(四)> RecyclerView

来源:互联网 发布:淘宝免费推广方式 编辑:程序博客网 时间:2024/06/06 03:57

介绍

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中onBindViewHolderholder.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/9541326

3 0