Android RecyclerView使用详解

     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
     */
	@Override
	public 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
     */
	@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);
		}
	}
}


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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值