前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android开发笔记(一百二十二)循环器视图RecyclerView

Android开发笔记(一百二十二)循环器视图RecyclerView

作者头像
aqi00
发布2019-01-18 14:37:38
2.3K0
发布2019-01-18 14:37:38
举报
文章被收录于专栏:老欧说安卓老欧说安卓

RecyclerView

RecyclerView是Android在support-v7库中新推出控件,中文别名为循环器视图,它的功能非常强大,可分别实现ListView、GridView,以及瀑布流网格的显示效果。 RecyclerView相关工程在sdk中的路径为sdk\extras\android\support\v7\recyclerview,不过幸好用它不像用Toolbar那样麻烦,要想使用Toolbar得先导入并引用v7-appcompat工程(具体步骤参见《Android开发笔记(一百一十九)工具栏Toolbar》),而使用RecyclerView只需像其他第三方jar一样往libs目录添加android-support-v7-recyclerview.jar就好了。 但是若在Eclipse/ADT中调用RecyclerView,可能app运行时会报错“Caused by: java.lang.NoClassDefFoundError: android.support.v7.recyclerview.R$styleable”,这时就不能使用sdk\extras\android\support\v7\recyclerview下面的jar包,而要到extras/android/m2repository/com/android/support/recyclerview-v7目录下,在版本号21.0.0的子目录中找到recyclerview-v7-21.0.0.aar,该aar文件其实是个压缩文件,解压该文件可得到classes.jar,将该jar包更名并加入到你的工程,上面的运行错误应该就没有了。  下面看看强悍的RecyclerView都提供了哪些常用方法: setAdapter : 设置列表项的适配器。有关适配器的详细说明见下一标题。 setLayoutManager : 设置列表项的布局管理器。目前有三种,分别是:线性布局管理器LinearLayoutManager、网格布局管理器GridLayoutManager、瀑布流网格布局管理器StaggeredGridLayoutManager。有关布局管理器的详细说明见本文的后半部分。 addItemDecoration : 添加列表项的分割线。 removeItemDecoration : 移除列表项的分割线。 setItemAnimator : 设置列表项的增删动画。 addOnItemTouchListener : 添加列表项的触摸监听器。因为RecyclerView没有实现列表项的点击接口,所以开发者可通过这里的触摸监听器来监控用户手势。 removeOnItemTouchListener : 移除列表项的触摸监听器。

循环适配器

RecyclerView有专门的适配器类,即RecyclerView.Adapter。在调用RecyclerView的setAdapter方法前,我们要先实现一个从RecyclerView.Adapter派生而来的数据适配器,用来定义列表项的布局与具体操作。下面是与RecyclerView.Adapter相关的常用方法: 下面是自定义适配器必须要重写的方法: getItemCount : 获得列表项的数目。 onCreateViewHolder : 创建整个布局的视图持有者。输入参数中包括视图类型,可根据视图类型加载不同的布局,从而实现带头部的列表布局。 onBindViewHolder : 绑定每项的视图持有者。 下面是可以重写也可以不重写的方法: getItemViewType : 返回每项的视图类型。这里返回的视图类型给onCreateViewHolder方法使用。 getItemId : 获得每项的编号。 下面是可以直接调用的方法: notifyItemInserted : 通知适配器在指定位置插入了新项。 notifyItemRemoved : 通知适配器在指定位置删除了原有项。 notifyItemChanged : 通知适配器在指定位置的项目发生了变化。 notifyDataSetChanged : 通知适配器整个列表的数据发生了变化。 总的来说,RecyclerView.Adapter与我们之前经常遇到的BaseAdapter在处理流程上是基本一致的,当然它们之间也有不小的差异,下面是RecyclerView.Adapter和其他适配器的主要区别: 1、自带ViewHolder及其重用功能,无需开发者手工重用ViewHolder; 2、未自带列表项的点击和长按功能,需要开发者自己实现点击和长按事件的监听; 3、增加区分不同列表项的视图类型,方便开发者根据类型加载不同的布局; 4、可单独对个别项进行增删改操作,无需刷新整个列表; 下面是RecyclerView.Adapter的一个自定义类的代码例子:

代码语言:javascript
复制
import com.example.exmrecycler.R;
import com.example.exmrecycler.interfaces.OnItemClickListener;
import com.example.exmrecycler.interfaces.OnItemLongClickListener;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

public class RecyclerAdapter extends RecyclerView.Adapter<ViewHolder> 
		implements OnItemClickListener, OnItemLongClickListener {
	private final static String TAG = "RecyclerAdapter";
	private Context mContext;
	private LayoutInflater mInflater;
	private int mType;
	private String[] mTitleArray;
	
	public RecyclerAdapter(Context context, int type, String[] titleArray) {
		mContext = context;
		mInflater = LayoutInflater.from(context);
		mType = type;
		mTitleArray = titleArray;
	}

	@Override
	public int getItemCount() {
		return mTitleArray.length;
	}

	@Override
	public ViewHolder onCreateViewHolder(ViewGroup vg, int viewType) {
		View v = null;
		ViewHolder holder = null;
		v = mInflater.inflate(R.layout.list_title, vg, false);
		holder = new TitleHolder(v);
		return holder;
	}

	@Override
	public void onBindViewHolder(ViewHolder vh, final int position) {
		TitleHolder holder = (TitleHolder) vh;
		holder.tv_seq.setText(""+(position+1));
		holder.tv_title.setText(mTitleArray[position]);
		LayoutParams params = holder.ll_item.getLayoutParams();
		if (mType == 1) {  //表示是线性布局
			params.height = 50;
			holder.ll_item.setLayoutParams(params);
		} else if (mType == 2) {  //表示是网格布局
			params.height = 100;
			holder.ll_item.setLayoutParams(params);
		} else {  //表示是瀑布流网格布局
			params.height = (int) Math.round(300 * Math.random());
			if (params.height < 60) {
				params.height = 60;
			}
			//很奇怪,setLayoutParams对瀑布流网格不起作用,只能用setHeight
			holder.tv_title.setHeight(params.height);
		}
		
		//列表项的点击事件需要自己实现
		holder.ll_item.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				if (mOnItemClickListener != null) {
					mOnItemClickListener.onItemClick(v, position);
				}
			}
		});
		holder.ll_item.setOnLongClickListener(new OnLongClickListener() {
			@Override
			public boolean onLongClick(View v) {
				if (mOnItemLongClickListener != null) {
					mOnItemLongClickListener.onItemLongClick(v, position);
				}
				return true;
			}
		});
	}
	
	@Override
	public int getItemViewType(int position) {
		//这里返回每项的类型,开发者可自定义头部类型与一般类型,
		//然后在onCreateViewHolder方法中根据类型加载不同的布局,从而实现带头部的网格布局
		return 0;
	}
	
	@Override
	public long getItemId(int position) {
		return position;
	}

	public class TitleHolder extends RecyclerView.ViewHolder {
		public LinearLayout ll_item;
		public TextView tv_seq;
		public TextView tv_title;

		public TitleHolder(View v) {
			super(v);
			ll_item = (LinearLayout) v.findViewById(R.id.ll_item);
			tv_seq = (TextView) v.findViewById(R.id.tv_seq);
			tv_title = (TextView) v.findViewById(R.id.tv_title);
		}
		
	}

	private OnItemClickListener mOnItemClickListener;

	public void setOnItemClickListener(OnItemClickListener listener) {
		this.mOnItemClickListener = listener;
	}

	private OnItemLongClickListener mOnItemLongClickListener;

	public void setOnItemLongClickListener(OnItemLongClickListener listener) {
		this.mOnItemLongClickListener = listener;
	}

	@Override
	public void onItemClick(View view, int position) {
		String desc = String.format("您点击了第%d项,内容是%s", position+1, mTitleArray[position]);
		Toast.makeText(mContext, desc, Toast.LENGTH_SHORT).show();
	}
	
	@Override
	public void onItemLongClick(View view, int position) {
		String desc = String.format("您长按了第%d项,内容是%s", position+1, mTitleArray[position]);
		Toast.makeText(mContext, desc, Toast.LENGTH_SHORT).show();
	}

}

布局管理器

布局管理器LayoutManager是RecyclerView的精髓,也是RecyclerView之所以强悍的源泉。它不但提供了三类布局管理,分别实现类似ListView、GridView、瀑布流网格的效果,而且可在代码中随时由RecyclerView调用setLayoutManager方法设置新的布局;一旦调用了setLayoutManager方法,界面就会根据新布局刷新列表项,这个特性特别适合于手机在竖屏/横屏之间的显示切换(如竖屏时展示ListView,横屏时展示GridView),也适合在不同屏幕分辨率如手机/平板之间的显示切换(如手机上展示ListView,平板上展示GridView)。

LinearLayoutManager

线性布局管理器LinearLayoutManager类似于LinearLayout,当它是垂直方向布局时,则展示效果类似于ListView;当它是水平方向布局时,则展示效果类似于HorizontalListView,当然这个HorizontalListView不是Android的原生控件,而是大神们自定义的控件,有关HorizontalListView的说明参见《Android开发笔记(一百零一)滑出式菜单》。 下面是LinearLayoutManager的常用方法: 构造函数 : 可指定列表的方向与是否为相反方向开始布局。 setOrientation : 单独设置列表的方向。 setReverseLayout : 单独设置是否为相反方向开始布局。默认false,如果设置为true,那么垂直方向将从下往上开始布局,水平方向将从右往左开始布局。 下面是线性布局的效果截图:

下面是LinearLayoutManager的示例代码:

代码语言:javascript
复制
import com.example.exmrecycler.adapter.RecyclerAdapter;
import com.example.exmrecycler.widget.DividerItemDecoration;

import android.app.Activity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.widget.LinearLayout;

public class LinearActivity extends Activity {
	
	private RecyclerView rv_linear;
	private LinearLayoutManager mLayoutManager;
	private RecyclerAdapter mAdapter;
	private String[] starArray = {"水星", "金星", "地球", "火星", "木星", "土星"};
	private String[] yearArray = {"鼠年", "牛年", "虎年", "兔年", "龙年", "蛇年",
			"马年", "羊年", "猴年", "鸡年", "狗年", "猪年"};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_linear);
		
		rv_linear = (RecyclerView) findViewById(R.id.rv_linear);
		
		mLayoutManager = new LinearLayoutManager(this);
		mLayoutManager.setOrientation(LinearLayout.VERTICAL);
		rv_linear.setLayoutManager(mLayoutManager);
		
		mAdapter = new RecyclerAdapter(this, 1, yearArray);
		rv_linear.setAdapter(mAdapter);
		
		rv_linear.setItemAnimator(new DefaultItemAnimator());
		rv_linear.addItemDecoration(new DividerItemDecoration(this, 
				DividerItemDecoration.VERTICAL_LIST));
	}
	
}

GridLayoutManager

网格布局管理器GridLayoutManager类似于GridLayout,GridLayout是Android4.0新增的布局类型。话说Android陆陆续续增加了一些布局,比如前几节提到的侧滑布局SlidingPaneLayout和DrawerLayout(详细说明参见《Android开发笔记(一百二十)两种侧滑布局》),还有下一节要介绍的SwipeRefreshLayout(详细说明参见《Android开发笔记(一百二十三)下拉刷新布局》),这些新布局着实增加了广大码农的学习时间,所以还是能省则省,从展示效果来看,GridLayoutManager类似于GridView,所以就不再另外学习GridLayout了。 下面是GridLayoutManager的常用方法: 构造函数 : 可指定网格的列数。 setSpanCount : 单独设置网格的列数。 setSpanSizeLookup : 设置列表项的占位规则。默认一项占一列,如果想某项占多列,则可在此设置自定义的占位规则,即由抽象类GridLayoutManager.SpanSizeLookup派生出具体的实现类。 下面是网格布局的效果截图:

下面是GridLayoutManager的示例代码:

代码语言:javascript
复制
import com.example.exmrecycler.adapter.RecyclerAdapter;
import com.example.exmrecycler.widget.DividerGridItemDecoration;

import android.app.Activity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;

public class GridActivity extends Activity {
	
	private RecyclerView rv_grid;
	private GridLayoutManager mLayoutManager;
	private RecyclerAdapter mAdapter;
	private String[] yearArray = {"鼠年", "牛年", "虎年", "兔年", "龙年", "蛇年",
			"马年", "羊年", "猴年", "鸡年", "狗年", "猪年"};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_grid);
		
		rv_grid = (RecyclerView) findViewById(R.id.rv_grid);
		
		mLayoutManager = new GridLayoutManager(this, 4);
//		mLayoutManager.setSpanCount(3);
//		//以下占位规则的意思是:第一项占四列,第二列和第三项各占两列
//		//如果网格的列数为四,那么第一项将占满第一行,第二列和第三项平分第二行,第三行开始每行有四项
//		mLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
//            @Override
//            public int getSpanSize(int position) {
//            	if (position == 0) {
//                    return 4;
//            	} else if (position==1 || position==2) {
//                    return 2;
//            	} else {
//                    return 1;
//            	}
//            }
//        });
		rv_grid.setLayoutManager(mLayoutManager);
		
		mAdapter = new RecyclerAdapter(this, 2, yearArray);
		rv_grid.setAdapter(mAdapter);
		
		rv_grid.setItemAnimator(new DefaultItemAnimator());
		rv_grid.addItemDecoration(new DividerGridItemDecoration(this));
	}
	
}

StaggeredGridLayoutManager

瀑布流网格相信大家多多少少都有所了解了,如有不太清楚的,可参考《Android开发笔记(二十二)瀑布流网格》。之前我们要想实现瀑布流效果,都得自定义控件或者借助于第三方开源库如StaggeredGridView、PinterestLikeAdapterView等等;现在Android在support-v7库中推出了StaggeredGridLayoutManager,这让我们对瀑布流效果的开发大大简化了,只要在适配器代码中动态设置每个网格的高度,系统便会自动在界面上依次排列瀑布流网格。 下面是StaggeredGridLayoutManager的常用方法: 构造函数 : 可指定网格的列数和方向。 setSpanCount : 单独设置网格的列数。 setOrientation : 单独设置瀑布流布局的方向。LinearLayout.VERTICAL表示垂直方向,LinearLayout.HORIZONTAL表示水平方向,通常默认LinearLayout.VERTICAL。 setReverseLayout : 设置是否为相反方向开始布局。默认false,如果设置为true,那么垂直方向将从下往上开始布局,水平方向将从右往左开始布局。 下面是瀑布流网格布局的效果截图:

下面是StaggeredGridLayoutManager的示例代码:

代码语言:javascript
复制
import com.example.exmrecycler.adapter.RecyclerAdapter;
import com.example.exmrecycler.widget.SpacesItemDecoration;

import android.app.Activity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.widget.LinearLayout;

public class StaggeredActivity extends Activity {
	
	private RecyclerView rv_staggered;
	private StaggeredGridLayoutManager mLayoutManager;
	private RecyclerAdapter mAdapter;
	private String[] yearArray = {"鼠年", "牛年", "虎年", "兔年", "龙年", "蛇年",
			"马年", "羊年", "猴年", "鸡年", "狗年", "猪年"};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_staggered);
		
		rv_staggered = (RecyclerView) findViewById(R.id.rv_staggered);
		
		mLayoutManager = new StaggeredGridLayoutManager(4, LinearLayout.VERTICAL);
//		mLayoutManager.setSpanCount(3);
//		mLayoutManager.setOrientation(LinearLayout.HORIZONTAL);
//		mLayoutManager.setReverseLayout(true);
		rv_staggered.setLayoutManager(mLayoutManager);
		
		mAdapter = new RecyclerAdapter(this, 3, yearArray);
		rv_staggered.setAdapter(mAdapter);
		
		rv_staggered.setItemAnimator(new DefaultItemAnimator());
		//每项周围的空隙是5,那么项与项之间的间隔就是5+5=10。
		SpacesItemDecoration decoration=new SpacesItemDecoration(5);
		rv_staggered.addItemDecoration(decoration);
	}
	
}

点击下载本文用到的RecyclerView的工程代码 点此查看Android开发笔记的完整目录

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016年08月31日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • RecyclerView
  • 循环适配器
  • 布局管理器
    • LinearLayoutManager
      • GridLayoutManager
        • StaggeredGridLayoutManager
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档