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));
            }
        }
    }
    
  • 相关阅读:
    Mvc+三层(批量添加、删除、修改)
    js中判断复选款是否选中
    EF的优缺点
    Git tricks: Unstaging files
    Using Git Submodules
    English Learning
    wix xslt for adding node
    The breakpoint will not currently be hit. No symbols have been loaded for this document."
    Use XSLT in wix
    mfc110ud.dll not found
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/8820299.html
Copyright © 2011-2022 走看看