zoukankan      html  css  js  c++  java
  • RecyclerView 详解


    概述
    RecyclerView出现已经有一段时间了,相信大家肯定不陌生了,大家可以通过导入support-v7对其进行使用。 
    据官方的介绍,该控件用于在有限的窗口中展示大量数据集,其实这样功能的控件我们并不陌生,例如:ListView、GridView。
    那么有了ListView、GridView为什么还需要RecyclerView这样的控件呢?
    整体上看RecyclerView架构,提供了一种插拔式的体验,高度的解耦,异常的灵活,通过设置它提供的不同LayoutManager,ItemDecoration , ItemAnimator实现令人瞠目的效果。
    • 你想要控制其显示的方式,请通过布局管理器LayoutManager
    • 你想要控制Item间的间隔(可绘制),请通过ItemDecoration
    • 你想要控制Item增删的动画,请通过ItemAnimator
    • 你想要控制点击、长按事件,请自己写!

    RecyclerView基本的使用代码:
        mRecyclerView = findView(R.id.id_recyclerview);
        mRecyclerView.setLayoutManager(layout);//设置布局管理器
        mRecyclerView.setAdapter(adapter)//设置adapter
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());//设置Item增加、移除动画
        mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.HORIZONTAL_LIST));//添加分割线  

    相比较于ListView的代码,ListView可能只需要去设置一个adapter就能正常使用了。而RecyclerView基本需要上面一系列的步骤,那么为什么会添加这么多的步骤呢?
    那么就必须解释下RecyclerView的这个名字了,从它类名上看,RecyclerView代表的意义是,我只管Recycler View,也就是说RecyclerView只管回收与复用View,其他的你可以自己去设置。可以看出其高度的解耦,给予你充分的定制自由(所以你才可以轻松的通过这个控件实现ListView,GirdView,瀑布流等效果)。

    分割线 ItemDecoration
    RecyclerView并没有支持divider这样的属性,你可以给Item的布局去设置margin、background等方式来间接添加分割线,但这种方式不够优雅,我们的分割线可以在代码中通过以下方法添加。
        mRecyclerView.addItemDecoration() 
    该方法的参数为RecyclerView.ItemDecoration,该类为抽象类,官方目前并没有提供默认的实现类。 
    public static abstract class ItemDecoration {
        public void onDraw(Canvas c, RecyclerView parent, State state) {
            onDraw(c, parent);
        }
        public void onDrawOver(Canvas c, RecyclerView parent, State state) {
            onDrawOver(c, parent);
        }
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
            getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(), parent);
        }
        @Deprecated
        public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
            outRect.set(0, 0, 0, 0);
        }
    }

    若我们调用mRecyclerView.addItemDecoration()方法添加分割线,则RecyclerView在绘制的时候,会绘制Decoration,即调用该类的onDraw和onDrawOver方法:
    • onDraw 方法在drawChildren之前
    • onDrawOver在drawChildren之后,一般我们选择复写其中一个即可。
    • getItemOffsets 可以通过outRect.set()为每个Item设置一定的偏移量,主要用于绘制Decorator。

    代码 Activity




    public class MainActivity extends ActionBarActivity implements MyOnItemClickLitener {
        private RecyclerView mRecyclerView;
        private List<String> mDatas;//数据
        private List<Integer> mHeights;//高度
        private MyRecyclerViewAdapter mRecyclerViewAdapter;//适配器
        private MyStaggeredAdapter mStaggeredAdapter;
        private ItemDecoration decoration1;//分割线
        private ItemDecoration decoration2;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            mRecyclerView = new android.support.v7.widget.RecyclerView(this);
            setContentView(mRecyclerView);
            initData();
            initAdapter();
            initRecylerView();
        }
        protected void initData() {
            mDatas = new ArrayList<String>();
            for (int i = 'A'; i < 'z'; i++) {
                mDatas.add("" + (char) i);
            }
            mHeights = new ArrayList<Integer>();
            for (int i = 0; i < mDatas.size(); i++) {
                mHeights.add((int) (100 + Math.random() * 500));
            }
        }
        private void initAdapter() {
            mRecyclerViewAdapter = new MyRecyclerViewAdapter(thismDatas);
            mStaggeredAdapter = new MyStaggeredAdapter(thismDatasmHeights);
            mRecyclerViewAdapter.setOnItemClickLitener(this);
            mStaggeredAdapter.setOnItemClickLitener(this);
        }
        private void initRecylerView() {
            decoration1 = new DividerItemDecoration(this, 0);
            decoration2 = new DividerItemDecoration(this, 1);
            mRecyclerView.setPadding(10, 10, 10, 10);
            mRecyclerView.setAdapter(mRecyclerViewAdapter);//设置adapter
            mRecyclerView.setLayoutManager(new GridLayoutManager(this, 3));//设置布局管理器
            mRecyclerView.addItemDecoration(decoration1);//添加一个分割线
            mRecyclerView.addItemDecoration(decoration2);//还可以再添加一个分割线
            mRecyclerView.setItemAnimator(new DefaultItemAnimator());//设置Item增加、移除动画。github上有很多动画效果,如RecyclerViewItemAnimators
        }
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            getMenuInflater().inflate(R.menu.main, menu);
            return super.onCreateOptionsMenu(menu);
        }
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            mRecyclerView.removeItemDecoration(decoration1);//移除分割线,即使没有显示也可以移除
            mRecyclerView.removeItemDecoration(decoration2);
            switch (item.getItemId()) {
            case R.id.add:
                mRecyclerViewAdapter.addData(new Random().nextInt(5));
                break;
            case R.id.delete:
                mRecyclerViewAdapter.removeData(new Random().nextInt(5));
                break;
            //通过RecyclerView去实现ListView、GridView、瀑布流的效果基本上没有什么区别,仅仅通过设置不同的LayoutManager即可实现
            case R.id.listview:
                mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
                break;
            case R.id.gridview:
                mRecyclerView.setLayoutManager(new GridLayoutManager(this, 3));
                break;
            case R.id.staggeredHorizontalGridView://Staggered:错列的,叉排的。
                mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(5, StaggeredGridLayoutManager.HORIZONTAL));//5行
                break;
            case R.id.staggeredVerticalGridview:
                mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL));//3列
                break;
            case R.id.staggeredAdapter:
                mRecyclerView.setAdapter(mStaggeredAdapter);
                break;
            case R.id.recyclerViewAdapter:
                mRecyclerView.setAdapter(mRecyclerViewAdapter);
                break;
            }
            return true;
        }
        @Override
        public void onItemClick(View view, int position) {
            Toast.makeText(MainActivity.this, position + " 被点击了", Toast.LENGTH_SHORT).show();
        }
        @Override
        public void onItemLongClick(View view, int position) {
            Toast.makeText(MainActivity.this, position + "被长按了", Toast.LENGTH_SHORT).show();
        }
    }

    固定宽高的Adapter
    public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.MyViewHolder> {
        private Context context;
        private List<String> mDatas;
        private MyOnItemClickLitener mOnItemClickLitener;
        public void setOnItemClickLitener(MyOnItemClickLitener mOnItemClickLitener) {
            this.mOnItemClickLitener = mOnItemClickLitener;
        }
        public MyRecyclerViewAdapter(Context context, List<String> datas) {
            this.context = context;
            mDatas = datas;
        }
        @Override
        public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            return new MyViewHolder(LayoutInflater.from(context).inflate(R.layout.item, parent, false));
        }
        @Override
        public void onBindViewHolder(final MyViewHolder holder, final int position) {
            holder.tv.setText(mDatas.get(position));
            RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) holder.tv.getLayoutParams();
            lp.setMargins(5, 5, 5, 5);//设置边距
            holder.tv.setLayoutParams(lp);
            // 如果设置了回调,则设置点击事件
            if (mOnItemClickLitener != null) {
                holder.itemView.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        int pos = holder.getLayoutPosition();
                        mOnItemClickLitener.onItemClick(holder.itemView, pos);
                    }
                });
                holder.itemView.setOnLongClickListener(new OnLongClickListener() {
                    @Override
                    public boolean onLongClick(View v) {
                        int pos = holder.getLayoutPosition();
                        mOnItemClickLitener.onItemLongClick(holder.itemView, pos);
                        removeData(pos);
                        return false;
                    }
                });
            }
        }
        @Override
        public int getItemCount() {
            return mDatas.size();
        }
        /**添加并更新数据,同时具有动画效果*/
        public void addData(int position) {
            mDatas.add(position, "Insert One");
            notifyItemInserted(position);//更新数据集,注意不是用adapter.notifyDataSetChanged(),否则没有动画效果
        }
        /**移除并更新数据,同时具有动画效果*/
        public void removeData(int position) {
            mDatas.remove(position);
            notifyItemRemoved(position);
        }
        class MyViewHolder extends RecyclerView.ViewHolder {
            TextView tv;
            public MyViewHolder(View view) {
                super(view);
                tv = (TextView) view.findViewById(R.id.id_num);
            }
        }
    }

    随机宽高的Adapter
    /**和MyAdapter唯一的区别就是:在代码中动态设置了TextView的高度(宽度)*/
    public class MyStaggeredAdapter extends RecyclerView.Adapter<MyStaggeredAdapter.MyViewHolder> {
        private Context context;
        private List<String> mDatas;
        private List<Integer> mHeights;//高度
        private MyOnItemClickLitener mOnItemClickLitener;
        public void setOnItemClickLitener(MyOnItemClickLitener mOnItemClickLitener) {
            this.mOnItemClickLitener = mOnItemClickLitener;
        }
        public MyStaggeredAdapter(Context context, List<String> datas, List<Integer> heights) {
            this.context = context;
            this.mDatas = datas;
            this.mHeights = heights;
        }
        @Override
        public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            return new MyViewHolder(LayoutInflater.from(context).inflate(R.layout.item, parent, false));
        }
        @Override
        public void onBindViewHolder(final MyViewHolder holder, final int position) {
            RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) holder.tv.getLayoutParams();
            lp.setMargins(5, 5, 5, 5);
            //横向时,item的宽度需要设置;纵向时,item的高度需要设置
            lp.height = mHeights.get(position);//******************************************************************************************唯一的区别在这里!
            lp.width = mHeights.get(position);//*******************************************************************************************唯一的区别在这里!
            holder.tv.setLayoutParams(lp);
            holder.tv.setText(mDatas.get(position));
            // 如果设置了回调,则设置点击事件
            if (mOnItemClickLitener != null) {
                holder.itemView.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        int pos = holder.getLayoutPosition();
                        mOnItemClickLitener.onItemClick(holder.itemView, pos);
                    }
                });
                holder.itemView.setOnLongClickListener(new OnLongClickListener() {
                    @Override
                    public boolean onLongClick(View v) {
                        int pos = holder.getLayoutPosition();
                        mOnItemClickLitener.onItemLongClick(holder.itemView, pos);
                        removeData(pos);
                        return false;
                    }
                });
            }
        }
        @Override
        public int getItemCount() {
            return mDatas.size();
        }
        public void addData(int position) {
            mDatas.add(position, "Insert One");
            mHeights.add((int) (100 + Math.random() * 300));
            notifyItemInserted(position);
        }
        public void removeData(int position) {
            mDatas.remove(position);
            notifyItemRemoved(position);
        }
        class MyViewHolder extends RecyclerView.ViewHolder {
            TextView tv;
            public MyViewHolder(View view) {
                super(view);
                tv = (TextView) view.findViewById(R.id.id_num);
            }
        }
    }

    点击事件回调接口
    /**系统没有提供ClickListener和LongClickListener,我们自己通过接口回调处理 */
    public interface MyOnItemClickLitener {
        void onItemClick(View view, int position);
        void onItemLongClick(View view, int position);
    }

    分割线示例
    /**zhy写的分割线,Item如果为最后一列则右边无间隔线,如果为最后一行则底部无分割线*/
    public class MyGridItemDecoration extends RecyclerView.ItemDecoration {
        private Drawable mDivider;
        public MyGridItemDecoration(Context context) {
            //通过读取系统主题中的 Android.R.attr.listDivider属性,将其作为Item间的分割线, <item name="android:listDivider">@drawable/divider_bg</item>  
            TypedArray typedArray = context.obtainStyledAttributes(new int[] { android.R.attr.listDivider });
            mDivider = typedArray.getDrawable(0);
            typedArray.recycle();//回收TypedArray,以便后面重用。This TypedArray should be recycled after use with recycle()
        }
        @Override
        /**判断如果是最后一行,则不需要绘制底部;如果是最后一列,则不需要绘制右边,整个判断也考虑到了StaggeredGridLayoutManager的横向和纵向*/
        public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
            int spanCount = getSpanCount(parent);
            int itemCount = parent.getAdapter().getItemCount();
            //使用outRect设置绘制的范围。一般如果仅仅是希望有空隙,还是去设置item的margin方便
            if (isLastRaw(parent, itemPosition, spanCount, itemCount)) {// 如果是最后一行,则不需要绘制底部
                outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
            } else if (isLastColum(parent, itemPosition, spanCount, itemCount)) {// 如果是最后一列,则不需要绘制右边
                outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
            } else {
                outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight());
            }
        }
        @Override
        public void onDraw(Canvas c, RecyclerView parent, State state) {
            drawHorizontal(c, parent);
            drawVertical(c, parent);
        }
        //******************************************************************************************
        public void drawHorizontal(Canvas c, RecyclerView parent) {
            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.getLeft() - params.leftMargin;
                final int right = child.getRight() + params.rightMargin + mDivider.getIntrinsicWidth();
                final int top = child.getBottom() + params.bottomMargin;
                final int bottom = top + mDivider.getIntrinsicHeight();
                mDivider.setBounds(left, top, right, bottom);
                mDivider.draw(c);
            }
        }
        public void drawVertical(Canvas c, RecyclerView parent) {
            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.getTop() - params.topMargin;
                final int bottom = child.getBottom() + params.bottomMargin;
                final int left = child.getRight() + params.rightMargin;
                final int right = left + mDivider.getIntrinsicWidth();
                mDivider.setBounds(left, top, right, bottom);
                mDivider.draw(c);
            }
        }
        //******************************************************************************************
        /**获取RecyclerView有多少列*/
        private int getSpanCount(RecyclerView parent) {
            int spanCount = -1; // 列数
            LayoutManager layoutManager = parent.getLayoutManager();
            if (layoutManager instanceof GridLayoutManager) spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
            else if (layoutManager instanceof StaggeredGridLayoutManager) spanCount = ((StaggeredGridLayoutManager) layoutManager).getSpanCount();
            return spanCount;
        }
        private boolean isLastRaw(RecyclerView parent, int pos, int spanCount, int childCount) {
            LayoutManager layoutManager = parent.getLayoutManager();
            if (layoutManager instanceof GridLayoutManager) {
                childCount = childCount - childCount % spanCount;
                if (pos >= childCount) // 如果是最后一行,则不需要绘制底部
                return true;
            } else if (layoutManager instanceof StaggeredGridLayoutManager) {
                int orientation = ((StaggeredGridLayoutManager) layoutManager).getOrientation();
                // StaggeredGridLayoutManager 且纵向滚动
                if (orientation == StaggeredGridLayoutManager.VERTICAL) {
                    childCount = childCount - childCount % spanCount;
                    if (pos >= childCount) return true;// 如果是最后一行,则不需要绘制底部
                } else { // StaggeredGridLayoutManager 且横向滚动
                    if ((pos + 1) % spanCount == 0) return true;// 如果是最后一行,则不需要绘制底部
                }
            }
            return false;
        }
        private boolean isLastColum(RecyclerView parent, int pos, int spanCount, int childCount) {
            LayoutManager layoutManager = parent.getLayoutManager();
            if (layoutManager instanceof GridLayoutManager) {
                if ((pos + 1) % spanCount == 0) {// 如果是最后一列,则不需要绘制右边
                    return true;
                }
            } else if (layoutManager instanceof StaggeredGridLayoutManager) {
                int orientation = ((StaggeredGridLayoutManager) layoutManager).getOrientation();
                if (orientation == StaggeredGridLayoutManager.VERTICAL) {
                    if ((pos + 1) % spanCount == 0) {// 如果是最后一列,则不需要绘制右边
                        return true;
                    }
                } else {
                    childCount = childCount - childCount % spanCount;
                    if (pos >= childCount) // 如果是最后一列,则不需要绘制右边
                    return true;
                }
            }
            return false;
        }
    }





    附件列表

    • 相关阅读:
      SpringBoot学习:整合shiro(身份认证和权限认证),使用EhCache缓存
      帝国备份王出错
      spring boot整合mybatis+mybatis-plus
      Druid连接池简介和配置
      thinkphp生成的验证码不显示问题解决
      分布式文件系统-FastDFS
      Spring Security OAuth2 Demo
      spring cloud-给Eureka Server加上安全的用户认证
      spring cloud 报错Error creating bean with name 'hystrixCommandAspect' ,解决方案
      分布式唯一ID极简教程
    • 原文地址:https://www.cnblogs.com/baiqiantao/p/5585345.html
    Copyright © 2011-2022 走看看