Android RecyclerView初体验
很早之前就听说过RecyclerView这个组件了,但一直很忙没时间学习。趁着周末,就花了一天时间来学习RecyclerView。
准备工作
在Android Studio里新建一个Android项目,添加以下工具:
compile 'com.android.support:support-v4:25.2.0'compile 'com.android.support:appcompat-v7:25.2.0'compile 'com.android.support:recyclerview-v7:25.2.0'compile 'com.github.bumptech.glide:glide:3.7.0'
前两个工具就不说了,基本每个Android项目都会用到,第三个就是使用RecyclerView需要添加的工具了,而最后一个工具是Google推荐使用的一个开源图像加载工具,它使得加载图像变得更为流畅,并且使用起来容易上手。
在drawable目录下放入一些图像,并将这些图像名命名为"image[a-zA-Z0-9]*"的形式(因为在程序里会用反射来查找所有以"image"开头的图像资源)。
在drawable目录下放入"add.png"和"remove.png"等用于顶部工具栏的图像资源。
基本用法
使用RecyclerView,可以依照如下步骤:
1. 在资源文件中定义一个RecyclerView组件;
2. 在Activity中获取定义的RecyclerView组件,变量名就简单叫作view;
3. 为view设置LayoutManager(布局);
4. 为view设置Adapter(适配器);
5. 为view添加ItemDecoration(分隔符,可选);
6. 为view设置ItemAnimator(动画,可选)。
下面在资源文件中定义了一个RecyclerView组件:
1 <android.support.v7.widget.RecyclerView 2 android:layout_width="match_parent" 3 android:layout_height="0dp" 4 android:layout_weight="1" 5 android:id="@+id/vert_view" />
然后就可以在Activity中取得这个RecyclerView组件:
1 RecyclerView view=(RecyclerView) findViewById(R.id.vert_view);
至于其他的细节将在下文介绍。
Adapter
用于RecyclerView的适配器需要继承自RecyclerView.Adapter<T>类,并重写其中的 T onCreateViewHolder(final ViewGroup parent, int viewType) 、 void onBindViewHolder(T holder, final int position) 和 int getItemCount() 方法。下面是一个自定义的Adapter:
1 public class ViewAdapter extends RecyclerView.Adapter<ViewAdapter.ViewHolder> { 2 private Context context; 3 private List<Animal> datas; 4 private int layoutResId; 5 private int imageViewId; 6 private int textViewId; 7 private boolean isCenterCrop; 8 9 public ViewAdapter(Context c, List<Animal> list, int lri, int ivi, int tvi, boolean centerCrop) { 10 context=c; 11 datas=list; 12 layoutResId=lri; 13 imageViewId=ivi; 14 textViewId=tvi; 15 isCenterCrop=centerCrop; 16 } 17 18 @Override 19 public ViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) { 20 View view= LayoutInflater.from(context).inflate(layoutResId, parent, false); 21 return new ViewHolder(view, imageViewId, textViewId); 22 } 23 24 @Override 25 public void onBindViewHolder(ViewHolder holder, final int position) { 26 final Animal animal=datas.get(position); 27 if(isCenterCrop) 28 Glide.with(context).load(animal.getImgResId()).centerCrop().into(holder.imageView); 29 else 30 Glide.with(context).load(animal.getImgResId()).into(holder.imageView); 31 holder.textView.setText(animal.getDescription()); 32 holder.view.setOnClickListener(new View.OnClickListener() { 33 @Override 34 public void onClick(View v) { 35 Intent intent=new Intent(context, DetailActivity.class); 36 intent.putExtra("IMG_RES_ID", animal.getImgResId()); 37 intent.putExtra("DESCRIPTION", animal.getDescription()); 38 context.startActivity(intent); 39 } 40 }); 41 holder.view.setOnLongClickListener(new View.OnLongClickListener() { 42 @Override 43 public boolean onLongClick(View v) { 44 Toast.makeText(context, "第"+position+"个元素", Toast.LENGTH_SHORT).show(); 45 return true; 46 } 47 }); 48 } 49 50 @Override 51 public int getItemCount() { 52 return datas.size(); 53 } 54 55 public void addItem(int position) { 56 datas.add(position, ResourceUtils.getRandomAnimal()); 57 notifyItemInserted(position); 58 } 59 60 public void removeItem(int position) { 61 if(position>=0 && position<datas.size()) { 62 datas.remove(position); 63 notifyItemRemoved(position); 64 } 65 } 66 67 static class ViewHolder extends RecyclerView.ViewHolder { 68 View view; 69 ImageView imageView; 70 TextView textView; 71 72 public ViewHolder(View itemView, int ivi, int tvi) { 73 super(itemView); 74 view=itemView; 75 imageView=(ImageView) itemView.findViewById(ivi); 76 textView=(TextView) itemView.findViewById(tvi); 77 } 78 } 79 }
注意:
- 其中的Animal类封装了一张(动物)图像的资源ID和一段描述(字符串),Animal对象将作为我们的RecyclerView中的元素;
- onCreateViewHolder方法负责创建一个ViewHolder,因此我们需要提供一个元素使用的资源文件ID,我们需要使用到的view(这里包括一个ImageView和一个TextView)都可以通过这个ViewHolder得到;
- onBindViewHolder方法负责对某些view进行设置,这里我们为ImageView设置了图像,为TextView设置了文本,并为它们的父view注册了点击和长按监听器;
- 这里的addItem和removeItem方法是自定义方法,用于向/从RecyclerView中添加/移除一个元素(将在后面介绍),这里可以先忽略;
- 这里的ViewHolder是ViewAdapter.ViewHolder,它继承自RecyclerView.ViewHolder,我们将需要使用到的view交给它管理。
定义好Adapter后,就可以为RecyclerView设置适配器了:
1 List<Animal> datas= ResourceUtils.getAnimals(); 2 adapter=new ViewAdapter(this, datas, R.layout.item_vertical, R.id.vert_image, R.id.vert_text, true); 3 view.setAdapter(adapter);
垂直布局
要想让RecyclerView使用和ListView一样的布局,只需要为RecyclerView设置一个线性布局管理器就行了:
1 view.setLayoutManager(new LinearLayoutManager(this));
水平布局
使用水平布局只需设置LinearLayoutManager的方向即可:
1 LinearLayoutManager layoutManager=new LinearLayoutManager(this); 2 layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); 3 view.setLayoutManager(layoutManager);
网格布局
使用网格布局需要借助GridLayoutManager类:
1 GridLayoutManager manager=new GridLayoutManager(this, 3); 2 // manager.setOrientation(GridLayoutManager.HORIZONTAL); 3 view.setLayoutManager(manager);
创建GridLayoutManager时,第二个参数使用列数/行数。GridLayoutManager默认是上下滑动的,如果想要左右滑动,可以取消第二行的注释。
瀑布流布局
使用瀑布流布局只需使用StaggeredGridLayoutManager即可:
1 view.setLayoutManager(new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL));
StaggeredGridLayoutManager的第一个参数指定列数/行数,第二个参数指定滑动方向,这里设置成上下滑动。如果使用StaggeredGridLayoutManager时所有元素的大小一致,则效果和网格布局一样。
添加分割线
RecyclerView默认是不显示分割线的,这点和ListView不同,但这也为更灵活的定制自己的分割线提供了机会。
要想定制自己的分割线,需要定义类继承自RecyclerView.ItemDecoration,并实现如下方法:
- void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) :此方法负责绘制分割线;
- void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) :此方法和onDraw方法一样,也是绘制分割线,不过绘制的时机是在绘制元素View完成后,通常只需要重写onDraw和此方法中的一个即可;
- void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) :此方法计算分割线的尺寸,以方便计算元素View的偏移。
这里以绘制垂直布局的分割线为例,介绍如何定义自己的分割线,至于其他几种布局的分割线可以参考着来写,文章最后也会提供所有源码:
1 public class VerticalItemDecoration extends RecyclerView.ItemDecoration { 2 private Drawable divider; 3 private static final int[] ATTRS=new int[] { 4 android.R.attr.listDivider, 5 }; 6 7 public VerticalItemDecoration(Context c) { 8 TypedArray array=c.obtainStyledAttributes(ATTRS); 9 divider=array.getDrawable(0); 10 array.recycle(); 11 } 12 13 @Override 14 public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { 15 super.onDraw(c, parent, state); 16 DecorationUtils.onVerticalDraw(c, parent, divider); 17 } 18 19 @Override 20 public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 21 super.getItemOffsets(outRect, view, parent, state); 22 int position=parent.getChildLayoutPosition(view); 23 int childCount=parent.getAdapter().getItemCount(); 24 if((position+1)<childCount) 25 outRect.set(0, 0, 0, divider.getIntrinsicHeight()); 26 } 27 }
1 public class DecorationUtils { 2 public static void onVerticalDraw(Canvas c, RecyclerView parent, Drawable divider) { 3 int left=parent.getPaddingLeft(); 4 int right=parent.getWidth()-parent.getPaddingRight(); 5 6 for(int i=0; i<parent.getChildCount()-1; i++) { 7 View child=parent.getChildAt(i); 8 RecyclerView.LayoutParams params=(RecyclerView.LayoutParams) child.getLayoutParams(); 9 int top=child.getBottom()+params.bottomMargin; 10 int bottom=top+divider.getIntrinsicHeight(); 11 divider.setBounds(left, top, right, bottom); 12 divider.draw(c); 13 } 14 } 15 // ... 16 }
这里的分隔符样式使用了Android提供的“listDivider”,你可以重定义listDivider属性来修改分割线样式:
1 <!-- Base application theme. --> 2 <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> 3 <!-- Customize your theme here. --> 4 <item name="colorPrimary">@color/colorPrimary</item> 5 <item name="colorPrimaryDark">@color/colorPrimaryDark</item> 6 <item name="colorAccent">@color/colorAccent</item> 7 <item name="android:listDivider">@drawable/divider</item> 8 </style>
添加动画
你可能会好奇,RecyclerView为什么需要添加动画,不是就一个滑动吗?难道是添加滑动的动画?
考虑这种情况,当需要向/从RecyclerView中添加/移除元素时,如果一个元素突然插进来或者突然消失,用户会是什么感受(肯定觉得这APP是不是犯二了)。如果在添加/移除元素时,能有一个动画显示元素的出现/消失,那用户体验就更好了。所以,我们这里是为元素添加动画。
RecyclerView提供了一个默认动画,只需要简单的一行代码:
1 view.setItemAnimator(new DefaultItemAnimator());
好了,现在我们已经为元素的出现/消失添加了动画了。那么问题又来了,我们怎么添加/移除元素呢?
还记得我们上面介绍适配器时提供的ViewAdapter吗?当时让你暂时忽略了两个方法——addItem和removeItem,这两个方法就是用于添加/移除元素的。
我们现在来看下细节:
1 public void addItem(int position) { 2 datas.add(position, ResourceUtils.getRandomAnimal()); 3 notifyItemInserted(position); 4 } 5 6 public void removeItem(int position) { 7 if(position>=0 && position<datas.size()) { 8 datas.remove(position); 9 notifyItemRemoved(position); 10 } 11 }
注意到调用了notifyItemInserted和notifyItemRemoved方法,它们就是用于通知RecyclerView元素发生改变的。
现在已经有了添加/移除元素的接口,下面还需要在Activity中添加交互的动作。我们提供了一个Toolbar(用于代替ActionBar的一个组件),并提供了两个item。用户可以点击这两个item来添加/移除元素:
1 @Override 2 protected void onCreate(Bundle savedInstanceState) { 3 // ... 4 Toolbar toolbar=(Toolbar) findViewById(R.id.toolbar); 5 setSupportActionBar(toolbar); 6 // ... 7 } 8 9 @Override 10 public boolean onCreateOptionsMenu(Menu menu) { 11 getMenuInflater().inflate(R.menu.toolbar, menu); 12 return true; 13 } 14 15 @Override 16 public boolean onOptionsItemSelected(MenuItem item) { 17 switch (item.getItemId()) { 18 case R.id.add: 19 adapter.addItem(0); 20 view.smoothScrollToPosition(0); 21 break; 22 case R.id.remove: 23 adapter.removeItem(0); 24 break; 25 default:break; 26 } 27 return true; 28 } 29 }
好了,现在你就可以在添加/移除元素时看到动画效果了。
源代码
整个项目的源代码已经上传到GitHub:
https://github.com/jzyhywxz/RecyclerView
转载于:https://www.cnblogs.com/jzyhywxz/p/6962226.html
Android RecyclerView初体验相关推荐
- [Android Studio] 初体验
[Android Studio] 初体验 本人刚开始接触移动开发方面的知识,在很多方面都感觉寸步难行,移动开发这门课程应该是在我一年后学校才会开设,而移动开发所用到的java也是在我下个学期才开始正式 ...
- 使用Kotlin开发Android应用初体验
使用Kotlin开发Android应用初体验 昨晚,最近一届的谷歌IO大会正式将Kotlin确定为了官方开发语言,作为一名Android开发鸟,怎么能不及时尝尝鲜呢? Kotlin的简要介绍 在开发之 ...
- Android开发初体验
Android开发初体验 本次开发的应用能提出一道道问题,用户点击TRUE或者FALSE来回答问题,该应用则即时做出反馈. 一·该应用由一个activity和一个布局(layout)组成,我们先创建一 ...
- 一加6升级android p,一加6手机升级Android P初体验:系统更智能、操作更流畅!
原标题:一加6手机升级Android P初体验:系统更智能.操作更流畅! 8月7日谷歌发布正式版Android P后,8月15日一加手机领先业界最先放出了一加6的Android P公测版.当然,这极其 ...
- RecyclerView 初体验
目录: 1.基本使用 2.ItemDecoration Item分割线 3.LayoutManager 4.ItemAnimator Item动画 5.Click and LongClick Item ...
- Android Studio 初体验
Google在I/O2013大会上发布了Android新的开发工具Android Studio,趁周末时间做了一下尝试.有需要的可以 在http://developer.android.com/sdk ...
- Android TTS 初体验
http://bbs.apkok.com/thread-1893-1-1.html 一.基础知识 TextToSpeech 简称TTS,称为语音合成,是Android 从1.6版本开始支持的新功能,能 ...
- Android开发初体验之百度地图开发(3)
在前面文章我分享了如何开始初步的使用百度地图API以及地图搜索功能,这次我将分享如何在百度地图上设置marker,以及info窗口,这次也主要是参考了鸿洋老师的博客,大家想继续学习的话也可以关注一下鸿 ...
- GitHub与Android安装初体验(md版本)
说明: 第一次使用GitHub,安装时难免有些不如人意的地方,写下此文记录心路历程. 首先了解阅读有关的东西,GitHub与Android的作用及用途.配置的环境等,先参考老师发的材料: http:/ ...
最新文章
- 九度 1408 寻找表达式 (中缀转后缀)
- C语言与汇编语言相互调用原理以及实例
- 计算机辅助教学导学设计研究,《计算机辅助教学》实验报告
- jedivim加载失败,提示没有支持python3,(markdown也是如此)
- 计组第一章(唐朔飞)——计算机系统概述章节总结
- ASP.NET CORE 之 在IIS上部署MVC项目
- ASP.NET Core2.2 和2.1 版本中对cookie的设置和存储
- Linux系统中安装软件的三种方法(一)
- ubuntu linux编译apt,Ubuntu Linux系统下apt-get命令详解
- 【DVRP】基于matlab蚁群算法求解带距离的VRP问题【含Matlab源码 1040期】
- simulink的pid参数自整定
- Eth Transfer
- 阿里云数据迁移工具解决方案:腾讯云迁移到阿里云
- DNS、HTTP劫持的一些事
- 【综述】方面级情感分析 Aspect-level Sentiment Classification
- ICPC2019徐州 Yuuki and a problem 树套树
- Python | 查看微信撤回的消息(完整代码)
- 机械键盘win键和alt键反了
- 2021年数维杯数学建模分析和思路——B题
- Android 触摸事件机制(三) View中触摸事件详解
热门文章
- APUE 学习笔记(三) 文件和目录
- 小白设计模式:装饰者模式
- windows--reg--向注册表中写入一些数据
- AndroidStudio_安卓原生开发_保存全局数据---Android原生开发工作笔记141
- C#.Net工作笔记003---异步编程async await
- HBuilder工作笔记001---HBuilder打包安卓和ios应用
- 配置jupyter notebook到conda env虚拟环境里可运行
- 神经网络中快速傅立叶变换(FFT)的梯度传递
- 迄今为止我所见过的将BP算法最好的PPT
- C++面试宝典之STL向量容器