RecyclerView 使用指南
最近看了很多 RecyclerView 的使用文章,一直晕乎乎的,完全不知道套路是啥。很多人都是直接上代码,但是却没有详细说明代码的使用,于是打算自己写写,理理思路。顺便帮助那些正在学习 Android 的新人。
本文源码参见 https://github.com/huanshen/Learn-Android/tree/master/recycleTest
1、单个 item 的 recyclerView
首先 recycleView 需要我们引入,所以在 build.gradle ( model ) 中引入:
compile 'com.android.support:recyclerview-v7:26.0.+'
下面我们开始写布局文件:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"/>
</RelativeLayout>
这相当于在屏幕上占了个位置给 recyclerView 用,但是我们还得为其添加 item 项,item 也得有自己的布局呢,于是我们继续编写下面这个布局。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="wenzi "
android:id="@+id/text"/>
</LinearLayout>
布局写好了,接下去就是怎么把数据在 view 上进行显示。这个当然得用到 适配器啦, recycleView 有自己的适配器,我们只需要继承就好,然后编写具体的处理代码,具体如下,后面有详细的分析的。
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
public String[] datas = null;
public MyAdapter(String[] data) {
datas = data;
}
//创建新View,被LayoutManager所调用
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item,viewGroup,false);
ViewHolder vh = new ViewHolder(view);
return vh;
}
//将数据与界面进行绑定的操作
@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
viewHolder.mTextView.setText(datas[position]);
}
//获取数据的数量
@Override
public int getItemCount() {
return datas.length;
}
//自定义的ViewHolder,持有每个Item的的所有界面元素
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView mTextView;
public ViewHolder(View view){
super(view);
mTextView = (TextView) view.findViewById(R.id.text);
}
}
}
这里我们再来细细分析下继承之后要写的东西,首先是构造函数 MyAdapter,用来传入所需要的数据。一般都是数组或者链表之类的,这样才能形成列表啊。
其次是 onCreateViewHolder, 我把它翻译成 “创建视图容器”,就是用来装 item 视图的。先获取 item 的视图,然后再把它放进容器即可。
接下去就是 onBindViewHolder,就是对容器里的 item 的每一个项进行绑定,这样我们才能将数据映射到 view 上进行显示啊。
然后就是 getItemCount 了,它其实就是返回一个数量,就是最后到底创建了几个。
最后呢,我们自定义了一个 ViewHolder ,继承于RecyclerView.ViewHolder。 这个就更好理解啦,就是我们要把 item 中的每一项都先装进ViewHolder 这个大容器里面,这样我们才能进行前面 绑定啊。这里定义了一个 mTextView, 注意它也出现在了 onBindViewHolder 中噢。
把代码好好看一遍,然后再阅读一遍上面的分析,你应该就知道怎么用了呢。
下一步,我们就要开始用它们啦,具体见代码如下:
private RecyclerView mRecyclerView;
private RecyclerView.LayoutManager mLayoutManager;
private RecyclerView.Adapter mAdapter; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); mRecyclerView = (RecyclerView)findViewById(R.id.my_recycler_view);
//创建默认的线性LayoutManager
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
//如果可以确定每个item的高度是固定的,设置这个选项可以提高性能
mRecyclerView.setHasFixedSize(true);
//创建并设置Adapter
mAdapter = new MyAdapter(new String[]{"1231","43252345","2342342"});
mRecyclerView.setAdapter(mAdapter);
}
首先我们得找到原来的 RecyclerView 这个大容器,然后我们创建一个默认的线性 Layoutmanger,并设置 RecyclerView 为线性得得。 然后我们在将数据传入到 MyAdapter 中,并创建一个它的实例,最后调用 setAdapter 即可。
2、多个 item 的 recycleView 实现
我们前面展现的是 只有一个 item,并且样式都是一样的,那我们能不能有多种不同的样式呢?答案是可以。
如上图所示,我们让它奇偶采用不同的样式。当然,我们完全还可以自由发挥实现有图和无图的。
那这个具体怎么实现的呢,还是在上一个代码基础上进行修改。
首先,我们添加一个新的样式,叫 item1.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:id="@+id/root"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="56dp"
android:text="wenzi "
android:background="@color/colorAccent"
android:layout_margin="10dp"
android:id="@+id/text"/>
</LinearLayout>
然后我们开始添加实现脸多个item 的代码,主要添加在 我们自己写的适配器里面,activity 的代码不用动。
private final int ITEM = 1;
private final int ITEM1 = 2;
首先我们定义了两个常量来表示 两种不同的类型。因为有两种类型,自然要定义两种不同的 viewhold 了。具体代码如下:
//自定义的ViewHolder,持有每个Item的的所有界面元素
public class ImageHolder extends RecyclerView.ViewHolder {
public TextView mTextView;
public ImageHolder(View view){
super(view);
mTextView = (TextView) view.findViewById(R.id.text);
}
} public class ColorHolder extends RecyclerView.ViewHolder{
public TextView mTextView;
public ColorHolder(View view){
super(view);
mTextView = (TextView) view.findViewById(R.id.text);
}
}
同样的我们也要创建两种不同的 viewHold 容器,那我们怎样才能知道我们需要创建哪一种呢?
这时候,getItemViewType 就可以派上用场了,这个函数就是根据不同的位置,为我们确定不同的类型的。
由于我们的数据比较简单,我是用奇偶来划分的。那如果真的操作的时候,我们必须在提供数据的同时,还要提供数据的类别,这样我们才知道那种数据采用哪种 view 进行展示。
public int getItemViewType(int position) {
if (position % 2 == 0){
return ITEM;
}
return ITEM1;
}
当我们把数据类型划定好了,就可以来创建 viewHold 了。
//创建新View,被LayoutManager所调用
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
if (viewType == ITEM) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item, viewGroup, false);
ImageHolder vh = new ImageHolder (view);
return vh;
}else{
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item1, viewGroup, false);
ColorHolder vh = new ColorHolder(view);
return vh;
}
}
上面的代码,我们根据不同的类型,来引用不同的布局。
创建好之后,就是对数据进行绑定啦。具体代码如下:
//将数据与界面进行绑定的操作
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
if (viewHolder instanceof ImageHolder) {
//Toast.makeText(MainActivity.this, datas[position], Toast.LENGTH_SHORT).show();
((ImageHolder)viewHolder).mTextView.setText(datas[position]);
viewHolder.itemView.setTag(position);
}else {
((ColorHolder)viewHolder).mTextView.setText(datas[position]);
viewHolder.itemView.setTag(position);
}
}
绑定的时候,我们要对 viewHold 的类型进行判定,只有这样我们才能正确的将数据绑定到 view 上。
好了,到这里我们就完成了呢,相信你应该能够掌握 recycleView 了。
3、点击事件的产生
我们让每个 item 点击的时候,弹出相对应的 position 。
首先,我们要实现 View.OnClickListener 接口,让 item 可以点击。
public class ImageHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public TextView mTextView;
public ImageHolder(View view){
super(view);
mTextView = (TextView) view.findViewById(R.id.text);
view.setOnClickListener(this);
} @Override
public void onClick(View view) {
if (mOnRvItemClick != null)
mOnRvItemClick.onItemClick(view, getAdapterPosition());
}
}
这里,我们在 ImageHolder 实现该接口,重写了 onClick 方法。方法里面的内容后面再说。该方法的参数并没有 position ,因此需要我们在做一些处理。position 位置的获取主要是通过 getAdapterPosition 来实现的。
我们定义了一个 item 的点击接口
public interface onRecyclerViewItemClick {
void onItemClick(View v, int position);
}
这里我们定义了一个 onRecyclerViewItemClick 接口,其内部方法则是 onItemClick,可以看到我们有 position 参数了。
public MyAdapter(String[] data, onRecyclerViewItemClick onRvItemClick) {
datas = data;
mOnRvItemClick = onRvItemClick;
}
然后我们在初始化的时候,将 onRecyclerViewItemClick 的实例 onRvItemClick 传给私有参数 mOnRvItemClick;然后再 onClick 中进行调用,具体见前面的所说的。
最后,我们在调用即可。
mAdapter = new MyAdapter(data, new MyAdapter.onRecyclerViewItemClick() {
@Override
public void onItemClick(View v, int position) {
Toast.makeText(MainActivity.this, "第" + position + "行", Toast.LENGTH_SHORT).show();
}
});
mRecyclerView.setAdapter(mAdapter);
4、添加分割线
recyclerView 有一个默认的分割线方法:DividerItemDecoration。
只需这样调用就可以:
mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
分割线也是分水平还是竖直的。如果我们需要自定义自己的分割线也是可以的。我们只需要 extends RecyclerView.ItemDecoration 重写其中的一些方法就可以。
package com.example.shenjiaqi.httpshiyong; import android.annotation.SuppressLint;
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.annotation.NonNull;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.LinearLayout; /**
* Created by shenjiaqi on 2017/10/22.
*/ public class MyDecoration extends RecyclerView.ItemDecoration {
/**
*
* @param outRect 边界
* @param view recyclerView ItemView
* @param parent recyclerView
* @param state recycler 内部数据管理
*//*
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
//设定底部边距为1px
*//*outRect.set(0, 0, 0, 30);*//*
}*/ public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
public static final int VERTICAL = LinearLayout.VERTICAL; private static final int[] ATTRS = new int[]{ android.R.attr.listDivider }; private Drawable mDivider; /**
* Current orientation. Either {@link #HORIZONTAL} or {@link #VERTICAL}.
*/
private int mOrientation; private final Rect mBounds = new Rect(); /**
* Creates a divider {@link RecyclerView.ItemDecoration} that can be used with a
* {@link LinearLayoutManager}.
*
* @param context Current context, it will be used to access resources.
* @param orientation Divider orientation. Should be {@link #HORIZONTAL} or {@link #VERTICAL}.
*/
public MyDecoration(Context context, int orientation) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
setOrientation(orientation);
} /**
* Sets the orientation for this divider. This should be called if
* {@link RecyclerView.LayoutManager} changes orientation.
*
* @param orientation {@link #HORIZONTAL} or {@link #VERTICAL}
*/
public void setOrientation(int orientation) {
if (orientation != HORIZONTAL && orientation != VERTICAL) {
throw new IllegalArgumentException(
"Invalid orientation. It should be either HORIZONTAL or VERTICAL");
}
mOrientation = orientation;
} /**
* Sets the {@link Drawable} for this divider.
*
* @param drawable Drawable that should be used as a divider.
*/
public void setDrawable(@NonNull Drawable drawable) {
if (drawable == null) {
throw new IllegalArgumentException("Drawable cannot be null.");
}
mDivider = drawable;
} @Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (parent.getLayoutManager() == null) {
return;
}
if (mOrientation == VERTICAL) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
} @SuppressLint("NewApi")
private void drawVertical(Canvas canvas, RecyclerView parent) {
canvas.save();
final int left;
final int right;
if (parent.getClipToPadding()) {
left = parent.getPaddingLeft();
right = parent.getWidth() - parent.getPaddingRight();
canvas.clipRect(left, parent.getPaddingTop(), right,
parent.getHeight() - parent.getPaddingBottom());
} else {
left = 0;
right = parent.getWidth();
} final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
parent.getDecoratedBoundsWithMargins(child, mBounds);
final int bottom = mBounds.bottom + Math.round(child.getTranslationY());
final int top = bottom - mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(canvas);
}
canvas.restore();
} @SuppressLint("NewApi")
private void drawHorizontal(Canvas canvas, RecyclerView parent) {
canvas.save();
final int top;
final int bottom;
if (parent.getClipToPadding()) {
top = parent.getPaddingTop();
bottom = parent.getHeight() - parent.getPaddingBottom();
canvas.clipRect(parent.getPaddingLeft(), top,
parent.getWidth() - parent.getPaddingRight(), bottom);
} else {
top = 0;
bottom = parent.getHeight();
} final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
parent.getLayoutManager().getDecoratedBoundsWithMargins(child, mBounds);
final int right = mBounds.right + Math.round(child.getTranslationX());
final int left = right - mDivider.getIntrinsicWidth();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(canvas);
}
canvas.restore();
} @Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
RecyclerView.State state) {
int childAdapterPosition = parent.getChildAdapterPosition(view); int lastCount = parent.getAdapter().getItemCount() - 1;
//如果当前条目与是最后一个条目,就不设置divider padding
if (childAdapterPosition == lastCount) {
outRect.set(0, 0, 0, 0);
return;
}
if (mOrientation == VERTICAL) {
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
}
}
}
为了用我们自己的分割线
在 drawable 中建一个 divider.xml 的文件
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" > <gradient
android:centerColor="#ff00ff00"
android:endColor="#ff0000ff"
android:startColor="#ffff0000"
android:type="linear" />
<size android:height="4dp"/> </shape>
然后我们再在 style 文件中放一个 item ,name 就叫 android:listDivider;引入上面的文件。
<resources> <!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:listDivider">@drawable/divider</item>
</style> </resources>
RecyclerView 使用指南的更多相关文章
- RecyclerView 源码分析(一) —— 绘制流程解析
概述 对于 RecyclerView 是那么熟悉又那么陌生.熟悉是因为作为一名 Android 开发者,RecyclerView 是经常会在项目里面用到的,陌生是因为只是知道怎么用,但是却不知道 Re ...
- 安卓权威编程指南 挑战练习 13.8 用于RecyclerView的空视图
当前,CriminalIntent应用启动后,会显示一个空白列表.从用户体验上来讲,即使crime列表 是空的,也应展示提示或解释类信息. 请设置空视图展示类似“没有crime记录可以显示”的信息.再 ...
- 安卓权威编程指南 挑战练习:实现高效RecyclerView刷新
Adapter的notifyDataSetChanged方法会通知RecyclerView刷新全部的可见列表项. 在CriminalIntent应用里,这个方法不够高效,我们知道,返回CrimeLis ...
- Android RecyclerView体验(一)- 简介
在网上关于RecyclerView的基本使用方式已经有了比较详细介绍,而且其设计结构也类似于ListView,所以本文将不重点介绍如何使用,在文末的引用中都可以相关内容.这里主要是介绍Recycler ...
- Android开发之漫漫长途 XVI——ListView与RecyclerView项目实战
该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...
- Android简易实战教程--第四十六话《RecyclerView竖向和横向滚动》
Android5.X后,引入了RecyclerView,这个控件使用起来非常的方便,不但可以完成listView的效果,而且还可以实现ListView无法实现的效果.当然,在新能方便也做了大大的提高. ...
- Android开发指南--0 总览
无意间发现一个网站,主打IOS方面的教程,然而作为一个Android开发者,我就找了下网站里有没有Android的教程,还真有,这里就翻译一下. 翻译目标教程:https://www.raywende ...
- Android(Lollipop/5.0) Material Design(二) 入门指南
Material Design系列 Android(Lollipop/5.0)Material Design(一) 简介 Android(Lollipop/5.0)Material Design(二) ...
- MVVM Light Toolkit使用指南
原文:MVVM Light Toolkit使用指南 原文地址: https://blog.csdn.net/ldld1717/article/details/77040077 概述 MVVM Lig ...
随机推荐
- jQuery插件初级练习2答案
html: $.font($("p"),"30px").html("变化了") jQuery: $.extend({ font:functi ...
- Oracle EBS使用adpatch工具打patch过程(hotpatch mode)
目录(?)[-] 从Metalink下载补丁 上传解压 使用adpatch来打patch完整的日志 检查patch是否打成功 adpatch的日志文件 补充关于Oracle EBS Patch的类型 ...
- 64位进程调用32位dll的解决方法
64位进程调用32位dll的解决方法 最近做在Windows XP X64,VS2005环境下做32位程序编译为64位程序的工作,遇到了一些64位编程中可能遇到的问题:如内联汇编(解决方法改为C/ ...
- Windows核心编程:第4章 进程
Github https://github.com/gongluck/Windows-Core-Program.git //第4章 进程.cpp: 定义应用程序的入口点. // #include &q ...
- 【VB.NET】利用纯真IP数据库查询IP地址及信息
几年前从某个博客抄来的,已经忘记原地址了,如果需要C#版的,可以在博客园搜到吧.我因为自己用,所以转换为了VBNET代码,而且也放置了很久,今天无意间翻出来,就分享给大家吧. 首先,先下载 纯真数据库 ...
- StriveEngine-UDP
文章中的StriveEngine.dll版本为V3.9.0.0,源码下载请到 这里 UDP比TCP通信,就相对简单多了 先上代码,建立2个控制台程序,分别为SEUDP1,SEUDP2,其中SEUDP1 ...
- 在ASP.NET MVC应用中开发插件框架(中英对照)
[原文] Developing a plugin framework in ASP.NET MVC with medium trust [译文] 在ASP.NET MVC应用中开发一个插件框架 I’v ...
- [翻译]Bitmap的异步加载和缓存
内容概述 [翻译]开发文档:android Bitmap的高效使用 本文内容来自开发文档"Traning > Displaying Bitmaps Efficiently", ...
- 「PKUSC2018」星际穿越(倍增)
倍增好题啊! 我们我们预处理 \(f[x][i]\) 表示 \(x\) 点最左到达的端点,\(sum[x][i]\) 表示 \(x\) 点最左到达的端点时 \(f[x][i]\sim x\) 的答案, ...
- cad.net之ACAD和GCAD环境变量获取
#if AC2006 || AC2007 || AC2008 || AC2009 || AC2010 || AC2011 || AC2012 [System.Security.SuppressUnma ...