zoukankan      html  css  js  c++  java
  • <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/9541326

  • 相关阅读:
    深入剖析Java中的装箱和拆箱
    JDBC(1)
    设计模式
    MySQL学习笔记(3)
    MySQL学习笔记(2)
    MySQL学习笔记(1)
    tomcat 部署项目出现Error thrown in preDeregister method
    JSP页面中的request.getContextPath()出现“ .... .. refers to the missing type String
    myEclipse 导入 jquery包为什么整个项目都会报错
    走楼梯
  • 原文地址:https://www.cnblogs.com/lanzhi/p/6467202.html
Copyright © 2011-2022 走看看