Android RecyclerView使用详解

来源:互联网 发布:剑网三光头捏脸数据 编辑:程序博客网 时间:2024/05/16 19:32

     RecyclerView 是Android L版本中新添加的一个用来取代ListView的SDK,RecyclerView与ListView的原理是类似的:都是仅仅维护少量的View并且可以展示大量的数据集,但是,RecyclerView提供了插拔式的体验,高度的解耦,十分的灵活 ,通过它提供的LayoutManager,ItemDecoration,ItemAnimator可以实现令ListView可望不可即的效果。

RecyclerView用以下两种方式简化了数据的展示和处理:

  • 使用LayoutManager来确定每一个item的排列方式。
  • 使用ItemAnimator来控制Item的增删动画
  • 使用ItemDecoration来控制Item的间隔。

先来看看实现的效果:



下面就来详解RecyclerView 的使用,以下给出了实现此效果的全部代码:

1,添加依赖:

dependencies {    compile fileTree(dir: 'libs', include: ['*.jar'])    testCompile 'junit:junit:4.12'    compile 'com.android.support:appcompat-v7:24.+'    compile 'com.android.support:recyclerview-v7:23.2.1'}


2,RecyclerView对应的布局文件:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout 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="com.example.recycleviewtest.MainActivity">    <android.support.v7.widget.RecyclerView        android:id="@+id/rv"        android:layout_width="match_parent"        android:layout_height="match_parent" /></RelativeLayout>

3,和ListView相同,RecyclerView 也需要一个适配器,需要说明的是,RecyclerView 并没有提供点击和长按事件监听的API,所以需要自己来实现,这里我在适配器中通过定义接口,然后回调接口的方式来实现点击和长按事件的监听。代码中有详细注解:

package com.example.recycleviewtest;import android.content.Context;import android.support.v7.widget.RecyclerView;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.TextView;import java.util.ArrayList;/** * Created by Administrator on 2016/10/6. * RecycleView对应的适配器 * 要求必须实现ViewHolder类,用于缓存控件 */public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {    //通过构造方法把数据传递过来    protected ArrayList<String>mData;    protected LayoutInflater inflater;    public MyAdapter(Context context,ArrayList<String> mData) {        this.mData = mData;        this.inflater = LayoutInflater.from(context);    }    //定义一个接口,响应点击事件    protected OnItemClickListener mOnItemClickListener;    protected  interface OnItemClickListener{        //点击事件的回调        void setOnItemClick(View view,int position);        void setOnLongItemClick(View view,int position);    }    public void setmOnItemClickListener(OnItemClickListener listener) {        this.mOnItemClickListener = listener;    }    /**     * 渲染布局     */    @Override    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        View itemView = inflater.inflate(R.layout.item, parent, false);        return new MyViewHolder(itemView);    }    /**     * 将数据绑定到ViewHolder     */    @Override    public void onBindViewHolder(MyViewHolder holder, final int position) {        //将传递过来的数据设置给holder中的控件        holder.mTextView.setText(mData.get(position));        //初始化点击事件        initClick(holder,position);    }    protected void initClick(MyViewHolder holder, final int position) {        //单击事件的回调        holder.itemView.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                // mOnItemClickListener是在MainActivity调用之后传过来的,                // 如果不为空,说明被调用了,把当前的position回调给MainActivity                if (mOnItemClickListener != null){                    mOnItemClickListener.setOnItemClick(v,position);                }            }        });        //长按事件的回调        holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {            @Override            public boolean onLongClick(View v) {                if (mOnItemClickListener != null){                    mOnItemClickListener.setOnLongItemClick(v,position);                }                return true; //注意:一定要返回true,这样可以消费事件,让点击事件不再生效            }        });    }    /**     * 返回条目数     */    @Override    public int getItemCount() {        return mData.size();    }    /**     * 用于缓存条目数据的Holder类     * RcycleView要求必须实现此类     */    public class MyViewHolder extends RecyclerView.ViewHolder{        //参数itemView即为要缓存的条目布局        TextView mTextView;        public MyViewHolder(View itemView) {            super(itemView);            mTextView = (TextView) itemView.findViewById(R.id.tv);        }    }}


4,实现RecyclerView神奇效果的核心代码+注解:

RecyclerView.LayoutManager是一个抽象类,系统提供了3个实现类:

①  LinearLayoutManager 现行管理器,支持横向、纵向。默认为纵向。

②  GridLayoutManager 网格布局管理器,支持横向、纵向。默认为纵向。

  StaggeredGridLayoutManager 瀑布就式布局管理器,支持横向、纵向。默认为纵向。

package com.example.recycleviewtest;import android.content.Intent;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.support.v7.widget.DefaultItemAnimator;import android.support.v7.widget.GridLayoutManager;import android.support.v7.widget.LinearLayoutManager;import android.support.v7.widget.RecyclerView;import android.support.v7.widget.StaggeredGridLayoutManager;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.widget.Toast;import java.util.ArrayList;public class MainActivity extends AppCompatActivity {    private RecyclerView mRecycleView;    private ArrayList<String>mData;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //1.获取RecyclerView控件        mRecycleView = (RecyclerView) findViewById(R.id.rv);        //2.获取数据        initData();        //3.设置适配器        MyAdapter adapter = new MyAdapter(this,mData);        //4.给RecyclerView设置布局容器        // 4.1 线性布局//        mRecycleView.setLayoutManager(new LinearLayoutManager(this));        // 4.2 宫格布局(GridView) (设置为默认布局)        mRecycleView.setLayoutManager(new GridLayoutManager(this,2));        // 4.3 瀑布流布局//        mRecycleView.setLayoutManager(new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL));        //5.给RecyclerView设置适配器        mRecycleView.setAdapter(adapter);        //6.给RecyclerView添加分割线//        mRecycleView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST));        //7.给RecyclerView设置动画        mRecycleView.setItemAnimator(new DefaultItemAnimator());        //8.给适配器设置点击事件        adapter.setmOnItemClickListener(new MyAdapter.OnItemClickListener() {            @Override            public void setOnItemClick(View view, int position) {                Toast.makeText(MainActivity.this,"点击了"+position+"条目",Toast.LENGTH_SHORT).show();            }            @Override            public void setOnLongItemClick(View view, int position) {                Toast.makeText(MainActivity.this,"长按了"+position+"条目",Toast.LENGTH_SHORT).show();            }        });    }    /**     * 初始化数据     */    private void initData() {        mData = new ArrayList<>();        for (int i = 'A'; i <= 'z'; i++) {            mData.add(String.valueOf((char)i));        }    }    /**     * 创建选择菜单     * 加载自定义的菜单     * @param menu Interface for managing the items in a menu.     * @return     */    @Override    public boolean onCreateOptionsMenu(Menu menu) {        getMenuInflater().inflate(R.menu.menu,menu);        return true; //消费事件,处理自己的业务    }    /**     * 菜单选择项     * 判断选择了哪个菜单项,做相应的业务     * @param item 选中的菜单项     * @return     */    @Override    public boolean onOptionsItemSelected(MenuItem item) {        int itemId = item.getItemId();        switch (itemId){  //根据选中菜单项的id来显示不同的布局            case R.id.listView:  //线性布局                mRecycleView.setLayoutManager(new LinearLayoutManager(this));                break;            case R.id.gridView:  //宫格布局                mRecycleView.setLayoutManager(new GridLayoutManager(this,2));                break;            case R.id.horizontalStaggerGridView:  //水平瀑布流宫格布局                mRecycleView.setLayoutManager(new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.HORIZONTAL));                break;            case R.id.staggerGridView:  //垂直瀑布流宫格布局                Intent intent = new Intent(MainActivity.this,StaggerActivity.class);                startActivity(intent);                break;        }        return true;    }}

5,把不同的布局切换以菜单的形式来展示,这样界面看起来更加的简洁,对应的菜单文件res---->menu---->menu.xml:

<?xml version="1.0" encoding="utf-8"?>    <menu xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools">    <item        android:id="@+id/listView"        android:orderInCategory="100"        android:showAsAction="never"        android:title="ListView"        tools:ignore="AppCompatResource"/>    <item        android:id="@+id/gridView"        android:orderInCategory="100"        android:showAsAction="never"        android:title="GridView"        tools:ignore="AppCompatResource"/>    <item        android:id="@+id/horizontalStaggerGridView"        android:orderInCategory="100"        android:showAsAction="never"        android:title="HorizontalStaggerGridView"        tools:ignore="AppCompatResource"/>    <item        android:id="@+id/staggerGridView"        android:orderInCategory="100"        android:showAsAction="never"        android:title="StaggerGridView"        tools:ignore="AppCompatResource"/></menu>

6,适配器中条目布局对应的布局文件item.xml:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    android:background="#55aa00ff"    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:layout_margin="5dp">    <TextView        android:id="@+id/tv"        android:layout_width="60dp"        android:layout_height="40dp"        android:textSize="20sp"        android:gravity="center"/></LinearLayout>

7,RecyclerView虽然很强大,但我感觉有两个让我们开发者很不爽的地方,一是没有点击和长按监听事件,二是没有提供分割线的API,所以如果我们想设置分割线的话需要自定义,我在代码中设置分割线的代码注释掉了,而是使用布局文件来给每个Item设置间距,下面是官方提供的分割线的demo:

package com.example.recycleviewtest;/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Rect;import android.graphics.drawable.Drawable;import android.support.v7.widget.LinearLayoutManager;import android.support.v7.widget.RecyclerView;import android.util.Log;import android.view.View;/** * This class is from the v7 samples of the Android SDK. It's not by me! * <p/> * See the license above for details. */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);}/** * 设置排列方向 * @param orientation     */public void setOrientation(int orientation){if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST){throw new IllegalArgumentException("invalid orientation");}mOrientation = orientation;}/** * 绘制分割线 * @param c * @param parent     */@Overridepublic void onDraw(Canvas c, RecyclerView parent){ if (mOrientation == VERTICAL_LIST) {            drawVertical(c, parent);        } else {            drawHorizontal(c, parent);        }}/** * 画竖直方向的分割线 * @param c * @param 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);RecyclerView v = new RecyclerView(parent.getContext());final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();final int top = child.getBottom() + params.bottomMargin;final int bottom = top + mDivider.getIntrinsicHeight();mDivider.setBounds(left, top, right, bottom);mDivider.draw(c);}}/** * 画水平方向的分割线 * @param c * @param parent     */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;final int right = left + mDivider.getIntrinsicHeight();mDivider.setBounds(left, top, right, bottom);mDivider.draw(c);}}/** * 为分割线预留空间 * @param outRect * @param itemPosition * @param parent     */@Overridepublic 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);}}}


8,下面是我在drawable目录下自定义的一个分割线divider.xml:

<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android"    android:shape="rectangle">    <!--gradient:设置矩形的渐变色        size:设置矩形的尺寸-->    <gradient        android:startColor="#ff0000"        android:centerColor="#00ff00"        android:endColor="#0000ff"        android:type="linear"/>    <size        android:width="4dp"        android:height="4dp"/></shape>


9,实现垂直瀑布流宫格布局效果时,需要设置每个条目的高度(或宽带)最好有差别,这里我的思路是:当选择垂直瀑布流宫格布局时,我跳转到另外一个StaggerActivity中,在StaggerActivity对应的适配器中来改变每个条目的高度。这个方案很笨拙,但容易理解,StaggerActivity对应的适配器代码:

package com.example.recycleviewtest;import android.content.Context;import android.view.ViewGroup;import java.util.ArrayList;/** * Created by Administrator on 2016/10/7. */public class StaggerAdapter extends MyAdapter {    private ArrayList<Integer> mHeights; //瀑布流item的高度    public StaggerAdapter(Context context, ArrayList<String> mData) {        super(context, mData);        mHeights = new ArrayList<>();        for (int i = 0; i < mData.size(); i++) {            mHeights.add((int) (Math.random()*300 + 100));        }    }    /*        在绑定数据时改变item的高度     */    @Override    public void onBindViewHolder(MyViewHolder holder, int position) {        //2.通过getLayoutParams拿到对应控件的属性,对其进行设置        ViewGroup.LayoutParams params = holder.mTextView.getLayoutParams();        //3.改变item的高度        params.height = mHeights.get(position);        //1.通过setLayoutParams方法来设置控件的属性        holder.mTextView.setLayoutParams(params);        //给控件设置内容        holder.mTextView.setText(mData.get(position));        //初始化监听事件        initClick(holder,position);    }}

10,StaggerActivity其实和MainActivity的代码几乎相同,只是改变适配器和menu的条目选择后的跳转方向。

package com.example.recycleviewtest;import android.content.Intent;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.support.v7.widget.DefaultItemAnimator;import android.support.v7.widget.RecyclerView;import android.support.v7.widget.StaggeredGridLayoutManager;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.widget.Toast;import java.util.ArrayList;public class StaggerActivity extends AppCompatActivity {    private RecyclerView mRecycleView;    private ArrayList<String>mData;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //1.获取RecyclerView控件        mRecycleView = (RecyclerView) findViewById(R.id.rv);        //2.获取数据        initData();        //3.设置适配器        StaggerAdapter adapter = new StaggerAdapter(this,mData);        //4.给RecyclerView设置布局容器        // 4.1 线性布局//        mRecycleView.setLayoutManager(new LinearLayoutManager(this));        // 4.2 宫格布局(GridView)//        mRecycleView.setLayoutManager(new GridLayoutManager(this,2));        // 4.3 瀑布流布局        mRecycleView.setLayoutManager(new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL));        //5.给RecyclerView设置适配器        mRecycleView.setAdapter(adapter);        //6.给RecyclerView设置分割线//        mRecycleView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST));        //7.给RecyclerView设置动画        mRecycleView.setItemAnimator(new DefaultItemAnimator());        //8.给适配器设置点击事件        adapter.setmOnItemClickListener(new MyAdapter.OnItemClickListener() {            @Override            public void setOnItemClick(View view, int position) {                Toast.makeText(StaggerActivity.this,"点击了"+position+"条目",Toast.LENGTH_SHORT).show();            }            @Override            public void setOnLongItemClick(View view, int position) {                Toast.makeText(StaggerActivity.this,"长按了"+position+"条目",Toast.LENGTH_SHORT).show();            }        });    }    /**     * 初始化数据     */    private void initData() {        mData = new ArrayList<>();        for (int i = 'A'; i <= 'z'; i++) {            mData.add(String.valueOf((char)i));        }    }    /**     * 创建选择菜单     * 加载自定义的菜单     * @param menu Interface for managing the items in a menu.     * @return     */    @Override    public boolean onCreateOptionsMenu(Menu menu) {        getMenuInflater().inflate(R.menu.menu,menu);        return true; //消费事件,处理自己的业务    }    /**     * 菜单选择项     * 判断选择了哪个菜单项,做响应的业务     * @param item 选中的菜单项     * @return     */    @Override    public boolean onOptionsItemSelected(MenuItem item) {        int itemId = item.getItemId();        switch (itemId){  //根据选中菜单项的id来显示不同的布局            case R.id.listView:  //线性布局                Intent intent = new Intent(StaggerActivity.this,MainActivity.class);                startActivity(intent);                break;            case R.id.gridView:  //宫格布局                Intent intent2 = new Intent(StaggerActivity.this,MainActivity.class);                startActivity(intent2);                break;            case R.id.horizontalStaggerGridView:  //水平瀑布流宫格布局                Intent intent3 = new Intent(StaggerActivity.this,MainActivity.class);                startActivity(intent3);                break;            case R.id.staggerGridView:  //垂直瀑布流宫格布局                mRecycleView.setLayoutManager(new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL));                break;        }        return true;    }}



2 0
原创粉丝点击