zoukankan      html  css  js  c++  java
  • 一篇博客理解Recyclerview的使用

    从Android 5.0开始,谷歌公司推出了RecylerView控件,当看到RecylerView这个新控件的时候,大部分人会首先发出一个疑问,recylerview是什么?为什么会有recylerview也就是说recylerview的优点是什么?recylerview怎么用?等等,下面我们将深入解析recylerview。

    1.RecyclerView是什么?

    RecyclerView是support-v7包中的新组件,是一个强大的滑动组件,与经典的ListView相比,同样拥有item回收复用的功能,这一点从它的名字Recyclerview即回收view也可以看出。看到这也许有人会问,不是已经有ListView了吗,为什么还要RecyclerView呢?这就牵扯到第二个问题了。

    2.RecyclerView的优点是什么?

    根据官方的介绍RecyclerView是ListView的升级版,既然如此那RecyclerView必然有它的优点,现就RecylerView相对于ListView的优点罗列如下:
    ① RecyclerView封装了viewholder的回收复用,也就是说RecyclerView标准化了ViewHolder,编写Adapter面向的是ViewHolder而不再是View了,复用的逻辑被封装了,写起来更加简单。
    ② 提供了一种插拔式的体验,高度的解耦,异常的灵活,针对一个Item的显示RecyclerView专门抽取出了相应的类,来控制Item的显示,使其的扩展性非常强。例如:你想控制横向或者纵向滑动列表效果可以通过LinearLayoutManager这个类来进行控制(与GridView效果对应的是GridLayoutManager,与瀑布流对应的还StaggeredGridLayoutManager等),也就是说RecyclerView不再拘泥于ListView的线性展示方式,它也可以实现GridView的效果等多种效果。你想控制Item的分隔线,可以通过继承RecyclerView的ItemDecoration这个类,然后针对自己的业务需求去抒写代码。
    ③ 可以控制Item增删的动画,可以通过ItemAnimator这个类进行控制,当然针对增删的动画,RecyclerView有其自己默认的实现。

    recyclerView = (RecyclerView) findViewById(R.id.recyclerView);  
    LinearLayoutManager layoutManager = new LinearLayoutManager(this );  
    //设置布局管理器  
    recyclerView.setLayoutManager(layoutManager);  
    //设置为垂直布局,这也是默认的  
    layoutManager.setOrientation(OrientationHelper. VERTICAL);  
    //设置Adapter  
    recyclerView.setAdapter(recycleAdapter);  
     //设置分隔线  
    recyclerView.addItemDecoration( new DividerGridItemDecoration(this ));  
    //设置增加或删除条目的动画  
    recyclerView.setItemAnimator( new DefaultItemAnimator());  
    
    

    可以看到对RecylerView的设置过程,比ListView要复杂一些,虽然代码抒写上有点复杂,但它的扩展性是极高的。在了解了RecyclerView的一些控制之后,紧接着来看看它的Adapter的写法,RecyclerView的Adapter与ListView的Adapter还是有点区别的,RecyclerView.Adapter,需要实现3个方法:
    a) onCreateViewHolder()
    这个方法主要生成为每个Item inflater出一个View,但是该方法返回的是一个ViewHolder。该方法把View直接封装在ViewHolder中,然后我们面向的是ViewHolder这个实例,当然这个ViewHolder需要我们自己去编写。直接省去了当初的convertView.setTag(holder)和convertView.getTag()这些繁琐的步骤。

    b) onBindViewHolder()
    这个方法主要用于适配渲染数据到View中。方法提供给你了一viewHolder而不是原来的convertView。

    c) getItemCount()
    这个方法就类似于BaseAdapter的getCount方法了,即总共有多少个条目。接下来通过几个小的实例帮助大家更深入的了解RecyclerView的用法。

    例子1:用RecyclerView实现一个图片滚动的列表
    代码如下:

    public class MainActivity extends ActionBarActivity {
    
        private RecyclerView mRecyclerView;
        private List<Integer> mDatas;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initData();
            // 得到控件
            mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
            // 设置布局管理器
            LinearLayoutManager layoutManager = new LinearLayoutManager(this);
            layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
            mRecyclerView.setLayoutManager(layoutManager);
            // 设置适配器
            mRecyclerView.setAdapter(new MyRecyclerAdapter(this, mDatas));
    
        }
    
        private void initData() {
            mDatas = new ArrayList<Integer>(Arrays.asList(R.drawable.kenan1,
                    R.drawable.kenan2, R.drawable.kenan3, R.drawable.kenan4,
                    R.drawable.kenan5, R.drawable.kenan6, R.drawable.kenan7,
                    R.drawable.kenan8));
        }
    }
    public class DividerItemDecoration extends ItemDecoration {
    
        public DividerItemDecoration() {
            super();
            // TODO Auto-generated constructor stub
        }
    
        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
                State state) {
            // TODO Auto-generated method stub
            super.getItemOffsets(outRect, view, parent, state);
        }
    
        @Override
        @Deprecated
        public void onDraw(Canvas c, RecyclerView parent) {
            // TODO Auto-generated method stub
            super.onDraw(c, parent);
        }
    
    }
    
    public class MyRecyclerAdapter extends Adapter<MyRecyclerAdapter.MyHolder> {
    
        private Context mContext;
        private List<Integer> mDatas;
    
        public MyRecyclerAdapter(Context context, List<Integer> datas) {
            super();
            this.mContext = context;
            this.mDatas = datas;
        }
    
        @Override
        public int getItemCount() {
            // TODO Auto-generated method stub
            return mDatas.size();
        }
    
        @Override
        // 填充onCreateViewHolder方法返回的holder中的控件
        public void onBindViewHolder(MyHolder holder, int position) {
            // TODO Auto-generated method stub
            holder.imageView.setImageResource(mDatas.get(position));
        }
    
        @Override
        // 重写onCreateViewHolder方法,返回一个自定义的ViewHolder
        public MyHolder onCreateViewHolder(ViewGroup arg0, int arg1) {
            // 填充布局
            View view = LayoutInflater.from(mContext).inflate(R.layout.item, null);
            MyHolder holder = new MyHolder(view);
            return holder;
        }
    
        // 定义内部类继承ViewHolder
        class MyHolder extends ViewHolder {
    
            private ImageView imageView;
    
            public MyHolder(View view) {
                super(view);
                imageView = (ImageView) view.findViewById(R.id.iv_item);
            }
    
        }
    
    
    }
    
    

    效果如下:
    这里写图片描述

    3.为RecyclerView添加OnItemClickListener回调

    效果很不错,这就是RecyclerView的基本用法了,但细心的你会发现,竟然没有提供setOnItemClickListener这个回调,也就是无法响应点击事件,然而在日常开发中,响应点击事件无疑都是必须的,虽然它没有提供,但是我们可以手动添加OnItemClickListener,我们可以在Adapter中添加这个回调接口:

    例子2:可以点击的RecyclerView
    在原工程基础上对Adapter进行修改,添加OnItemClickListener接口,由于具体点击后的逻辑是交给MainActivity去确定的,所以我们定义抽象的OnItemClickListener接口,里面有一个抽象方法,用于设置被点击后的逻辑:

    //item的回调接口
        public interface OnItemClickListener{
            void onItemClick(View view,int Position);
        }

    对外暴露一个设置点击监听器的方法,其中传入需要OnItemClickListener接口

    //定义一个设置点击监听器的方法
        public void setOnItemClickListener(OnItemClickListener itemClickListener) {
            this.mItemClickListener = itemClickListener;
        }

    在绑定ViewHolder的逻辑之中,对RecyclerView的每一个itemView设置点击事件:

    @Override
        // 填充onCreateViewHolder方法返回的holder中的控件
        public void onBindViewHolder(final MyHolder holder, final int position) {
            // TODO Auto-generated method stub
            holder.imageView.setImageResource(mDatas.get(position));
            //如果设置了回调,则设置点击事件  
            if(mItemClickListener != null){
                holder.itemView.setOnClickListener(new OnClickListener() {
    
                    @Override
                    public void onClick(View v) {
                        mItemClickListener.onItemClick(holder.itemView, position);
    
                    }
                });
            }
        }

    最后粘上Adapter的全部代码,其他代码均和例子1一样

    public class MyRecyclerAdapter extends Adapter<MyRecyclerAdapter.MyHolder> {
    
        private Context mContext;
        private List<Integer> mDatas;
        private OnItemClickListener mItemClickListener;
    
        public MyRecyclerAdapter(Context context, List<Integer> datas) {
            super();
            this.mContext = context;
            this.mDatas = datas;
        }
        //item的回调接口
        public interface OnItemClickListener{
            void onItemClick(View view,int Position);
        }
        //定义一个设置点击监听器的方法
        public void setOnItemClickListener(OnItemClickListener itemClickListener) {
            this.mItemClickListener = itemClickListener;
        }
        @Override
        public int getItemCount() {
            // TODO Auto-generated method stub
            return mDatas.size();
        }
    
        @Override
        // 填充onCreateViewHolder方法返回的holder中的控件
        public void onBindViewHolder(final MyHolder holder, final int position) {
            // TODO Auto-generated method stub
            holder.imageView.setImageResource(mDatas.get(position));
            //如果设置了回调,则设置点击事件  
            if(mItemClickListener != null){
                holder.itemView.setOnClickListener(new OnClickListener() {
    
                    @Override
                    public void onClick(View v) {
                        mItemClickListener.onItemClick(holder.itemView, position);
    
                    }
                });
            }
        }
    
        @Override
        // 重写onCreateViewHolder方法,返回一个自定义的ViewHolder
        public MyHolder onCreateViewHolder(ViewGroup viewgroup, int i) {
            // 填充布局
            View view = LayoutInflater.from(mContext).inflate(R.layout.item, null);
            MyHolder holder = new MyHolder(view);
            return holder;
        }
    
        // 定义内部类继承ViewHolder
        class MyHolder extends ViewHolder {
    
            private ImageView imageView;
    
            public MyHolder(View view) {
                super(view);
                imageView = (ImageView) view.findViewById(R.id.iv_item);
            }
    
        }
    
    
    }
    

    效果如下:
    这里写图片描述

    4.自定义RecyclerView实现滚动时内容联动

    例子3:RecyclerView制作相册效果
    效果:在原工程的基础上进行修改,改成相册效果,即上面显示一张大图,下面的RecyclerView做为图片切换的指示器。

    首先修改下布局:

    <LinearLayout 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"
        android:orientation="vertical"
        tools:context="com.example.recyclerviewdemo.MainActivity" >
    
        <ImageView
            android:id="@+id/iv_group"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_margin="10dp"
            android:layout_weight="1"
            android:scaleType="centerCrop"
            android:src="@drawable/ic_launcher" />
    
        <!--
        <FrameLayout
            android:layout_width="fill_parent"
            android:layout_height="0dp"
            android:layout_weight="1" >
    
            <ImageView
                android:id="@+id/iv_group"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:layout_gravity="center"
                android:layout_margin="10dp"
                android:scaleType="centerCrop"
                android:src="@drawable/ic_launcher" />
        </FrameLayout>
        -->
    
        <com.example.recyclerviewdemo.MyRecyclerView
            android:id="@+id/recyclerview"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:layout_centerVertical="true"
            android:layout_gravity="bottom" />
    
    </LinearLayout>
    

    添加一个显示大图的区域,把RecyclerView改为自己定义的。
    然后看我们自定义RecyclerView的代码:

    public class MyRecyclerView extends RecyclerView {
    
        private onItemScrollChangeListener mItemScrollChangeListener;
        private View mCurrentView;
    
        public MyRecyclerView(Context context, AttributeSet attrs) {
            super(context, attrs);
            // TODO Auto-generated constructor stub
        }
    
        // 回调的接口
        public interface onItemScrollChangeListener {
            void onChange(View view, int position);
        }
    
        // 对外暴露设置滚动接口的方法
        public void setOnItemScrollChangeListener(
                onItemScrollChangeListener itemScrollChangeListener) {
            this.mItemScrollChangeListener = itemScrollChangeListener;
        }
    
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            // TODO Auto-generated method stub
            super.onLayout(changed, l, t, r, b);
            mCurrentView = getChildAt(0);
            if (mItemScrollChangeListener != null) {
                mItemScrollChangeListener.onChange(mCurrentView,
                        getChildPosition(mCurrentView));
            }
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent e) {
            // TODO Auto-generated method stub
    
            if (e.getAction() == MotionEvent.ACTION_MOVE) {
                mCurrentView = getChildAt(0);
                if (mItemScrollChangeListener != null) {
                    mItemScrollChangeListener.onChange(mCurrentView,
                            getChildPosition(mCurrentView));
                }
            }
            return super.onTouchEvent(e);
        }
    }
    

    最主要是重写onLayout,onTouchEvent方法,并设置滚动监听的回调,还有向外界暴露监听器的逻辑。

    然后是Adapter的代码

    public class MyRecyclerAdapter extends Adapter<MyRecyclerAdapter.MyHolder> {
    
        private Context mContext;
        private List<Integer> mDatas;
        //private OnItemClickListener mItemClickListener;
    
        public MyRecyclerAdapter(Context context, List<Integer> datas) {
            super();
            this.mContext = context;
            this.mDatas = datas;
        }
        /*//item的回调接口
        public interface OnItemClickListener{
            void onItemClick(View view,int Position);
        }
        //定义一个设置点击监听器的方法
        public void setOnItemClickListener(OnItemClickListener itemClickListener) {
            this.mItemClickListener = itemClickListener;
        }*/
        @Override
        public int getItemCount() {
            // TODO Auto-generated method stub
            return mDatas.size();
        }
    
        @Override
        // 填充onCreateViewHolder方法返回的holder中的控件
        public void onBindViewHolder(final MyHolder holder, final int position) {
            // TODO Auto-generated method stub
            holder.imageView.setImageResource(mDatas.get(position));
            /*//如果设置了回调,则设置点击事件  
            if(mItemClickListener != null){
                holder.itemView.setOnClickListener(new OnClickListener() {
    
                    @Override
                    public void onClick(View v) {
                        mItemClickListener.onItemClick(holder.itemView, position);
    
                    }
                });
            }*/
        }
    
        @Override
        // 重写onCreateViewHolder方法,返回一个自定义的ViewHolder
        public MyHolder onCreateViewHolder(ViewGroup viewgroup, int i) {
            // 填充布局
            View view = LayoutInflater.from(mContext).inflate(R.layout.item, null);
            MyHolder holder = new MyHolder(view);
            return holder;
        }
    
        // 定义内部类继承ViewHolder
        class MyHolder extends ViewHolder {
    
            private ImageView imageView;
    
            public MyHolder(View view) {
                super(view);
                imageView = (ImageView) view.findViewById(R.id.iv_item);
            }
    
        }
    
    
    }
    

    定义了一个滚动时回调的接口,然后在onTouchEvent中,监听ACTION_MOVE,用户手指滑动时,不断把当前第一个View回调回去
    关于为什么getChildAt(0)和getChildPosition()可用,起初我以为有getFirstVisibleItem这个方法,后来发现么有;但是发现了getRecycledViewPool()看名字我觉得是Viewholder那个缓存队列,我想那么直接取这个队列的第一个不就是我要的View么,后来没有成功。我就观察它内部的View,最后发现,第一个显示的始终是它第一个child,至于getChildPosition这个看方法就看出来了。
    效果如下:
    这里写图片描述

    5.RecyclerView实现瀑布流

    例子4:用RecyclerView打造瀑布流效果
    其中大部分内容实现和基本的RecyclerView使用是一样的,就不多叙述了,就一个地方不同,就是我们在适配器中绑定ViewHolder的方法中需要重新给我们的itemView布局设置height,这里是生成随机数来设置高度的。

    //得到随机item的高度
        private void getRandomHeight(List<Integer> datas) {
            heights = new ArrayList<Integer>();
            for (int i = 0; i < datas.size(); i++) {
                heights.add((int) (200+Math.random()*100));
            }
        }

    在onBindViewHolder方法中:

    @Override
        // 填充onCreateViewHolder方法返回的holder中的控件
        public void onBindViewHolder(final MyHolder holder, int position) {
            // TODO Auto-generated method stub  
            //得到item的LayoutParams布局参数
            ViewGroup.LayoutParams params= holder.itemView.getLayoutParams();
            //把随机的高度赋予item布局
            params.height = heights.get(position);
            //把params设置给item布局
            holder.itemView.setLayoutParams(params);
            //为控件绑定数据
            holder.imageView.setImageResource(mDatas.get(position));
            //如果设置了监听那么它就不为空,然后回调相应的方法
            if(onItemClickListener!=null){
                holder.itemView.setOnClickListener(new OnClickListener() {
    
                    @Override
                    public void onClick(View v) {
                        //得到当前点击item的位置pos
                        int position = holder.getLayoutPosition();
                        //把事件交给我们实现的接口那里处理
                        onItemClickListener.onOnItemClick(holder.itemView, position);
                    }
                });
                holder.itemView.setOnLongClickListener(new OnLongClickListener() {
    
                    @Override
                    public boolean onLongClick(View v) {
                        //得到当前点击item的位置pos
                        int position = holder.getLayoutPosition();
                        //把事件交给我们实现的接口那里处理
                        onItemClickListener.onOnItemClick(holder.itemView, position);
                        return true;
                    }
                });
            }
        }

    最后附上项目完整代码:

    public class MainActivity extends ActionBarActivity {
    
        private RecyclerView mRecyclerView;
        private List<Integer> mDatas;
        private MyRecyclerAdapter adapter;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initData();
            // 得到控件
            mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
            mRecyclerView.setItemAnimator(new DefaultItemAnimator());
            //设置RecyclerView布局管理器为2列垂直排布
            StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
            mRecyclerView.setLayoutManager(layoutManager);
    
            adapter = new MyRecyclerAdapter(this, mDatas);
            mRecyclerView.setAdapter(adapter);
            adapter.setOnItemClickListener(new onItemClickListener() {
    
                @Override
                public void onOnItemClick(View view, int position) {
                    // TODO Auto-generated method stub
                    Toast.makeText(MainActivity.this, "点击了:"+position, Toast.LENGTH_SHORT).show();
                }
    
                @Override
                public void onLongClick(View view, int position) {
                     //长按删除
                    mDatas.remove(position);
                    adapter.notifyItemRemoved(position);
                }
            });
    
        }
    
        private void initData() {
            mDatas = new ArrayList<Integer>(Arrays.asList(R.drawable.kenan1,
                    R.drawable.kenan2, R.drawable.kenan3, R.drawable.kenan4,
                    R.drawable.kenan5, R.drawable.kenan6, R.drawable.kenan7,
                    R.drawable.kenan8,R.drawable.kenan1,
                    R.drawable.kenan2, R.drawable.kenan3, R.drawable.kenan4));
        }
    }
    
    public class MyRecyclerAdapter extends Adapter<MyRecyclerAdapter.MyHolder> {
    
        private Context mContext;
        private List<Integer> mDatas;
        private List<Integer> heights;
        private onItemClickListener onItemClickListener;
        public MyRecyclerAdapter(Context context, List<Integer> datas) {
            super();
            this.mContext = context;
            this.mDatas = datas;
            getRandomHeight(this.mDatas);
        }
    
        public interface onItemClickListener{
            //条目被点击时触发的回调
            void onOnItemClick(View view,int position);
            //长按时触发的回调
            void onLongClick(View view,int position);
        }
    
    
        public void setOnItemClickListener(onItemClickListener onItemClickListener) {
            this.onItemClickListener = onItemClickListener;
        }
        @Override
        public int getItemCount() {
            // TODO Auto-generated method stub
            return mDatas.size();
        }
    
        @Override
        // 填充onCreateViewHolder方法返回的holder中的控件
        public void onBindViewHolder(final MyHolder holder, int position) {
            // TODO Auto-generated method stub  
            //得到item的LayoutParams布局参数
            ViewGroup.LayoutParams params= holder.itemView.getLayoutParams();
            //把随机的高度赋予item布局
            params.height = heights.get(position);
            //把params设置给item布局
            holder.itemView.setLayoutParams(params);
            //为控件绑定数据
            holder.imageView.setImageResource(mDatas.get(position));
            //如果设置了监听那么它就不为空,然后回调相应的方法
            if(onItemClickListener!=null){
                holder.itemView.setOnClickListener(new OnClickListener() {
    
                    @Override
                    public void onClick(View v) {
                        //得到当前点击item的位置pos
                        int position = holder.getLayoutPosition();
                        //把事件交给我们实现的接口那里处理
                        onItemClickListener.onOnItemClick(holder.itemView, position);
                    }
                });
                holder.itemView.setOnLongClickListener(new OnLongClickListener() {
    
                    @Override
                    public boolean onLongClick(View v) {
                        //得到当前点击item的位置pos
                        int position = holder.getLayoutPosition();
                        //把事件交给我们实现的接口那里处理
                        onItemClickListener.onOnItemClick(holder.itemView, position);
                        return true;
                    }
                });
            }
        }
    
        @Override
        // 重写onCreateViewHolder方法,返回一个自定义的ViewHolder
        public MyHolder onCreateViewHolder(ViewGroup viewGroup, int arg1) {
            // 填充布局
            View view = LayoutInflater.from(mContext).inflate(R.layout.item,viewGroup, false);
            MyHolder holder = new MyHolder(view);
            return holder;
        }
    
        // 定义内部类继承ViewHolder
        class MyHolder extends ViewHolder {
    
            private ImageView imageView;
            public MyHolder(View view) {
                super(view);
                imageView = (ImageView) view.findViewById(R.id.iv_item);
            }
    
        }
        //得到随机item的高度
        private void getRandomHeight(List<Integer> datas) {
            heights = new ArrayList<Integer>();
            for (int i = 0; i < datas.size(); i++) {
                heights.add((int) (200+Math.random()*100));
            }
        }
    }
    
  • 相关阅读:
    android Textview动态设置大小
    小米1plus MIUI RadioButton的问题
    快读
    高精集合
    清北学堂part2
    清北学堂part1
    OTZ%%%子谦。大佬
    筛质数大优化
    回文日期
    高精度加法
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/8820299.html
Copyright © 2011-2022 走看看