本文主要对回收视图的使用进行了详细介绍,文章结尾附上源码下载,具有一定的参考价值,下面跟着小编一起来看下吧
RecylerView介绍
再循环视图是支持-v7包中的新组件,是一个强大的滑动组件,与经典的列表视图相比,同样拥有项目回收复用的功能,这一点从它的名字再循环视图即回收视角也可以看出。官方对于它的介绍则是:回收视图是列表视图的升级版本,更加先进和灵活回收视图通过设置布局管理器、项目装饰、项目动画制作人实现你想要的效果。
使用布局管理器来确定每一个项目的排列方式。
使用项目装饰自己绘制分割线,更灵活
使用ItemAnimator为增加或删除一行设置动画效果。
注意
新建完项目,需要在app/build.gradle增加再循环视图依赖,不然找不到回收视图类
编译“通讯器。安卓。支持:回收器视图-V7:23。1 .0"
RecylerView简单的Demo
我们来看活动代码,跟列表视图写法差不多,只是这边多设置了布局管理器。
公共类LinearLayoutActivity扩展了AppCompatActivity {
私有回收视图回收视图
私有RecyclerViewAdapter适配器;
私有列表字符串数据;
@覆盖
创建时的公共void(绑定保存的实例状态){
超级棒。oncreate(savedInstanceState);
setContentView(r . layout。recycler _ main);
initData();
回收视图=(回收视图)findViewById(r . id。回收视图);
回收视图。setlayoutmanager(new LinearLayoutManager(this));//设置布局管理器
回收器视图。additem装饰(新的分隔项目装饰(this));
recyclerview。set adapter(adapter=new recyclervieadapter(this,datas));
}
私有void initData(){
datas=new ArrayList();
for(int I=0;i100i ){
数据。add( item: I );
}
}
}
活动对应的布局文件:recycler_main.xml
?可扩展标记语言版本=1.0 编码=utf-8 ?
相对布局xmlns:Android= http://模式。安卓。 com/apk/RES/Android
Android:layout _ width= match _ parent
Android:layout _ height= match _ parent
安卓。支持。V7。小部件。回收视图
Android:id= @ id/recycle view
Android:layout _ width= match _ parent
Android:layout _ height= match _ parent /
/RelativeLayout
适配器相对列表视图来说变化比较大的。把取景器逻辑封装起来了,代码相对简单一些。
需要继承回收查看.适配器,重写三个方法
MyViewHolder需要继承回收查看.取景器
公共类RecyclerViewAdapter扩展了回收查看.adapterrecyclerviedapter。我的视图持有者{
私有列表字符串数据;
私人布局充气机;
公共RecyclerViewAdapter(上下文上下文,列表字符串数据){
inflater=LayoutInflater.from(上下文);
this.datas=datas
}
//创建每一行的视角用回收查看.取景器包装
@覆盖
公共回收适配器MyViewHolder oncreateviewmolder(视图组parent,int viewType) {
查看项目视图=充气机。膨胀(r .布局。recycler _ item,null);
返回新的myview holder(项目视图);
}
//给每一行视角填充数据
@覆盖
公共void onBindViewHolder(recyclervieadapter .MyViewHolder holder,int位置){
持有人。文本视图。settext(数据。get(位置));
}
//数据源的数量
@覆盖
public int getItemCount() {
返回数据。size();
}
MyViewHolder类扩展回收查看.取景框{
私有文本视图文本视图
公共我的视图持有者(查看项目视图){
超级(项目视图);
TextView=(TextView)项目视图。findviewbyid(r . id。TextView);
}
}
}
我们来看看效果图:
RecyclerView增加分隔线
回收视图是没有安卓:分割线跟android:dividerHeight属性的,如果我们需要分割线,就只能自己动手去实现了。
需要继承项目装饰类,实现翁德劳跟getItemOffsets方法。
调用回收视图的附加装饰方法。
我们先写一个装饰类,继承回收查看.项目装饰,在getItemOffsets留出项目之间的间隔,然后就会调用翁德劳方法绘制(翁德劳的绘制优先于每一行的绘制)
公共类分隔符项目装饰扩展回收查看.项目装饰{
/*
*回收视图的布局方向,默认先赋值为纵向布局
*回收视图布局可横向,也可纵向
* 横向和纵向对应的分割线画法不一样
* */
private int memory=LinearLayoutManager .垂直;
private int mite msize=1;//项目之间分割线的尺寸,默认为一
私漆mPaint//绘制项目分割线的画笔,和设置其属性
公共分隔项目装饰(上下文上下文){
这(上下文,LinearLayoutManager .垂直,r .颜色颜色强调);
}
公共分隔符项装饰(上下文Context,int orientation) {
这(上下文、方向、r .颜色。颜色强调);
}
公共分隔符项装饰(Context Context,int orientation,int dividerColor){
this(context,orientation,dividerColor,1);
}
/**
* @param上下文
* @param方向绘制方向
* @param dividerColor分割线颜色颜色资源身份证明(识别)
* @param mItemSize分割线宽度传入数据处理值就行
*/
公共分隔符项装饰(Context Context,int orientation,int dividerColor,int mitesize){
this.mOrientation=方位
如果(定向!=LinearLayoutManager .垂直方向!=LinearLayoutManager .水平){
抛出新的IllegalArgumentException(请传入正确的参数) ;
}
//把数据处理值换算成像素
这个。mitesize=(int)类型值。应用维度(键入得值.COMPLEX_UNIT_DIP,mItemSize,context.getResources().getDisplayMetrics());
mPaint=新油漆(油漆ANTI _ ALIAS _ FLAG);
MP int。设置颜色(上下文。获取资源().getColor(分割线颜色));
}
@覆盖
公共void onDraw(Canvas c,RecyclerView parent,RecyclerView .State state) {
if(mori entation==LinearLayoutManager .垂直){
drawVertical(c,parent);
}否则{
drawHorizontal(c,parent);
}
}
/**
* 绘制纵向项目分割线
* @param画布
* @param parent
*/
私有void垂直绘制(Canvas Canvas,RecyclerView parent){
final int left=parent。get padding left();
final int right=parent。getmeasuredwidth()-parent。get padding right();
final int childSize=parent。获取子计数();
for(int I=0;我儿童尺寸;i ){
最终视图子视图=父视图。在(I)处获得孩子;
回收查看.layout params layout params=(回收视图.布局参数)子项。get layout params();
final int top=child。getbottom()布局参数。底部边距;
final int bottom=top mItemSize
canvas.drawRect(左、上、右、下、mPaint);
}
}
/**
* 绘制横向项目分割线
* @param画布
* @param parent
*/
私有水平绘制无效(画布Canvas,RecyclerView parent){
final int top=parent。get padding top();
最终int bottom=parent。getmeasuredheight()-parent。getpaddingbottom();
final int childSize=parent。获取子计数();
for(int I=0;我儿童尺寸;i ){
最终视图子视图=父视图。在(I)处获得孩子;
回收查看.layout params layout params=(回收视图.布局参数)子项。get layout params();
final int left=child。getright()布局参数。右边距;
final int right=left mItemSize
canvas.drawRect(左、上、右、下、mPaint);
}
}
/**
* 设置项目分割线的大小
* @param outRect
* @param视图
* @param parent
* @param状态
*/
@覆盖
公共void getitem偏移量(Rect outRect,View view,RecyclerView parent,RecyclerView .State state) {
if(mori entation==LinearLayoutManager .垂直){
outRect.set(0,0,0,mite msize);//垂直排列底部偏移
}否则{
outRect.set(0,0,mItemSize,0);//水平排列右边偏移
}
}
}
不要忘记调用附加装饰方法哦
回收器视图。additem装饰(新的分隔项目装饰(this));//添加分割线
重新运行,效果图:
大家读到这里肯定会有一个疑问,这货比列表视图麻烦多了啊,但是谷歌官方为什么要说是列表视图的升级版呢?接下来开始放大招。
GridLayoutManager
在回收视图中实现不同的列表,只需要切换不同的布局管理器即可回收查看.布局管理器跟回收查看.项目装饰一样,都是回收视图静态抽象内部类,但是布局管理器有三个官方写好的实现类。
LinearLayoutManager线性布局管理器跟列表视图功能相似
GridLayoutManager网格布局管理器跟显示数据表格(一种控件)功能相似
StaggeredGridLayoutManager瀑布流布局管理器
刚刚我们用的是LinearLayoutManager,现在我们切换到GridLayoutManager,看到下面这句代码,有没有感觉分分钟切换不同列表显示。
回收视图。setlayoutmanager(new GridLayoutManager(this,2));
如果要显示多列或者要纵向显示就新的不同的构造方法,以下代码纵向显示四列。当前如果你还需要反方向显示,把错误的改成真实的就可以。
回收视图。setlayoutmanager(new GridLayoutManager(this,4,GridLayoutManager .横,假));
因为用的是网格布局,所以呢绘制分割线的代码需要重新修改一下。网格布局一行可以有多列,并且最后一列跟最后一行不需要绘制,所以我们得重新创建一个类。
DividerGridItemDecoration.java
公共类划分网格划分扩展回收查看.项目装饰{
/*
*回收视图的布局方向,默认先赋值为纵向布局
*回收视图布局可横向,也可纵向
* 横向和纵向对应的分割线画法不一样
* */
private int memory=LinearLayoutManager .垂直;
private int mite msize=1;//项目之间分割线的尺寸,默认为一
私漆mPaint//绘制项目分割线的画笔,和设置其属性
公共除数上下文上下文){
这(上下文,LinearLayoutManager .垂直,r .颜色颜色强调);
}
公共分隔符griditemdecoration(上下文Context,int orientation) {
这(上下文、方向、r .颜色。颜色强调);
}
public DividerGridItemDecoration(Context Context,int orientation,int dividerColor){
this(context,orientation,dividerColor,1);
}
/**
* @param上下文
* @param方向绘制方向
* @param dividerColor分割线颜色颜色资源身份证明(识别)
* @param mItemSize分割线宽度传入数据处理值就行
*/
public DividerGridItemDecoration(Context Context,int orientation,int dividerColor,int mItemSize){
this.mOrientation=方位
如果(定向!=LinearLayoutManager .垂直方向!=LinearLayoutManager .水平){
抛出新的IllegalArgumentException(请传入正确的参数) ;
}
//把数据处理值换算成像素
这个。mitesize=(int)类型值。应用维度(键入得值.COMPLEX_UNIT_DIP,mItemSize,context.getResources().getDisplayMetrics());
mPaint=新油漆(油漆ANTI _ ALIAS _ FLAG);
MP int。设置颜色(上下文。获取资源().getColor(分割线颜色));
}
@覆盖
公共void onDraw(Canvas c,RecyclerView parent,RecyclerView .State state) {
drawHorizontal(c,parent);
drawVertical(c,parent);
}
private int getSpanCount(回收视图父级){
//列数
int span计数=-1;
回收查看.布局管理器布局管理器=父级。getlayoutmanager();
GridLayoutManager的布局管理器实例){
span count=((GridLayoutManager)布局管理器).getSpanCount();
} else if(StaggeredGridLayoutManager的布局管理器实例){
span count=((StaggeredGridLayoutManager)布局管理器).getSpanCount();
}
返回跨距计数;
}
公共水平绘制无效(画布Canvas,RecyclerView parent) {
int子计数=父计数。获取子计数();
for(int I=0;我数孩子;i ) {
最终视图子视图=父视图。在(I)处获得孩子;
最终回收视图布局参数params=(循环视图.布局参数)子项。get layout params();
final int left=child。get left()-params。左边距;
final int right=child。getright()参数。右边界螨虫;
final int top=child。getbottom()参数。底部边距;
final int bottom=top mItemSize
canvas.drawRect(左、上、右、下、mPaint);
}
}
公共void drawVertical(Canvas canvas,RecyclerView parent) {
最终int子计数=父计数。获取子计数();
for(int I=0;我数孩子;i ) {
最终视图子视图=父视图。在(I)处获得孩子;
最终回收视图布局参数params=(循环视图.布局参数)子项。get layout params();
final int top=child。gettop()-params。上边距;
final int bottom=child。getbottom()参数。底部边距;
final int left=child。getright()参数。右边距;
final int right=left mItemSize
canvas.drawRect(左、上、右、下、mPaint);
}
}
@覆盖
公共void getitem偏移量(Rect outRect,int itemPosition,RecyclerView parent) {
int spanCount=getSpanCount(parent);
int子计数=父计数。获取适配器().getItemCount();
if(is lasrow(parent,itemPosition,spanCount,childCount)){//如果是最后一行,不需要绘制底部
outRect.set(0,0,mItemSize,0);
} else if (isLastColum(parent,itemPosition,spanCount,childCount)){//如果是最后一列,不需要绘制右边
outRect.set(0,0,0,mite msize);
}否则{
outRect.set(0,0,mItemSize,mite msize);
}
}
private boolean isLastColum(循环视图父级,int pos,int spanCount,int childCount) {
回收查看.布局管理器布局管理器=父级。getlayoutmanager();
GridLayoutManager的布局管理器实例){
如果(位置1) % spanCount==0){//如果是最后一列,则不需要绘制右边
返回真实的
}
} else if(StaggeredGridLayoutManager的布局管理器实例){
int orientation=((StaggeredGridLayoutManager)布局管理器).get orientation();
if(orientation==StaggeredGridLayoutManager .垂直){
如果(位置1) % spanCount==0){//如果是最后一列,则不需要绘制右边
返回真实的
}
}否则{
子计数=子计数-子计数% span计数;
if (pos=childCount)//如果是最后一列,则不需要绘制右边
返回真实的
}
}
返回错误的
}
private boolean是straw(recycle view parent,int pos,int spanCount,int childCount) {
回收查看.布局管理器布局管理器=父级。getlayoutmanager();
GridLayoutManager的布局管理器实例){
子计数=子计数-子计数% span计数;
if (pos=childCount)//最后一行
返回真实的
} else if(StaggeredGridLayoutManager的布局管理器实例){
int orientation=((StaggeredGridLayoutManager)布局管理器).get orientation();
if(orientation==StaggeredGridLayoutManager .垂直){//纵向
子计数=子计数-子计数% span计数;
if (pos=childCount)//最后一行
返回真实的
} else{ //横向
如果(位置1) % spanCount==0) {//是最后一行
返回真实的
}
}
}
返回错误的
}
}
写了这两个画分割线的类,主流的布局:线性列表跟网格列表都能展示了。赶紧运行代码看看结果:
StaggeredGridLayoutManager
活动性中修改下布局管理器,大家应该感觉很熟悉了吧~~~
recyclerView.setLayoutManager(新StaggeredGridLayoutManager(3,StaggeredGridLayoutManager .垂直));
瀑布流列表一般列的高度是不一致的,为了模拟不同的宽高,数据源我把线类型改成了对象。然后初始化的时候随机了一个高度。
公共类项目数据{
私有字符串内容;//项目内容
私有int高度;//项目高度
公共项目数据(){
}
公共项目数据(字符串内容,整数高度){
this.content=内容;
this.height=高度;
}
公共字符串getContent() {
返回内容;
}
公共void setContent(字符串内容){
this.content=内容;
}
public int getHeight() {
返回高度;
}
公共空设置高度(int height){
this.height=高度;
}
}
瀑布流列表没有添加分割线,给项目布局设置了安卓:填充属性。recycler_staggered_item.xml
?可扩展标记语言版本=1.0 编码=utf-8 ?
框架布局xmlns:Android= http://模式。安卓。 com/apk/RES/Android
android:padding=5dp
Android:layout _ width= wrap _ content
Android:layout _ height= match _ parent
文本视图
android:id=@ id/textview
Android:background= @ color/color accent
android:layout_width=100dp
Android:layout _ height= wrap _ content
安卓:重力=中心
android:text=122
android:textSize=20sp/
/框架布局
最后我们在适配器的onBindViewHolder方法中给项目d中的文本视图设置一个高度
@覆盖
public void onBindViewHolder(StaggeredGridAdapter .MyViewHolder holder,int位置){
项目数据项目数据=数据。get(位置);
持有人。文本视图。settext(项目数据。get content());
//手动更改高度,不同位置的高度有所不同
持有人。文本视图。设置高度(项目数据。get height());
}
是不是感觉如此简单,赶紧运行看看效果:
添加header跟footer
回收视图添加头部跟底部是没有对应的美国石油学会(美国石油协会)的,但是我们很多的需求都会用到,于是只能自己想办法实现了。我们可以通过适配器的getItemViewType方法来实现这个功能。
修改后的适配器代码:recyclerheadfootviewadapter。Java 语言(一种计算机语言,尤用于创建网站)
公共类RecyclerHeadFootViewAdapter扩展了回收查看.AdapterRecyclerView。取景框{
私有列表字符串数据;
私人布局充气机;
public static final int TYPE _ HEADER=1;//标头类型
public static final int TYPE _ FOOTER=2;//页脚类型
私有视图头=null//头视角
私有视图页脚=null//脚视角
公共RecyclerHeadFootViewAdapter(上下文上下文,列表字符串数据){
inflater=LayoutInflater.from(上下文);
this.datas=datas
}
//创建每一行的视角用回收查看.取景器包装
@覆盖
公共回收视图视图持有者oncreateview holder(视图组parent,int viewType) {
if(viewType==TYPE_HEADER){
返回新的回收查看.视图持有人(表头){ };
} else if(view TYPE==TYPE _ FOOTER){
返回新的回收查看.取景框(页脚){ };
}
查看项目视图=充气机。膨胀(r .布局。recycler _ item,null);
返回新的myview holder(项目视图);
}
//给每一行视角填充数据
@覆盖
公共void onBindViewHolder(回收器视图.取景器支架,中间位置){
if(getItemViewType(position)==TYPE _ HEADER | | getItemViewType(position)==TYPE _ FOOTER){
返回;
}
我的观点持有人my holder=(我的观点持有人)holder;
我的固定器。文本视图。settext(数据。get(获取真实位置(位置)));
}
//如果有头部位置的位置是从一开始的所以需要-1
public int getRealPosition(int position){
返回头==null?位置:位置-1;
}
//数据源的数量
@覆盖
public int getItemCount() {
if(header==null footer==null){//没有头跟脚
返回数据。size();
}else if(表头==空表尾!=null){//head为空脚不为空
返回数据。size()1;
}else if(表头!=空页脚==null){//页眉不为空脚为空
返回数据。size()1;
}否则{
返回数据。size()2;//头不为空脚不为空
}
}
@覆盖
public int getItemViewType(int position){
//如果头布局不为空位置是第一个那就是头类型
如果(表头!=nullposition==0){
返回类型_标题
}else if(页脚!=null position==getItemCount()-1){//如果页脚不为空最后一个
返回类型_页脚
}
返回超级棒。getitemviewtype(位置);
}
公共void setHeader(查看标题){
this.header=header
notifyItemInserted(0);//在位置0插入一条数据,然后刷新
}
公共void设置页脚(视图页脚){
this.footer=页脚;
notifyItemInserted(数据。size()-1);//在尾部插入一条数据,然后刷新
}
MyViewHolder类扩展回收查看.取景框{
私有文本视图文本视图
公共我的视图持有者(查看项目视图){
超级(项目视图);
TextView=(TextView)项目视图。findviewbyid(r . id。TextView);
}
}
}
getItemCount
有页眉跟页脚的时候需要在源数据长度基础上进行增加。
getItemViewType
通过getItemViewType判断不同的类型
onCreateViewHolder
通过不同的类型创建项目的视角
onBindViewHolder
如果是页眉跟页脚类型是不需要绑定数据的,标题跟页脚的视角一般在活动性中创建,不需要这边做处理,所以这两种类型我们就不往下执行,如果有头布局,位置==0的位置被页眉占用了,但是我们的数据源也就是集合的下标是从0开始的,所以这里需要-1。
setHeader
设置头布局,在第一行插入一条数据,然后刷新。注意这个方法调用后会有插入的动画,这个动画可以使用默认的,也可以自己定义
设置页脚
设置尾部布局,在尾部插入一条数据,然后刷新。
添加页眉跟页脚的方法终于封装好了,在活动中只需要两行代码就能添加标题,跟列表视图调用添加标题方法一样简单,又可以幸福的的玩耍了。这里需要注意的是我们初始化视角的时候,膨胀方法需要三个参数。
资源资源身份证明(识别)
根父视角
attachToRoot true:返回父查看错误:返回资源身份证明(识别)生成的视角
//添加页眉
view header=layoutinflater。从(这)开始.膨胀(r .布局。recycler _ header,recyclerView,false);
适配器。设置表头(表头);
//添加页脚
视图页脚=布局页脚。从(这)开始.膨胀(r .布局。recycler _ footer,recyclerView,false);
adapter.setFooter(页脚);
回收器_表头跟回收器_页脚布局文件我就不贴出来了,就一个文本视图,我们直接看效果图:
item点击事件增加或删除带动画效果
当我们调用回收视图的setOnItemClickListener方法的时候,发现居然没有,用了回收视图你要习惯什么东西都自己封装。
首先我们从适配器开刀,内部写一个接口,一个实例变量,提供一个公共方法,设置监听。
私有RecyclerViewItemClick RecyclerViewItemClick;
public void setRecyclerViewItemClick(RecyclerViewItemClick RecyclerViewItemClick){
这个。recyclerViewItemClick=recyclerViewItemClick;
}
公共接口RecyclerViewItemClick{
/**
*项目点击
* @param实数位置数据源位置
* @param位置视图位置
*/
void onItemClick(int实数位置,int位置);
}
在onBindViewHolder方法中给项目监听点击事件
if(recyclerViewItemClick!=null) {
我的固定器。项目视图。setonclicklistener(新视图OnClickListener() {
@覆盖
公共void onClick(视图五){
recyclerviewitemclick。onitemclick(getRealPosition(position),position);
}
});
}
在活动的onCreate方法中进行监听,顺便设置项目增加删除动画。我用的是软件开发工具包自带的默认动画。
适配器。setrecyclerviewitemclick(recyclerViewItemClick);
回收视图。setitem animator(new default item animator());
私有RecyclerHeadFootViewAdapter .RecyclerViewItemClick RecyclerViewItemClick=new RecyclerHeadFootViewAdapter .RecyclerViewItemClick() {
@覆盖
public void onItemClick(int real position,int position) {
Log.i(安森,删除数据:"真实位置"视图位置:位置);
Log.i(安森,当前位置:"位置"更新项目数量:(适配器。getitemcount()-position-1));
数据。移除(真实位置);//删除数据源
adapter.notifyItemRemoved(位置);//项目移除动画
//更新位置至adapter.getItemCount()-1的数据
适配器。notifyitemrangechanged(位置,适配器。getitemcount()-position-1);
}
};
源码下载
回收视图
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持我们!