RecyclerView 分割线和 Item默认增删动画
虽然RecyclerView出现已经有一段时间了,但是还是想要自己总结一下,总的来说其基本使用方法:
你想要控制其显示的方式,请通过布局管理器LayoutManager;
你想要控制Item间的间隔(可绘制),请通过ItemDecoration;
你想要控制Item增删的动画,请通过ItemAnimator;
你想要控制点击、长按事件,不好意思你得自己写喽;
鉴于我们对于ListView的使用特别的熟悉,对比下RecyclerView的使用代码:
我添加的依赖是:compile 'com.android.support:recyclerview-v7:24.0.0+'
//RecyclerView的三种常用Manager
mRecyclerView = findView(R.id.id_recyclerview);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, OrientationHelper.HORIZONTAL, false);
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 3, OrientationHelper.VERTICAL, false);
StaggeredGridLayoutManager sglm = newGridLayoutManager和StaggeredGridLayoutManager(4, OrientationHelper.VERTICAL);
mRecyclerView.setLayoutManager(gridLayoutManager);//设置布局管理器
mRecyclerView.setAdapter(adapter); //设置adapter
mRecyclerView.setItemAnimator(new DefaultItemAnimator());//添加默认Item增删动画而非初始化时的动画
//添加分割线
mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(),DividerItemDecoration.HORIZONTAL_LIST));
RecyclerView的分割线的设置:
RecyclerView只管回收与复用View,其他的你可以自己去设置。可以看出其高度的解耦,而且RecyclerView并没有像ListView一样有divide属性,
RecyclerView的分割线需要自己去设置,首先需要准备一个分割线类来实现ItemDecoration:
public static abstract class ItemDecoration {public void onDraw(Canvas c, RecyclerView parent, State state) {onDraw(c, parent);}public void onDrawOver(Canvas c, RecyclerView parent, State state) {onDrawOver(c, parent);}public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),parent);
}@Deprecated
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {outRect.set(0, 0, 0, 0);}
当我们调用mRecyclerView.addItemDecoration()
方法添加decoration的时候,RecyclerView在绘制的时候,去会绘制decorator,即调用该类的onDraw和onDrawOver方法,
- onDraw方法先于drawChildren
- onDrawOver在drawChildren之后,一般我们选择复写其中一个即可。
- getItemOffsets 可以通过outRect.set()为每个Item设置一定的偏移量,主要用于绘制Decorator。
接下来我们看一个RecyclerView.ItemDecoration
的实现类,该类很好的实现了RecyclerView添加分割线,我们先来看使用LinearLayoutManager时定义的分割线实现类:
/*** 描述:该类用于RecyclerView采用LinearLayoutManager时的分割线设置*/
public class RecycleLinearItemDecoration extends RecyclerView.ItemDecoration {/*** android.R.attr.listDivider:这是系统自带的分割线,可以找到Application theme,在theme中* 添加<item name="android:listDivider">@drawable/divider_bg</item>来设计自己的分割线* */private 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 RecycleLinearItemDecoration(Context context, int orientation) {final TypedArray a = context.obtainStyledAttributes(ATTRS);mDivider = a.getDrawable(0);a.recycle();setOrientation(orientation);}public void setOrientation(int orientation) {if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {throw new IllegalArgumentException("invalid orientation");}mOrientation = orientation;}@Overridepublic void onDraw(Canvas c, RecyclerView parent) { //系统自带Canvasif (mOrientation == VERTICAL_LIST) {drawVertical(c, parent);} else {drawHorizontal(c, 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++) { //注意这里的childCount是否需要-1final View child = parent.getChildAt(i);final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();final int top = child.getBottom() + params.bottomMargin;//item的下边开始设置分割线final int bottom = top + mDivider.getIntrinsicHeight();mDivider.setBounds(left, top, right, bottom);mDivider.draw(c);}}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++) { //注意这里的childCount是否需要-1final View child = parent.getChildAt(i);final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();final int left = child.getRight() + params.rightMargin; //item的右边开始设置分割线final int right = left + mDivider.getIntrinsicHeight();mDivider.setBounds(left, top, right, bottom);mDivider.draw(c);}}@Overridepublic void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {if (mOrientation == VERTICAL_LIST) {//通过outRect.set()为每个Item设置一定的偏移量,主要用于绘制Decorator,Intrinsic:固有的outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());} else {outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);}}
}
该实现类可以看到通过读取系统主题中的 Android.R.attr.listDivider
作为Item间的分割线,并且支持横向和纵向。如果你不清楚它是怎么做到的读取系统的属性用于自身,请参考我的另一篇博文:Android 深入理解Android中的自定义属性
我们在原来的代码中添加一句:
mRecyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL_LIST));
- 1
- 2
- 1
- 2
ok,现在再运行,就可以看到分割线的效果了。
该分割线是系统默认的,你可以在theme.xml中找到该属性的使用情况。那么,使用系统的listDivider有什么好处呢?就是方便我们去随意的改变,该属性我们可以直接声明在:
<!-- Application theme. --><style name="AppTheme" parent="AppBaseTheme"><item name="android:listDivider">@drawable/divider_bg</item> </style>
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
然后自己写个drawable即可,下面我们换一种分隔符:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"android:shape="rectangle" ><gradientandroid:centerColor="#ff00ff00"android:endColor="#ff0000ff"android:startColor="#ffff0000"android:type="linear" /><size android:height="4dp"/></shape>
再来让我们看下GridLayoutManager时定义的分割线实现类(因为这里每个item需要四条分割线,所以需要重定义):
/*** 描述:该类用于RecyclerView采用GridLayoutManager和StaggeredGridLayoutManager时的分割线设置*/public class DividerGridItemDecoration extends RecyclerView.ItemDecoration {private static final int[] ATTRS = new int[]{android.R.attr.listDivider};private Drawable mDivider;public DividerGridItemDecoration(Context context) {final TypedArray a = context.obtainStyledAttributes(ATTRS);mDivider = a.getDrawable(0);a.recycle();}@Overridepublic void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {drawHorizontal(c, parent);drawVertical(c, parent);}private int getSpanCount(RecyclerView parent) {// 列数int spanCount = -1;LayoutManager layoutManager = parent.getLayoutManager();if (layoutManager instanceof GridLayoutManager) {spanCount = ((GridLayoutManager) layoutManager).getSpanCount();} else if (layoutManager instanceof StaggeredGridLayoutManager) {spanCount = ((StaggeredGridLayoutManager) layoutManager).getSpanCount();}return spanCount;}public void drawHorizontal(Canvas c, RecyclerView parent) { //决定水平分割线int childCount = parent.getChildCount();int spanCount = getSpanCount(parent);for (int i = 0; i < childCount; i++) {if(isLastRaw(parent, i, spanCount, childCount)) {continue;}final View child = parent.getChildAt(i);final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();final int left = child.getLeft() - params.leftMargin;final int right = child.getRight() + params.rightMargin+ mDivider.getIntrinsicWidth();final int top = child.getBottom() + params.bottomMargin;final int bottom = top + mDivider.getIntrinsicHeight();mDivider.setBounds(left, top, right, bottom);mDivider.draw(c);}}public void drawVertical(Canvas c, RecyclerView parent) { //决定竖分割线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 top = child.getTop() - params.topMargin;final int bottom = child.getBottom() + params.bottomMargin;final int left = child.getRight() + params.rightMargin;final int right = left + mDivider.getIntrinsicWidth();mDivider.setBounds(left, top, right, bottom);mDivider.draw(c);}}private boolean isLastColum(RecyclerView parent, int pos, int spanCount, int childCount) {LayoutManager layoutManager = parent.getLayoutManager();if (layoutManager instanceof GridLayoutManager) {if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边{return true;}} else if (layoutManager instanceof StaggeredGridLayoutManager) {int orientation = ((StaggeredGridLayoutManager) layoutManager).getOrientation();if (orientation == StaggeredGridLayoutManager.VERTICAL) {if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边{return true;}} else {childCount = childCount - childCount % spanCount;if (pos >= childCount)// 如果是最后一列,则不需要绘制右边return true;}}return false;}private boolean isLastRaw(RecyclerView parent, int pos, int spanCount,int childCount) {LayoutManager layoutManager = parent.getLayoutManager();if (layoutManager instanceof GridLayoutManager) {childCount = childCount - childCount % spanCount;if (pos >= childCount)// 如果是最后一行,则不需要绘制底部return true;} else if (layoutManager instanceof StaggeredGridLayoutManager) {int orientation = ((StaggeredGridLayoutManager) layoutManager).getOrientation();// StaggeredGridLayoutManager 且纵向滚动if (orientation == StaggeredGridLayoutManager.VERTICAL) {childCount = childCount - childCount % spanCount;// 如果是最后一行,则不需要绘制底部if (pos >= childCount)return true;} else// StaggeredGridLayoutManager 且横向滚动{// 如果是最后一行,则不需要绘制底部if ((pos + 1) % spanCount == 0) {return true;}}}return false;}@Overridepublic void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {int spanCount = getSpanCount(parent);int childCount = parent.getAdapter().getItemCount();if (isLastRaw(parent, itemPosition, spanCount, childCount))// 如果是最后一行,则不需要绘制底部{outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);} else if (isLastColum(parent, itemPosition, spanCount, childCount))// 如果是最后一列,则不需要绘制右边{outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());} else {outRect.set(0, 0, mDivider.getIntrinsicWidth(),mDivider.getIntrinsicHeight());}}
}
为了能够看明白,我直接把Activity中的代码都粘过来吧,不过其中还有下边要讲的Item增删动画,也一起贴出来了:
public class RecyclerViewActivity extends BaseActivity implements Toolbar.OnMenuItemClickListener {@Bind(R.id.recyclerview)RecyclerView recyclerview;@Bind(R.id.swipe_recycle_lt)SwipeRefreshLayout swipeRecycleLt;private RecycleBean bean;private List<Integer> contentListT;private String[] titles;private ArrayList<Boolean> contentListCBox;private ReAdapter reAdapter;@Overrideprotected void initView() {//Toobar的使用需要先fvb到Toobar布局,然后在MenuInflate到menu布局,同时给toobar设置点击监听Toolbar toolBar = (Toolbar) findViewById(R.id.toolbar);setSupportActionBar(toolBar);toolBar.setTitle("ToolBarTitle");//设置标题
// toolBar.setNavigationIcon(R.mipmap.ic_launcher);//设置图标
// toolBar.addView(view);toolBar.setOnMenuItemClickListener(this);//设置Menu Item点击}@Overrideprotected void initData() {swipeRecycleLt.setColorSchemeColors(Color.RED, Color.BLUE, Color.GREEN, Color.YELLOW);bean = new RecycleBean();contentListT = new ArrayList<Integer>();contentListCBox = new ArrayList<Boolean>();titles = new String[160];for (int i = 0; i < 111; i++) {contentListT.add(i);titles[i] = "我是title: " + i;contentListCBox.add(false);}bean.contentListCBox = contentListCBox;bean.contentListT = contentListT;bean.titles = titles;LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, OrientationHelper.HORIZONTAL, false);GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 3, OrientationHelper.VERTICAL, false);
// StaggeredGridLayoutManager sglm = new StaggeredGridLayoutManager(this, null, 4, StaggeredGridLayoutManager.GAP_HANDLING_NONE, StaggeredGridLayoutManager.VERTICAL);StaggeredGridLayoutManager sglm = new StaggeredGridLayoutManager(4, OrientationHelper.VERTICAL);
// recyclerview.addItemDecoration( new RecycleLinearItemDecoration(this, OrientationHelper.HORIZONTAL)); recyclerview.addItemDecoration(new DividerGridItemDecoration(this)); //添加分割线recyclerview.setItemAnimator(new DefaultItemAnimator()); //添加默认Item动画reAdapter = new ReAdapter(this, bean);recyclerview.setLayoutManager(sglm);recyclerview.setHasFixedSize(true); //保存item尺寸值,以便不在测量recyclerview.setAdapter(reAdapter);}@Overridepublic int getContentId() {return R.layout.recyclerviewtext;}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);ButterKnife.bind(this);}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {getMenuInflater().inflate(R.menu.main, menu);return super.onCreateOptionsMenu(menu);}// @Override
// public boolean onOptionsItemSelected(MenuItem item) {
// Log.d("onOptionsItemSelected", "run");
// switch (item.getItemId()) {
// case R.id.acitonbar_add_menu:
// reAdapter.addData(1);
// break;
// case R.id.acitonbar_delate_menu:
// reAdapter.removeData(1);
// break;
// }
// return true;
// }@Overridepublic boolean onMenuItemClick(MenuItem item) {Log.d("onMenuItemClick", "run");switch (item.getItemId()) {case R.id.acitonbar_add_menu:reAdapter.addData(1);break;case R.id.acitonbar_delate_menu:reAdapter.removeData(1);break;}return true;}
}
分割线说完了,下面就浅谈一下条目增删动画吧,这里先借助默认的实现,当Item添加和移除的时候,添加动画效果就很简单了:
// 设置item动画
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
- 1
- 2
- 1
- 2
系统为我们提供了一个默认的实现,我们为我们的瀑布流添加以上一行代码,效果为:
这里需要注意,更新数据集不是用adapter.notifyDataSetChanged()
而是 notifyItemInserted(position)
和
notifyItemRemoved(position),
否则没有动画效果。
上述为adapter中添加了两个方法:
public void addData(int position) {mDatas.add(position, "Insert One");notifyItemInserted(position);}public void removeData(int position) {mDatas.remove(position);notifyItemRemoved(position);}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
Activity中点击MenuItem触发:
@Overridepublic boolean onCreateOptionsMenu(Menu menu){getMenuInflater().inflate(R.menu.main, menu);return super.onCreateOptionsMenu(menu);}@Overridepublic boolean onOptionsItemSelected(MenuItem item){switch (item.getItemId()){case R.id.id_action_add:mAdapter.addData(1);break;case R.id.id_action_delete:mAdapter.removeData(1);break;}return true;}
额外部分:因为系统没有提供ClickListener和LongClickListener,不过我们也可以自己去添加,如下:
class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyViewHolder>
{//...public interface OnItemClickLitener{void onItemClick(View view, int position);void onItemLongClick(View view , int position);}private OnItemClickLitener mOnItemClickLitener;public void setOnItemClickLitener(OnItemClickLitener mOnItemClickLitener){this.mOnItemClickLitener = mOnItemClickLitener;}@Overridepublic void onBindViewHolder(final MyViewHolder holder, final int position){holder.tv.setText(mDatas.get(position));// 如果设置了回调,则设置点击事件if (mOnItemClickLitener != null){holder.itemView.setOnClickListener(new OnClickListener(){@Overridepublic void onClick(View v){int pos = holder.getLayoutPosition();mOnItemClickLitener.onItemClick(holder.itemView, pos);}});holder.itemView.setOnLongClickListener(new OnLongClickListener(){@Overridepublic boolean onLongClick(View v){int pos = holder.getLayoutPosition();mOnItemClickLitener.onItemLongClick(holder.itemView, pos);return false;}});}}
}
Activity中去设置监听:
mAdapter.setOnItemClickLitener(new OnItemClickLitener(){@Overridepublic void onItemClick(View view, int position){Toast.makeText(HomeActivity.this, position + " click",Toast.LENGTH_SHORT).show();}@Overridepublic void onItemLongClick(View view, int position){Toast.makeText(HomeActivity.this, position + " long click",Toast.LENGTH_SHORT).show();mAdapter.removeData(position);}});
参考资料
Android 自定义RecyclerView 实现真正的Gallery效果
A First Glance at Android’s RecyclerView
https://github.com/gabrielemariotti/RecyclerViewItemAnimators
RecyclerView 分割线和 Item默认增删动画相关推荐
- recyclerview item动画_RecyclerView 的 Item 酷炫动画,效果加案例讲解!
Caner Gures | 作者依然范特西稀 | 译者技术最TOP(ID:Tech-Android) | 来源 在完成 app 的编码之后,对于一个要求美观的现代 App 来说,仅仅实现功能是不够的, ...
- Android添加item动画,RecyclerView基础篇-Item添加动画
Android_Banner.jpg 简介 本节中我们介绍下给RecyclerView中的Item添加动画. 添加的动画,分为,在打开列表时有Item的展示动画,当滑动的时候没有动画 和打开列表滑动时 ...
- Android开发RecyclerView刷新后item里面控件动画
项目中有个列表的item中有点赞动画,之前我用的列表框架是继承listview的,动画用起来一点问题都没有,现在列表框架是继承用的recyclerView,动画就卡主,百思不得其姐. 解决方法: 1. ...
- Android 活用RecyclerView分割线
1.ItemDecoration简介 Recyclerview是我们日常开发中使用频率比较高的的控件,而其中的ItemDecoration作为布局装饰又能很方便的帮助我们定义分割线,列表排行效果以及设 ...
- RecyclerView分割线的技巧
RecyclerView分割线的技巧 真的很简单,因为方法别人都已经写好了,不多说了还是看源码: package com.xiayiye.yhsh.recyclerviewdemo;import an ...
- RecyclerView之使用ItemTouchHelper实现交互动画
一.简述 RecyclerView默认就有item动画,例如在增加或删除item时,都会有一个条目间位移的动画,但本文要说的不是这个!!!本文的主角是v7包中的ItemTouchHelper,它跟Re ...
- RecyclerView分割线
闲来无事,把自己弄的一个RecyclerView分割线,整理一下,贴上来,当做笔记,方便自己以后查看. 使用方法: 一.添加默认分割线:默认纵向布局.高度为2.灰色, rv.addItemDecora ...
- 高仿支付宝增加减少item功能和动画效果
闲来无事,看了下支付宝App做的应用item效果,很炫,效果如下: 自己实现的效果如下: gif可能有点卡,但是实现了支付宝效果, 说下思路:下面是实例图(凑合看吧,大概意思是) 开始是打算用Scro ...
- 万能RecyclerView分割线扩展
该万能分割线参考自博客:RecyclerView的万能分割线_pengkv的博客-CSDN博客_android recyclerview 分割线 在他的基础上添加了距离左右边距的属性. recycle ...
最新文章
- web前端开发培训有哪些学习阶段
- 字符串-字符串反转(双指针)
- css编写要注意什么 及一些公用的样式和外部引用 转码
- 正则表达式处理的基本步骤
- 大学计算机基础课程报告python-Python程序设计习题解析(大学计算机基础教育规划教材)...
- 【算法】Logistic regression (逻辑回归) 概述
- 嗅觉计算机应用,重磅!美国科技巨头宣布!计算机终于有了“嗅觉”了!
- 电脑系统越来越慢,怎么删除临时文件
- 需求说明 用户登陆功能的实现 c#
- 【汇编语言与计算机系统结构笔记02】整数的计算机表示与运算,C中的无符号字符(unsigned)和带符号字符(signed),补码,一些例题
- 剑指offer面试题21. 调整数组顺序使奇数位于偶数前面(双指针)
- 解决AD不能复制粘贴
- 轨迹规划-二次规划QP
- 扩展YouTube视频频道,提高业务增量
- Thinkpad E430c 16GB内存安装成功
- 圆的内接正n边形的周长
- 图像处理零件尺寸测量matlab,题目基于数字图像处理技术的零件几何尺寸测量.doc...
- CSTC-2017-Web-writeup
- 全免费、保姆级Eclipse32位软件、安装、运行一条龙记录
- 【移动开发】View的scrollTo()和scrollBy()区别
热门文章
- 怎么取消苹果手机自动续费_怎么取消腾讯视频自动续费
- 关于大数据技术原理与应用的学习(5)
- 1900-2100公历年以查表方式取农历二十四节气(VBA)
- 计算机上的符号并不代表分数,你忽视的小小符号,决定着大大的分数!
- 【王喆-推荐系统】线上服务篇-(task1)线上高并发的推荐服务
- 如何比较两幅图的相似度
- [南京大学2022操作系统-蒋炎岩-P1] 笔记 - 操作系统概述
- java将汉字转成拼音首字母大写字母_java 根据汉字生成拼音全拼或拼音首字母的示例...
- Nu-LB-NUC140 LCD uc1601 整理
- 木瓜移动跨境电商SaaS 聚焦核心用户的核心需求场景