recycleView 使用指南1
最近看了很多 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>
recycleView 使用指南1的更多相关文章
- 《Android编程权威指南》CriminalIntent项目梳理
相信很多新手或者初级开发人员都已经买了第2版的<Android编程权威指南>, 这本书基于Android Studio开发,对入门人员来说是很好的选择,但是很可惜的是, 在完成一个项目后, ...
- JavaScript权威指南 - 函数
函数本身就是一段JavaScript代码,定义一次但可能被调用任意次.如果函数挂载在一个对象上,作为对象的一个属性,通常这种函数被称作对象的方法.用于初始化一个新创建的对象的函数被称作构造函数. 相对 ...
- UE4新手之编程指南
虚幻引擎4为程序员提供了两套工具集,可共同使用来加速开发的工作流程. 新的游戏类.Slate和Canvas用户接口元素以及编辑器功能可以使用C++语言来编写,并且在使用Visual Studio 或 ...
- JavaScript权威指南 - 对象
JavaScript对象可以看作是属性的无序集合,每个属性就是一个键值对,可增可删. JavaScript中的所有事物都是对象:字符串.数字.数组.日期,等等. JavaScript对象除了可以保持自 ...
- JavaScript权威指南 - 数组
JavaScript数组是一种特殊类型的对象. JavaScript数组元素可以为任意类型,最大容纳232-1个元素. JavaScript数组是动态的,有新元素添加时,自动更新length属性. J ...
- const extern static 终极指南
const extern static 终极指南 不管是从事哪种语言的开发工作,const extern static 这三个关键字的用法和原理都是我们必须明白的.本文将对此做出非常详细的讲解. co ...
- Atitit.研发管理软件公司的软资产列表指南
Atitit.研发管理软件公司的软资产列表指南 1. Isv模型下的软资产1 2. 实现层面implet1 3. 规范spec层1 4. 法则定律等val层的总结2 1. Isv模型下的软资产 Sof ...
- HA 高可用软件系统保养指南
又过了一年 618,六月是公司一年一度的大促月,一般提前一个月各系统就会减少需求和功能的开发,转而更多去关注系统可用性.稳定性和管控性等方面的非功能需求.大促前的准备工作一般叫作「备战」,可以把线上运 ...
- 第六代智能英特尔® 酷睿™ 处理器图形 API 开发人员指南
欢迎查看第六代智能英特尔® 酷睿™ 处理器图形 API 开发人员指南,该处理器可为开发人员和最终用户提供领先的 CPU 和图形性能增强.各种新特性和功能以及显著提高的性能. 本指南旨在帮助软件开发人员 ...
随机推荐
- Tp框架获取客户端IP地址
/** * 获取客户端IP地址 * @param integer $type 返回类型 0 返回IP地址 1 返回IPV4地址数字 * @return mixed */ function get_cl ...
- php变量双击选择无法选择$符号
创建/Data/Packages/User/PHP.sublime-settings文件,内容为 { "word_separators": "./\\()\&qu ...
- 无法将类型为excel.applicationclass的com 强制转换为接口类型的解决方法[转]
c#解决方案EXCEL 导出 今天碰到客户的电脑在导出EXCEL的时候提示,无法将类型为 excel.applicationclass 的 com 强制转换为接口类型 excel._applicati ...
- Loadrunner--自动关联和手动关联
2017-06-09 15:32:45个人也属于刚刚开始学习,有什么不对的地方敬请指导:qq:389791447 一开始的时候,准备去学习怎么去关联.一时也毛不着头脑,就在网上找了一些视频看,有的人说 ...
- 用py2exe将python文件转换成exe可执行程序
1.首先需要安装py2exe模块,下载地址:http://www.lfd.uci.edu/~gohlke/pythonlibs/ 然后用pip install 命令安装py2exe模块,如果你用的py ...
- Chrome浏览器扩展开发系列之二:Google Chrome浏览器扩展的调试
1) 查看扩展程序的详细信息和ID 通过Chrome 浏览器的“ 工具->更多工具->扩展程序”,打开chrome://extensions页面,选中右上角的“开发者模式”,可以 ...
- 设置TrackMouseEvent捕获WM_MOUSEHOVER和WM_MOUSELEAVE消息
WM_MOUSEHOVER(非客户区消息为WM_NCMOUSEHOVER)消息表示鼠标在客户区悬浮消息,WM_MOUSELEAVE(非客户区消息为WM_NCMOUSELEAVE)为鼠标离开客户区消息, ...
- FreeRTOS——队列管理
1. 队列主要用于任务与任务.中断与任务之间的消息传递. 2. 创建队列时,请注意队列中数据单元的长度. 3. 通常情况,队列被作为FIFO(先进先出)使用,即数据从队列尾写入,从队列首读.当然,数据 ...
- nyoj_7:街区最短路径问题
做这题时,先假设目标点在某个位置,然后对其稍微移动dx,dy,分析对ans的影响.最终得,选点时,使一半的横坐标比目标点横坐标小,一半的纵坐标比目标点小,这样得到的ans最小. 题目链接: http: ...
- getcomputedstyle()获取border像素差异问题
getComputedStyle()方法返回的是一个CSS样式声明对象--CSSStyleDeclaration对象(与style属性的类型相同),包含当前元素所有最终使用的CSS属性值: <! ...