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

RecyclerView的优势:

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

用法介绍:

导入RecyclerView的v7库:
RecyclerView是一个android.support.v7库里的控件,因此在使用的时候我们需要在gradle配置文件里加上compile 'com.android.support:recyclerview-v7:22.2.1'来引入google官方的这个库
xml布局中,使用常规的控件引入方式,来引入RecyclerView,如下:

 <android.support.v7.widget.RecyclerView
 android:id="@+id/recyclerview_content"
 style="?recyclerview_style"
 android:scrollbars="vertical"
 android:fadeScrollbars="true"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:layout_marginBottom="-55dp" />

代码中的写法基本和ListView相差无几,但还是要重点说一下:

在实例化RecyclerView之后,我们需要使用setLayoutManager()给它设置布局管理器,其中的实参即就是LayoutManager,这里总共有两种LayoutManager:

1.StaggeredGridLayoutManager,是我们之前提到的流式布局:
它有一个构造方法StaggeredGridLayoutManager(int spanCount, int orientation),第一个是网格的列数,第二个参数是数据呈现的方向
(如果是竖直,那么第一个参数的意义就是列数,反之为行数。而且第二个参数在StaggeredGridLayoutManager里也有同样名称的常量,请同学们自行采纳[这里还是建议大家使用库里自带的常量,因为他们一般都是整型值,这样可以避免各个类里所判别的常量值不一样而导致的其他问题]);
2.GridLayoutManager,网格布局(流式布局应该是它的一个特殊情况):
GridLayoutManager(Context context, int spanCount)或
GridLayoutManager(Context context, int spanCount, int orientation,
 boolean reverseLayout)需要说明的是,最后一个参数表示的是是否逆向布局(意思是将数据反向显示,原先从左向右,从上至下。设为true之后全部逆转)。
小提示:在这两个LayoutManager中,默认的orientation为vertical,reverseLayout为false。对应的参数在GridLayoutManager中都有对应的方法来进行补充设置。而在StaggeredGridLayoutManager中所有的方法都针对reverseLayout做了判断,然而它并没有给出这个参数设定值的api。

线性布局, LinearLayoutManager
LinearLayoutManager(Context context)构建一个默认布局方向为VERTICAL的RecyclerView,这种样式也是ListView默认的。
LinearLayoutManager(Context context, int orientation, boolean reverseLayout)。发现没有,线性布局和网格布局几乎是一样的。只是少了一个spanCount参数。

和ListView一样,为RecyclerView设置Adapter

 class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyViewHolder>
 {

 @Override
 public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
 {
   MyViewHolder holder = new MyViewHolder(LayoutInflater.from(
       HomeActivity.this).inflate(R.layout.item_home, parent,
       false));
   return holder;
 }

 @Override
 public void onBindViewHolder(MyViewHolder holder, int position)
 {
   holder.tv.setText(mDatas.get(position));
 }

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

 class MyViewHolder extends ViewHolder
 {
   TextView tv;

   public MyViewHolder(View view)
   {
     super(view);
     tv = (TextView) view.findViewById(R.id.id_num);
   }
 }
}

如上,即就是Adapter的写法。其中的ViewHolder就是拿来负责View的回收和复用的,这样就不需要我们自己写完ViewHolder之后,还要在getView(int position, View convertView, ViewGroup parent)里一顿判断,一顿绑定,一顿find了。而且这里的ViewHolder成为了RecyclerView中必须继承的一部分,重写完后就需要放入 RecyclerView.Adapter< >这里来对基类的范型初始化。

在这里,Recyclerview已经为你封装好了:

  • getItemCount()就不必多说了,和ListView是一样的
  • getItemViewType(int position)是用来根据position的不同来实现RecyclerView中对不同布局的要求。从这个方法中所返回的值会在onCreateViewHolder中用到。比如头部,尾部,等等的特殊itemView(这里说成ViewHolder比较好)都可以在这里进行判断。
  • onCreateViewHolder(ViewGroup parent, int viewType)是用来配合写好的ViewHolder来返回一个ViewHolder对象。这里也涉及到了条目布局的加载。viewType则表示需要给当前position生成的是哪一种ViewHolder,这个参数也说明了RecyclerView对多类型ItemView的支持。
  • onBindViewHolder(MyViewHolder holder, int position)专门用来绑定ViewHolder里的控件和数据源中position位置的数据。

这里,会有人问,那么item的子控件findViewById 去哪儿了?我们把它交给了ViewHolder的构造方法(其他方法也可以),它的本质是在onCreateViewHolder方法里生成ViewHolder的时候执行的。

提升:
1.代码重构:
在上边看了这么多,有木有觉得,ViewHolder的功能并不是非常明确?它既负责了子控件的查询,又负责了子控件的装载工作。而布局加载和数据绑定却交给了Adapter......
我们来看看掘金的做法:
首先,它把Adapter和ViewHolder的功能以一种较为低耦合的方式进行了职能分离,让ViewHolder里所有的逻辑代码全部都只出现在ViewHolder中。我们现在就对前边提到的代码进行重构:

ViewHodler:

class MyViewHolder extends ViewHolder{
  TextView tv;
  public MyViewHolder(Context context, View view){
     super(view);
     tv = (TextView) view.findViewById(R.id.id_num);

     //当然,我们也可以在这里使用view来对RecyclerView的一个item进行事件监听,也可以使用
     //tv等子控件来实现item的子控件的事件监听。这也是我之所以要传context的原因之一呢~
     ......
  }

  public static MyViewHolder newInstance(Activity context, ViewGroup parent){
      View view = LayoutInflater.from(
       context).inflate(R.layout.item_home, parent,false);
     return new MyViewHolder(context, view);
  }
 }  

 //你没看错,数据绑定也被整合进来了,
 //将adapter里的数据根据position获取到后传进来。当然,也可以根据具体情况来做调整。
  public void onBinViewHolder(String data){
    tv.setText(data);//既然这里也有子控件,那么这里也可以做item子控件的事件监听喽
}

RecyclerView:

class HomeAdapter extends RecyclerView.Adapter<MyViewHolder>{
 @Override
 public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType){//如需要,还要对viewType做判断
   return MyViewHolder.newInstance(this, parent)
 }

 @Override
 public void onBindViewHolder(MyViewHolder holder, int position){
   holder.onBindViewHolder(mDatas.get(position));
 }

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

抽取一个条目点击事件,让它更像ListView:

class HomeAdapter extends RecyclerView.Adapter<MyViewHolder>{
 private OnItemClickListener mOnItemClickListener;

 public void setOnItemClickLitener(OnItemClickLitener mOnItemClickLitener)
 {
   this.mOnItemClickLitener = mOnItemClickLitener;
 } 

 @Override
 public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType){//如需要,还要对viewType做判断
   return MyViewHolder.newInstance(this, parent)
 }

 @Override
 public void onBindViewHolder(MyViewHolder holder, int position){
   holder.onBindViewHolder(mDatas.get(position));

   //如果设置了回调,则设置点击事件
   if (mOnItemClickLitener != null) {
     viewHolder.itemView.setOnClickListener(new OnClickListener() {
       @Override
       public void onClick(View v) {
         mOnItemClickLitener.onItemClick(viewHolder.itemView, i);
       }
     });
   }
 }

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

 public interface OnItemClickLitener {
   void onItemClick(View view, int position);
 }
}

接口调用

 mAdapter.setOnItemClickLitener(new OnItemClickLitener() {
 @Override
 public void onItemClick(View view, int position) {
   ......
 }
});

2.External
上边提到了

控制item的间隔,可以使用addItemDecoration(ItemDecoration decor),不过里边的ItemDecoration是一个抽象类,需要自己去实现...
这个问题,那我们来实际解决一下:

这是羊神实现的一个子类

package com.zhy.sample.demo_recyclerview;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.State;
import android.util.Log;
import android.view.View;

public class DividerItemDecoration extends RecyclerView.ItemDecoration {

  private static 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 DividerItemDecoration(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;
  }

  @Override
  public void onDraw(Canvas c, RecyclerView parent) {
    Log.v("recyclerview - itemdecoration", "onDraw()");

    if (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++) {
      final View child = parent.getChildAt(i);
      android.support.v7.widget.RecyclerView v = new android.support.v7.widget.RecyclerView(parent.getContext());
      final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
          .getLayoutParams();
      final int top = child.getBottom() + params.bottomMargin;
      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++) {
      final View child = parent.getChildAt(i);
      final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
          .getLayoutParams();
      final int left = child.getRight() + params.rightMargin;
      final int right = left + mDivider.getIntrinsicHeight();
      mDivider.setBounds(left, top, right, bottom);
      mDivider.draw(c);
    }
  }

  @Override
  public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
    if (mOrientation == VERTICAL_LIST) {
      outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
    } else {
      outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
    }
  }
}

然后就是为我们的RecyclerView实例添加分割线

addItemDecoration(new DividerItemDecoration(this,
DividerItemDecoration.VERTICAL_LIST));

系统默认的分割线往往达不到我们产品和设计师的要求,怎么办呢?

<item name="android:listDivider">@drawable/your_custom_divider</item>

通过以上xml属性,将系统的listDivider设为自己画出来的分割线,只需要放在你对应activity的主题下即可。

技巧:RecyclerView 滚动条的显示与隐藏

<android.support.v7.widget.RecyclerView
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:scrollbars="vertical"
      >

    </android.support.v7.widget.RecyclerView>

纵向显示:

android:scrollbars="vertical"

横向显示:

android:scrollbars="horizontal"

隐藏:

android:scrollbars="none"

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索android
recyclerview
陈亮指弹吉他进阶讲解、第13讲解应用题进阶、recyclerview、android recyclerview、recyclerview 分割线,以便于您获取更多的相关知识。

时间: 2024-08-19 23:35:11

Android开发中RecyclerView组件使用的一些进阶技讲解_Android的相关文章

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

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

Android开发中编写蓝牙相关功能的核心代码讲解_Android

一. 什么是蓝牙(Bluetooth)?1.1  BuleTooth是目前使用最广泛的无线通信协议 1.2  主要针对短距离设备通讯(10m) 1.3  常用于连接耳机,鼠标和移动通讯设备等.二. 与蓝牙相关的API2.1 BluetoothAdapter: 代表了本地的蓝牙适配器 2.2 BluetoothDevice 代表了一个远程的Bluetooth设备三. 扫描已经配对的蓝牙设备(1)注:必须部署在真实手机上,模拟器无法实现 首先需要在AndroidManifest.xml 声明蓝牙权限

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

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

Android开发中RecyclerView控件使用实例教程

Android RecyclerView 是Android5.0推出来的,导入support-v7包即可使用. 个人体验来说,RecyclerView绝对是一款功能强大的控件. 首先总结下RecyclerView的特点: 1.支持不同方向,不同排版模式,实现多种展现数据的形式,涵盖了ListView,GridView,瀑布流等数据表现的形式 2.内部实现了回收机制,无需我们考虑View的复用情况 3.取消了onItemClick等点击事件,需要自己手动去写 -------------------

Android开发中重要组件activity 生命周期以及启动模式分析

Activity是一个应用程序组件,提供一个屏幕,用户可以用来交互为了完成某项任务. Activity中所有操作都与用户密切相关,是一个负责与用户交互的组件,可以通过setContentView(View)来显示指定控件. 在一个android应用中,一个Activity通常就是一个单独的屏幕,它上面可以显示一些控件也可以监听并处理用户的事件做出响应.Activity之间通过Intent进行通信. Activity生命周期 安卓活动由一个返回栈管理 安卓活动有四个状态 1.运行状态 当一个活动位

Android开发中Activity创建跳转及传值的方法_Android

在Android系统的江湖中有四大组件:活动(Activity), 服务(Service), 广播接收器(Broadcast Reciver)和内容提供者(Content Provider). 今天所介绍的就是Android开发中的四大组件之一:Activity,其他那三大组件以后再进行介绍.说道Android中的Activity,如果你做过iOS开发的话,Activity类似于iOS中的ViewController(视图控制器).在应用中能看到的东西都是放在活动中的.活动是安卓开发比较重要的东

Android开发中Eclipse报错及对应处理方法总结_Android

本文较为详细的总结了Android开发中Eclipse报错及对应处理方法.分享给大家供大家参考,具体如下: 报错1: Conversion to Dalvik format failed with error 1 报错原因:原因是我在android工程中不小心多导入Java的mina,结果一跑程序就报这个错误. 解决方法:将Java中用到的mina包移除,就OK了. 小结:以后遇到这种报错时,记得提醒自己可能是由于自己导错包了. 附: 网上其他处理方法:<丢失Android系统库或者Conver

Android开发中的9个常见错误和解决方法_Android

经过各种各样的整理,以及和热心网友讨论,终于整理出了九种android开发中最常见的问题和解决方案再次跟大家分享下!!有用的话请顶顶帖子,共同进步.好了不多说了,下面是详解! 1. 如果你的项目的R文件不见的话,可以试下改版本号在保存,R文件不见一般都是布局文本出错导致. 2. 布局文件不可以有大写字母 3. 抛出如下错误WARNING: Application does not specify an API level requirement!, 是由于没有指定users sdk的缘故,修改A

Android开发中在TableView上添加悬浮按钮的方法_Android

如果直接在TableVIewController上贴Button的话会导致这个会随之滚动,下面解决在TableView上实现位置固定悬浮按钮的两种方法: 1.在view上贴tableView,然后将悬浮按钮贴在view的最顶层 2.使用window 首先看一下最终的效果,在tableViewController上添加一个悬浮按钮,该按钮不能随着视图的滚动而滚动 首先介绍上面的第一种方法: 1)创建tableview和底部按钮的属性 //屏幕宽 #define kScreenW [UIScreen