Android应用开发中RecyclerView组件使用入门教程

RecyclerView是一种列表容器, 发布很久了, 才想起来写点什么.

RecyclerView相比于ListView, 在回收重用时更具有灵活性, 也就是低耦合, 并且提供了扩展. 加载多个视图时, 应该多用RecyclerView代替ListView.

那么我们来看看这东西应该怎么用? 比如生成一个瀑布流的视图.

首先我们从一个HelloWorld写起, 看看如何构建一个RecyclerView.

1. 依赖库
Gradle配置, 添加Recycler库

compile 'com.android.support:recyclerview-v7:+'

2. 资源文件
资源文件

 <android.support.v7.widget.RecyclerView
  android:id="@+id/test_recycler_view"
  android:layout_width="match_parent"
  android:layout_height="match_parent"/>

3. 代码
LayoutManager: 管理RecyclerView的结构.
Adapter: 处理每个Item的显示.
ItemDecoration: 添加每个Item的装饰.
ItemAnimator: 负责添加\移除\重排序时的动画效果.

LayoutManager\Adapter是必须, ItemDecoration\ItemAnimator是可选.
 /**
  * 初始化RecyclerView
  *
  * @param recyclerView 主控件
  */
 private void initRecyclerView(RecyclerView recyclerView) {
  recyclerView.setHasFixedSize(true); // 设置固定大小
  initRecyclerLayoutManager(recyclerView); // 初始化布局
  initRecyclerAdapter(recyclerView); // 初始化适配器
  initItemDecoration(recyclerView); // 初始化装饰
  initItemAnimator(recyclerView); // 初始化动画效果
 }

4. LayoutManager
管理RecyclerView的布局结构.

 private void initRecyclerLayoutManager(RecyclerView recyclerView) {
  // 错列网格布局
  recyclerView.setLayoutManager(new StaggeredGridLayoutManager(4,
    StaggeredGridLayoutManager.VERTICAL));
 }

提供了多种LayoutManager, 瀑布流使用错列网格布局.
5. Adapter
适配器, 处理RecyclerView的Item事务.

 private void initRecyclerAdapter(RecyclerView recyclerView) {
  mAdapter = new MyAdapter(getData());
  recyclerView.setAdapter(mAdapter);
 }

对于Adapter, 我们需要展开来说, 先看看类.

public class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {

 private List<DataModel> mDataModels;
 private List<Integer> mHeights;

 MyAdapter(List<DataModel> dataModels) {
  if (dataModels == null) {
   throw new IllegalArgumentException("DataModel must not be null");
  }
  mDataModels = dataModels;
  mHeights = new ArrayList<>();
 }

 @Override
 public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  View itemView = LayoutInflater.from(parent.getContext())
    .inflate(R.layout.item_recycler_view, parent, false);
  return new MyViewHolder(itemView);
 }

 @Override
 public void onBindViewHolder(MyViewHolder holder, int position) {
  DataModel dataModel = mDataModels.get(position);

  // 随机高度, 模拟瀑布效果.
  if (mHeights.size() <= position) {
   mHeights.add((int) (100 + Math.random() * 300));
  }

  ViewGroup.LayoutParams lp = holder.getTvLabel().getLayoutParams();
  lp.height = mHeights.get(position);

  holder.getTvLabel().setLayoutParams(lp);

  holder.getTvLabel().setText(dataModel.getLabel());
  holder.getTvDateTime().setText(new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH)
    .format(dataModel.getDateTime()));
 }

 @Override
 public int getItemCount() {
  return mDataModels.size();
 }

 public void addData(int position) {
  DataModel model = new DataModel();
  model.setDateTime(getBeforeDay(new Date(), position));
  model.setLabel("No. " + (int) (new Random().nextDouble() * 20.0f));

  mDataModels.add(position, model);
  notifyItemInserted(position);
 }

 public void removeData(int position) {
  mDataModels.remove(position);
  notifyItemRemoved(position);
 }

 /**
  * 获取日期的前一天
  *
  * @param date 日期
  * @param i 偏离
  * @return 新的日期
  */
 private static Date getBeforeDay(Date date, int i) {
  Calendar calendar = Calendar.getInstance();
  calendar.setTime(date);
  calendar.add(Calendar.DAY_OF_YEAR, i * (-1));
  return calendar.getTime();
 }
}

(1)onCreateViewHolder创建ViewHolder.

(2)onBindViewHolder绑定每一项数据.

(3)getItemCount返回列表长度.

(4)RecyclerView强制使用ViewHolder.

public class MyViewHolder extends RecyclerView.ViewHolder {

 private TextView mTvLabel; // 标签
 private TextView mTvDateTime; // 日期

 public MyViewHolder(View itemView) {
  super(itemView);
  mTvLabel = (TextView) itemView.findViewById(R.id.item_text);
  mTvDateTime = (TextView) itemView.findViewById(R.id.item_date);
 }

 public TextView getTvLabel() {
  return mTvLabel;
 }

 public TextView getTvDateTime() {
  return mTvDateTime;
 }

}

在onCreateViewHolder方法, 创建类; 在onBindViewHolder方法, 绑定数据.
DataModel

public class DataModel {

 private String mLabel;
 private Date mDateTime;

 public String getLabel() {
  return mLabel;
 }

 public void setLabel(String label) {
  mLabel = label;
 }

 public Date getDateTime() {
  return mDateTime;
 }

 public void setDateTime(Date dateTime) {
  mDateTime = dateTime;
 }
}

6. ItemDecoration
项的装饰, 比如ListView中的分割线, 在本例中, 左右两条粉线.

 private void initItemDecoration(RecyclerView recyclerView) {
  recyclerView.addItemDecoration(new MyItemDecoration(this));
 }

ItemDecoration, 注意parent和child的使用方式.

public class MyItemDecoration extends RecyclerView.ItemDecoration {

 private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
 private Drawable mDivider;

 public MyItemDecoration(Context context) {
  final TypedArray array = context.obtainStyledAttributes(ATTRS);
  mDivider = array.getDrawable(0);
  array.recycle();
 }

 @Override
 public void onDraw(Canvas c, RecyclerView parent, State state) {
  drawHorizontal(c, parent);
  drawVertical(c, parent);
 }

 // 水平线
 public void drawHorizontal(Canvas c, RecyclerView parent) {

  final int childCount = parent.getChildCount();

  // 在每一个子控件的底部画线
  for (int i = 0; i < childCount; i++) {
   final View child = parent.getChildAt(i);

   final int left = child.getLeft() + child.getPaddingLeft();
   final int right = child.getWidth() + child.getLeft() - child.getPaddingRight();
   final int top = child.getBottom() - mDivider.getIntrinsicHeight() - child.getPaddingBottom();
   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);
   int right = child.getRight() - child.getPaddingRight();
   int left = right - mDivider.getIntrinsicWidth();
   final int top = child.getTop() + child.getPaddingTop();
   final int bottom = child.getTop() + child.getHeight() - child.getPaddingBottom();

   mDivider.setBounds(left, top, right, bottom);
   mDivider.draw(c);
  }
 }

 // Item之间的留白
 @Override
 public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
  outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight());
 }
}

本例重写了listDivider

 <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
  ...
  <item name="android:listDivider">@drawable/divider_bg</item>
 </style>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
  android:shape="rectangle">
 <solid android:color="#ff00ff"/>
 <size android:height="4dp"/>
 <size android:width="4dp"/>
</shape>

7. ItemAnimator
动画效果比较复杂, 使用默认动画. 如要定制的话, 继承DefaultItemAnimator; 如设置null, 则不显示任何动画.

 private void initItemAnimator(RecyclerView recyclerView) {
  recyclerView.setItemAnimator(new DefaultItemAnimator()); // 默认动画
 }

8. 最终Activity

public class MainActivity extends AppCompatActivity {

 private MyAdapter mAdapter;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
  setSupportActionBar(toolbar);

  FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
  fab.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View view) {
    Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
      .setAction("Action", null).show();
   }
  });

  // 初始化RecyclerView
  initRecyclerView((RecyclerView) findViewById(R.id.test_recycler_view));
 }

 /**
  * 初始化RecyclerView
  *
  * @param recyclerView 主控件
  */
 private void initRecyclerView(RecyclerView recyclerView) {
  recyclerView.setHasFixedSize(true); // 设置固定大小
  initRecyclerLayoutManager(recyclerView); // 初始化LayoutManager
  initRecyclerAdapter(recyclerView); // 初始化Adapter
  initItemDecoration(recyclerView); // 初始化边界装饰
  initItemAnimator(recyclerView); // 初始化动画效果
 }

 /**
  * 初始化RecyclerView的LayoutManager
  *
  * @param recyclerView 主控件
  */
 private void initRecyclerLayoutManager(RecyclerView recyclerView) {
  // 错列网格布局
  recyclerView.setLayoutManager(new StaggeredGridLayoutManager(4,
    StaggeredGridLayoutManager.VERTICAL));
 }

 /**
  * 初始化RecyclerView的Adapter
  *
  * @param recyclerView 主控件
  */
 private void initRecyclerAdapter(RecyclerView recyclerView) {
  mAdapter = new MyAdapter(getData());
  recyclerView.setAdapter(mAdapter);
 }

 /**
  * 初始化RecyclerView的(ItemDecoration)项目装饰
  *
  * @param recyclerView 主控件
  */
 private void initItemDecoration(RecyclerView recyclerView) {
  recyclerView.addItemDecoration(new MyItemDecoration(this));
 }

 /**
  * 初始化RecyclerView的(ItemAnimator)项目动画
  *
  * @param recyclerView 主控件
  */
 private void initItemAnimator(RecyclerView recyclerView) {
  recyclerView.setItemAnimator(new DefaultItemAnimator()); // 默认动画
 }

 /**
  * 模拟的数据
  *
  * @return 数据
  */
 private ArrayList<DataModel> getData() {
  int count = 57;
  ArrayList<DataModel> data = new ArrayList<>();
  for (int i = 0; i < count; i++) {
   DataModel model = new DataModel();

   model.setDateTime(getBeforeDay(new Date(), i));
   model.setLabel("No. " + i);

   data.add(model);
  }

  return data;
 }

 /**
  * 获取日期的前一天
  *
  * @param date 日期
  * @param i 偏离
  * @return 新的日期
  */
 private static Date getBeforeDay(Date date, int i) {
  Calendar calendar = Calendar.getInstance();
  calendar.setTime(date);
  calendar.add(Calendar.DAY_OF_YEAR, i * (-1));
  return calendar.getTime();
 }

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  // Inflate the menu; this adds items to the action bar if it is present.
  getMenuInflater().inflate(R.menu.menu_main, menu);
  return true;
 }

 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
  // Handle action bar item clicks here. The action bar will
  // automatically handle clicks on the Home/Up button, so long
  // as you specify a parent activity in AndroidManifest.xml.
  int id = item.getItemId();

  switch (item.getItemId()) {
   case R.id.id_action_add:
    mAdapter.addData(1);
    break;
   case R.id.id_action_delete:
    mAdapter.removeData(1);
    break;
  }

  //noinspection SimplifiableIfStatement
  if (id == R.id.action_settings) {
   return true;

  }

  return super.onOptionsItemSelected(item);
 }
}

为了测试动画, Menu额外添加两个按钮。

9.运行ReactNative示例
既然感觉ReactNative开发靠谱, 那么我们就来看看ReactNative都能做哪些好玩的东西, 和原生的有哪些区别?
示例图

按照文档安装一些命令行工具, 再下载Git代码.
Github: https://github.com/facebook/react-native

内容很多, 包含一些依赖库和示例(Example), 下载的有点慢, 耐心等待.

下载完成后, 在react-native内, 执行npm install.

Android项目执行, 参考ReactAndroid的README.md.

在react-native目录, 新建local.properties

sdk.dir=/Users/wangchenlong/Installations/android-sdk
ndk.dir=/Users/wangchenlong/Installations/android-ndk-r10e

执行

cd react-native
./gradlew :ReactAndroid:assembleDebug

再执行

./gradlew :ReactAndroid:installArchives

启动服务

./packager/packager.sh

安装项目

cd react-native
./gradlew :Examples:UIExplorer:android:app:installDebug

一定要先启动服务, 再安装项目.
出现transforming 100%, 即导入成功.

在最新版本中, 我的红米note4无法运行项目.
报错: Upload package to device fails.
原因是编译的gradle版本太高, 默认1.5.0, 实际1.2.0~1.3.0都可以运行.
我的是1.2.3.
真机调试, 本人红米note(Android 4.2)
摇动手机, 选择Dev Settings->Debug sever host & port for device. 设置IP地址, 观察本机的IP, 填入即可. 我当前的是

192.168.2.202:8081
注意设置端口8081, 否则无法加载. 有些情况可以直接输入IP即可.
Android5.0以上, 直接设置端口即可.

adb reverse tcp:8081 tcp:8081

参考Android的真机调试文档.

IOS模拟器, 太穷没有iPhone. 直接打开open UIExplorer.xcodeproj项目, 执行就可以显示.

开发有两种选择, 一种是直接基于ReactNative开发, 一种是把ReactNative集成到现有的App中, 对于第二种, 我们就需要关注, ReactNative会增大多少代码呢?

使用最基本的HelloWorld做测试, ReactNative也是生成一个简单HelloWorld的JS. 最新生成的HelloWorld的大小是1.4M, 加上ReactNative的是7.6M, 框架大约6.2M左右, 各位可以权衡一下使用.

ReactNative的UIExplorer已经包含了大量示例, 很接近原生, 非常绚丽, 速度也很快. 如Android的ViewPager

OK, 好的开始是成功的一半, 继续探索吧! Enjoy it!

(0)

相关推荐

  • Android开发之RecyclerView控件

    现阶段,我们创建了最简单的Android项目,现在在此公布github链接https://github.com/neuyu/android-best-practices,希望大家多多支持. 因为之前谈到过包结构的定义,我选择第一种方式,按照Android特性定义,所以你的包结构应该是这样的: RecyclerView 在MainActivity中,我们需要用到RecyclerView这一新控件,那么如何引用,如何使用它呢?在gradle文件中添加库依赖: compile 'com.android

  • Android RecyclerView基本使用详解

    什么是RecyclerView 关于RecyclerView,是一个主要用于展示和回收View的有一个控件,在官用了一句话来概括 RecyclerView 是一种通过提供有限的数据项窗口有效显示大数据集的视图. 基提供几个基本的功能, 控制其显示的方式 --->LayoutManager-(必需) 控制Item间的间隔 --->ItemDecoration--(非必需) 控制Item增删的动画--->ItemAnimator--(非必需) 以上就可以完成ListView大部的功能了,但是

  • Android中RecyclerView实现多级折叠列表效果(二)

    前言 在本文开始之前请大家先看一下这篇文章:http://www.jb51.net/article/113510.htm 上面的这篇文章是之前写的,里面发现有很多不好用地方,也学到些新姿势,改动了许多地方.下面来看看详细的介绍: 要点: 1.可以通过后台控制Item的展示. 2.TreeRecyclerAdapter,可以展开,折叠.多级展示 3.adapter可以使用装饰者模式进行扩展.支持EmptyAdapter.可以添加headview和footview 4.item的样式可以编写文档,t

  • Android中RecyclerView实现多级折叠列表效果(TreeRecyclerView)

    前言 首先不得不吐槽一下产品,尼玛为啥要搞这样的功能....搞个两级的不就好了嘛...自带控件,多好.三级,四级,听说还有六级的....这样丧心病狂的设计,后台也不好给数据吧. 先看看效果: 两级的效果: 三级的效果: 全部展开的效果(我只写了五级) 说说为什么写这货吧: 公司产品提出三级这个需求后,我就在网上找啊找. 找的第一个,发现实现其实是ExpandListview嵌套. 找的第二个,ExpandRecyclview,然后就用呗,发现三级展开很卡,看源码, 发现是RecyclerView

  • Android App开发中RecyclerView控件的基本使用教程

    概述 RecyclerView出现已经有一段时间了,相信大家肯定不陌生了,大家可以通过导入support-v7对其进行使用. 据官方的介绍,该控件用于在有限的窗口中展示大量数据集,其实这样功能的控件我们并不陌生,例如:ListView.GridView. 那么有了ListView.GridView为什么还需要RecyclerView这样的控件呢?整体上看RecyclerView架构,提供了一种插拔式的体验,高度的解耦,异常的灵活,通过设置它提供的不同LayoutManager,ItemDecor

  • 深入解析Android中的RecyclerView组件

    前些日子,组里为了在目前的Android程序里实现基于ListView子项的动画效果,希望将最新的RecyclerView引入到程序中,于是我便花了一些时间研究了一下RecyclerView的基本情况.本文算是对这些日子里了解的内容做一些汇总. 在网上关于RecyclerView的基本使用方式已经有了比较详细介绍,而且其设计结构也类似于ListView,所以本文将不重点介绍如何使用,在文末的引用中都可以相关内容.这里主要是介绍RecyclerView的基本功能.设计理念,以及系统提供API的情况

  • Android应用开发中RecyclerView组件使用入门教程

    RecyclerView是一种列表容器, 发布很久了, 才想起来写点什么. RecyclerView相比于ListView, 在回收重用时更具有灵活性, 也就是低耦合, 并且提供了扩展. 加载多个视图时, 应该多用RecyclerView代替ListView. 那么我们来看看这东西应该怎么用? 比如生成一个瀑布流的视图. 首先我们从一个HelloWorld写起, 看看如何构建一个RecyclerView. 1. 依赖库 Gradle配置, 添加Recycler库 compile 'com.and

  • Android App开发中ViewPager组件的入门使用教程

    首先让大家有个全局的认识,直接上个项目,看看仅仅通过这几行代码,竟然就能完成如此强悍的功能.下篇再仔细讲讲为什么要这么写. 效果图: 实现了三个view间的相互滑动 第一个VIEW向第二个VIEW滑动: 第二个VIEW向第三个VIEW滑动: 一.新建项目,引入ViewPager控件 ViewPager.它是google SDk中自带的一个附加包的一个类,可以用来实现屏幕间的切换. 1.在主布局文件里加入 <RelativeLayout xmlns:android="http://schem

  • Android开发中RecyclerView组件使用的一些进阶技讲解

    RecyclerView的优势: 它自带ViewHolder来实现View的复用机制,再也不用ListView那样在getView()里自己写了 使用LayoutManager可以实现ListView,GridView以及流式布局的列表效果 通过setItemAnimator(ItemAnimator animator)可以实现增删动画(懒的话,可以使用默认的ItemAnimator对象,效果也不错) 控制item的间隔,可以使用addItemDecoration(ItemDecoration

  • Android应用开发中使用Fragment的入门学习教程

    Fragment是Android honeycomb 3.0开始新增的概念,Fragment名为碎片不过却和Activity十分相似,下面介绍下Android Fragment的作用和用法.Fragment用来描述一些行为或一部分用户界面在一个Activity中,你可以合并多个fragment在一个单独的activity中建立多个UI面板,同时重用fragment在多个activity中.你可以认为fragment作为一个activity中的一节模块 ,fragment有自己的生命周期,接收自己

  • Android开发中RecyclerView模仿探探左右滑动布局功能

    我在此基础上优化了部分代码, 添加了滑动回调, 可自定义性更强. 并且添加了点击按钮左右滑动的功能. 据说无图都不敢发文章了. 看图: 1:这种功能, 首先需要自己管理布局 继承 RecyclerView.LayoutManager , 显示自己管理布局, 比如最多显示4个view, 并且都是居中显示. 底部的View还需要进行缩放,平移操作. public class OverLayCardLayoutManager extends RecyclerView.LayoutManager { p

  • Android开发中Button组件的使用

    前言 安卓系统中,Button是程序和用户进行交互的一个重要控件,今天我们就来简单的对Button进行学习,其中Button组件是文本按钮(继承自TextView),而ImageButton是图像按钮(继承自ImageView).两者之间的区别在于: 1.Button即可显示文本也可显示图形(通过设置背景图),而ImageButton只能显示图形不能显示文本: 2.Button可在文本周围区域显示小图,而ImageButton无法在某个区域显示小图: 3.ImageButton上的图像可按比例进

  • Android开发中关于组件导出的风险及防范

    前言 近年来,移动APP存在一个非常的重要的问题就是安全问题,造成的后果有可能是用户的隐私泄露和财产损失等,对于一款成熟的APP或者是金融银行类APP,这无疑是最致命的,所以对APP进行有效的防范也是很有必要. 近段时间,公司安排了某安全公司对我们的APP进行了全方面的安全测试,根据文档检测结果看,整体上看还是很安全的,其中有一项就是组件导出风险,接下来我们说说四大组件.组件导出必要性.风险以及如何防范. 一.四大组件 从事Android开发,我们都知道Android有四大组件, 分别是: 活动

  • Android开发中Flutter组件实用技巧

    目录 正文 简化 Assert 管理 更容易 imports 从按钮上移除飞溅效果 更简单的平台小工具 可见性小工具 正文 今天我将向您展示 4 个非常有用的 Flutter 技巧,您可以立即应用到您的项目.我不会向您展示任何包或扩展,就像我通常做的那样,但是非常简单,但是非常有用的提示! 简化 Assert 管理 管理 Assert 可能非常困难.如果你想在你的应用程序中多次使用一个图像,你必须一次又一次地指定路径.但是有一个简单得多的解决方案.创建一个 App Assets 类,用于存储所有

  • Android编程开发中的正则匹配操作示例

    本文实例讲述了Android编程开发中的正则匹配操作.分享给大家供大家参考,具体如下: 在Android开发中,可能也会遇到一下输入框的合法性验证,这时候最常用的就应该是正则表达式去做一些匹配了,下面就常用的正则匹配做一下介绍 1. 手机号码的验证 根据实际开发于2009年9月7日最新统计: 中国电信发布中国3G号码段:中国联通185,186;中国移动188,187;中国电信189,180共6个号段. 移动:134.135.136.137.138.139.150.151.157(TD).158.

随机推荐