很早之前就听说过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初体验相关推荐

  1. [Android Studio] 初体验

    [Android Studio] 初体验 本人刚开始接触移动开发方面的知识,在很多方面都感觉寸步难行,移动开发这门课程应该是在我一年后学校才会开设,而移动开发所用到的java也是在我下个学期才开始正式 ...

  2. 使用Kotlin开发Android应用初体验

    使用Kotlin开发Android应用初体验 昨晚,最近一届的谷歌IO大会正式将Kotlin确定为了官方开发语言,作为一名Android开发鸟,怎么能不及时尝尝鲜呢? Kotlin的简要介绍 在开发之 ...

  3. Android开发初体验

    Android开发初体验 本次开发的应用能提出一道道问题,用户点击TRUE或者FALSE来回答问题,该应用则即时做出反馈. 一·该应用由一个activity和一个布局(layout)组成,我们先创建一 ...

  4. 一加6升级android p,一加6手机升级Android P初体验:系统更智能、操作更流畅!

    原标题:一加6手机升级Android P初体验:系统更智能.操作更流畅! 8月7日谷歌发布正式版Android P后,8月15日一加手机领先业界最先放出了一加6的Android P公测版.当然,这极其 ...

  5. RecyclerView 初体验

    目录: 1.基本使用 2.ItemDecoration Item分割线 3.LayoutManager 4.ItemAnimator Item动画 5.Click and LongClick Item ...

  6. Android Studio 初体验

    Google在I/O2013大会上发布了Android新的开发工具Android Studio,趁周末时间做了一下尝试.有需要的可以 在http://developer.android.com/sdk ...

  7. Android TTS 初体验

    http://bbs.apkok.com/thread-1893-1-1.html 一.基础知识 TextToSpeech 简称TTS,称为语音合成,是Android 从1.6版本开始支持的新功能,能 ...

  8. Android开发初体验之百度地图开发(3)

    在前面文章我分享了如何开始初步的使用百度地图API以及地图搜索功能,这次我将分享如何在百度地图上设置marker,以及info窗口,这次也主要是参考了鸿洋老师的博客,大家想继续学习的话也可以关注一下鸿 ...

  9. GitHub与Android安装初体验(md版本)

    说明: 第一次使用GitHub,安装时难免有些不如人意的地方,写下此文记录心路历程. 首先了解阅读有关的东西,GitHub与Android的作用及用途.配置的环境等,先参考老师发的材料: http:/ ...

最新文章

  1. Istio 网关中的 Gateway 和 VirtualService 配置深度解析
  2. 面试小记---外部脚本必须包含 script 标签吗?
  3. C/C++添加设置任务计划
  4. Python3求解找到小镇的法官问题
  5. 并发编程应用场景_linux网络编程之select函数的并发限制和poll函数应用举例
  6. 在线正则表达式测试,正则替换工具
  7. 设置隔离级别实现并发控制
  8. 固高GTS运动控制卡,C#语言三轴点胶机样本程序源代码
  9. 初次涉足手机广告联盟行业
  10. 关于面向对象(OOB)的一些理解。主要是封装、继承、多态。
  11. vc++之windows api
  12. 不带电脑看-吃货联盟集合
  13. Ubuntu安装ros rotors 以及中间出现的问题的解决办法
  14. Android中Finish和OnBackPressed、OnDestroy的区别
  15. c语言有多难?一个新手刚学c语言的无奈
  16. 10月25日 c语言 打印所有水仙花数
  17. linux工作中软件运行安装常见问题
  18. 关于机械臂仿真软件的简介
  19. 好用app开屏广告组件XHLaunchAd -支持静态/动态图片广告/mp4视频广告
  20. 只会直接插入图片到PPT?图片这样处理,PPT的颜值瞬间提升几倍!

热门文章

  1. Windows系统创建符号链接文件
  2. 参数返回Oracle 常用函数:nvl/nullif/case when/wm_concat/replace
  3. UVa 11044 - Searching for Nessy
  4. Delphi2CS破解 Delphi 转换C#
  5. std::map的使用
  6. 关于进程句柄 窗口句柄的关系
  7. ES6新特性_Promise封装Ajax请求---JavaScript_ECMAScript_ES6-ES11新特性工作笔记026
  8. DataBseDesign工作笔记005---将excel中的表导入到powerdesigner中
  9. Maven异常总结001---Maven project导入到myeclipse时候出现异常:could not get mojo execution paramater value
  10. android学习笔记---49_屏幕适配,根据不同手机屏幕大小适配软件界面