前言
哇,感觉好久都没有写博客了。前一段时间在忙Android Bus的教程系列,虽说是So Easy的一篇,但也确确实实了解了一些东西。这里,感谢大家的肯定,也感谢自己,让我们一起,加油~!
今天为大家带来RecyclerView最全解析,包含基础使用以及到最后实际开发中可能会遇到的方面都会进行说明,希望给大家带来新的感官~
本文目标
阅读此文后,你会掌握如下技能:
RecyclerView初步使用;
RecyclerView设置监听事件;
RecyclerView动态布局展示;
RecyclerView添加、删除数据。
RecyclerView初识
首先老规矩,上图,让大家更清晰的看到学习了RecyclerView初识,可以实实在在的玩点什么效果,如> PS:由于图片压的太小,导致效果不是很清晰,见谅。
简单查看效果后,让我们开启RecyclerView之旅~
Hello RecyclerView
RecyclerView,Android 5.0 (LOLLIPOP) 新增加的类属于Material Design 支持中的一个组件,它的出现,替代了原有的ListView以及GirdView,它的高内聚低耦合以及自带性能优化让人沉醉不已~不过这里需要说明下,RecyclerView最低兼容到Android 3.0,大家切记~
下面,一起来看看官方如何介绍这款神器~附上官方RecyclerView学习地址:
> https://developer.android.google.cn/reference/android/support/v7/widget/RecyclerView.html
首先我们了解下RecyclerView继承以及实现关系,我们看看从这些东西中,我们可以了解到什么有用信息。分解:
> 首先,RecyclerView继承于ViewGroup,也就说明RecycleView是一个View组,也就是一个容器;
>
> 其次,实现ScrollingView以及NestedScrollingChild2,使其具有可滑动的性质;
>
> 综上俩点所述,我们晓得了:RecyclerView是一个容器,它通过实现从而使其本身具有可滑动的属性。
谷歌对其称赞如下:
> A flexible view for providing a limited window into a large data set.
>
> 用于向大型数据集提供有限窗口的灵活视图。<font color=#FF00>(在此,大家可以理解为,RecyclerView主要可用于展示数据量比较大且展示的视图风格相当灵活的场景)
其他详情大家可直接查阅官方文档进行了解学习,下面,开撸~
RecyclerView 初使用
使用RecycleView的时候,大家不需要想太多,当初我们怎么玩ListView or GirdView,如今我们就怎么玩RecyclerView,只不过我们只需要注意部分的小细节而已;
1. 引入依赖
之前我们的ListView以及GirdView已经包含在v4包中,而RecyclerView则需要引入远程依赖,如下:
compile 'com.android.support:recyclerview-v7:21.0.3'
2. UI 搭建
老规矩,搭建UI界面:
3. Adapter 创建
这里需要明确如下几点:
创建Adapter需要继承RecyclerView.Adapter并添加ViewHolder,而创建的ViewHolder又必须继承RecyclerView.ViewHolder;
实现onCreateViewHolder():创建视图、onBindViewHolder():绑定数据源以及getItemCount():获取Item个数;
关键点如上,下面将在代码中进行具体说明。
package com.materialdesignstudy.activity.adapter;import android.support.v7.widget.RecyclerView;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.TextView;import com.materialdesignstudy.R;import java.util.List;/** * Created by HLQ on 2017/9/25 */public class MyRecycleViewAdapter extends RecyclerView.Adapter { private final List mStrList; /** * 接收数据源 * * @param strList */ public MyRecycleViewAdapter(List strList) { mStrList = strList; } /** * 创建视图 * * @param parent 父容器 * @param viewType view类型 可用于后期进行多布局兼容 * @return */ @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { // 引入布局 ViewHolder viewHolder = new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recycle_view, parent, false)); return viewHolder; } /** * 绑定数据 * * @param holder * @param position */ @Override public void onBindViewHolder(ViewHolder holder, int position) { holder.tvShow.setText(mStrList.get(position)); } /** * 获取item个数 * * @return */ @Override public int getItemCount() { return mStrList.size(); } class ViewHolder extends RecyclerView.ViewHolder { private TextView tvShow; /** * 实例化 View 布局优化 * * @param itemView */ public ViewHolder(View itemView) { super(itemView); tvShow = (TextView) itemView.findViewById(R.id.id_item_r); } } }
4. 初始化以及展示
这里大家需要掌握的是,RecyclerView通过LayoutManager为我们提供了三种展示方式:
LinearLayoutManager: 线性布局展示方式,默认垂直;
GridLayoutManager: GridView展示方式;
StaggeredGridLayoutManager: 瀑布流方式。
接下来开始撸码~
package com.materialdesignstudy.activity;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.support.v7.widget.GridLayoutManager;import android.support.v7.widget.LinearLayoutManager;import android.support.v7.widget.RecyclerView;import android.support.v7.widget.StaggeredGridLayoutManager;import com.materialdesignstudy.R;import com.materialdesignstudy.activity.adapter.MyRecycleViewAdapter;import com.materialdesignstudy.activity.adapter.MyStaggeredAdapter;import java.util.ArrayList;import java.util.List;/** * RecyclerView Study * 谷歌推出替代ListView、GirdView工具 * 具有高度解耦和 */public class RecycleViewActivity extends AppCompatActivity { private RecycleViewActivity selfActivity = RecycleViewActivity.this; private RecyclerView mRecyclerView; private MyRecycleViewAdapter myAdapter; private MyStaggeredAdapter myStaggerAdapter; private ArrayList mStrList = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_recycle_view); initView(); } private List initData() { for (int i = 0; i < 60; i++) { mStrList.add("item:" + i); } return mStrList; } private void initView() { mRecyclerView = (RecyclerView) findViewById(R.id.id_recycler_view); myAdapter = new MyRecycleViewAdapter(initData()); // LayoutManager 布局拜访管理器(线性摆放、瀑布流) mRecyclerView.setLayoutManager(new LinearLayoutManager(selfActivity)); // 默认垂直 mRecyclerView.setAdapter(myAdapter); } }
4.1 设置水平展示
将setLayoutManager修改如下:
mRecyclerView.setLayoutManager(new LinearLayoutManager(selfActivity,LinearLayoutManager.HORIZONTAL,false)); // 设置水平
4.2 设置水平 且数据从右侧开始展示
mRecyclerView.setLayoutManager(new LinearLayoutManager(selfActivity,LinearLayoutManager.HORIZONTAL,true)); // 设置水平 且数据从右侧开始展示
4.3 GirdView 效果
mRecyclerView.setLayoutManager(new GridLayoutManager(selfActivity,3)); // GirdView 效果
4.4 瀑布流 效果
首先我们来回顾下瀑布流的简单效果:
从上可见,每个子控件的高度不同,所以我们这里需要动态模拟每个子控件高度以及为了模拟显示效果分别设置不同的效果,如下,在Adapter构造新增:
for (int i = 0; i < strList.size(); i++) { mHeightlist.add((int) Math.max(200, Math.random() * 550)); }
数据绑定中进行设置:
LayoutParams params = holder.tvShow.getLayoutParams(); params.height = mHeightlist.get(position); holder.tvShow.setBackgroundColor(Color.rgb(100, (int) (Math.random() * 255), (int) (Math.random() * 255))); holder.tvShow.setLayoutParams(params);
activity中设置瀑布流:
mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(3, LinearLayoutManager.VERTICAL));
效果如下(注意,以上代码只贴出关键内容,详情查阅GitHub):
RecyclerView 中级进阶
RecyclerView最大的特点就是高内聚、低耦合,虽说思想很棒,但是同时也为我们带来一些小麻烦。比如,我想要点击效果怎么办?
我们可以通过模仿ListView的事件,去通过接口形式间接完成需求。
一、添加点击事件
1. 定义一个 item项单击事件 接口,如下:
/** * 单击事件 */ public interface OnItemClickListener { void onItemClick(View view, int position); }
2. 对外提供设置接口方法;
/** * 设置单击事件 * * @param onItemClickListener */ public void setOnItemClickListener(OnItemClickListener onItemClickListener) { this.mOnItemClickListener = onItemClickListener; }
3. 绑定控件的同时设置item单击事件;
// 设置点击事件 if (mOnItemClickListener != null) { holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mOnItemClickListener.onItemClick(view, position); } }); }
4. activity设置并回调。
myStaggerAdapter.setOnItemClickListener(new MyStaggeredAdapter.OnItemClickListener() { @Override public void onItemClick(View view, int position) { Toast.makeText(selfActivity, "点击了item:" + position, Toast.LENGTH_SHORT).show(); } });
二、处理item点击position错乱问题
在这里,如果延续上面的写法,很可能引发一个问题,那就是当item点击的时候,position错乱问题。
那么,问题出现的原因是在当item被点击后,由于position没有更新所以才会造成混乱问题。那么,我们可不可以在点击的时候,将position传递下去呢?答案是肯定的,只是需要我们重写OnClickListener即可,如下:
/** * 重写OnClick解决item点击时position可能出现错位bug */ class MyClickListener implements View.OnClickListener { private int mPosition; // 记录当前点击位置 public MyClickListener(int position) { this.mPosition = position; } @Override public void onClick(View v) { mOnItemClickListener.onItemClick(v, mPosition); } }
那么如何调用呢?在原有基础上不变,而绑定数据时,添加监听修改如下即可:
if (mOnItemClickListener != null) { // 解决item position错位 holder.itemView.setOnClickListener(new MyClickListener(position)); }
三、照猫画虎 设置长按事件
长按事件实现的逻辑和单击没什么区别,下面简单写下逻辑。
定义长按事件接口以及对外暴露方法;
重写OnLongClickListener()方法,避免item点击时position错乱;
设置长按事件。
下面按照如上逻辑附上关键代码:
1. 定义 and 暴露
/** * 长按事件 */ public interface onItemLongClickListener { void onItemLongClick(View view, int position); } public void setOnItemLongClickListener(onItemLongClickListener onItemLongClickListener) { this.mOnItemLongClickListener = onItemLongClickListener; }
2. 重写 避免 position错乱
class MyLongClickListener implements View.OnLongClickListener { private int mPosition; public MyLongClickListener(int position) { this.mPosition = position; } @Override public boolean onLongClick(View v) { mOnItemLongClickListener.onItemLongClick(v, mPosition); return true; } }
3. 绑定事件
// 设置长按事件 if (mOnItemLongClickListener != null) { holder.itemView.setOnLongClickListener(new MyLongClickListener(position)); }
4. 设置监听并回调
myStaggerAdapter.setOnItemLongClickListener(new MyStaggeredAdapter.onItemLongClickListener() { @Override public void onItemLongClick(View view, int position) { Toast.makeText(selfActivity, "长按了item:" + position, Toast.LENGTH_SHORT).show(); } });
四、动态改变布局
很简单,主要通过LayoutManager去动态管理,如下,在默认是一种布局样式,点击按钮可进行切换。
/** * 点击动态改变RecyclerView布局 * * @param view */ public void showChange(View view) { if (!isChange) { mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(3, LinearLayoutManager.VERTICAL)); } else { mRecyclerView.setLayoutManager(new LinearLayoutManager(selfActivity)); } isChange = !isChange; }
五、新增,删除item项
到目前为止,我们的RecyclerView已具备了显示以及相关事件功能,那下面,我们就来掩饰项目中可能会出现的新增数据以及删除数据。
逻辑很easy,但是还是要简单的分解下:
adapter对外暴露新增以及删除的方法,通过接收要新增or移除itemId从而对数据以及UI进行刷新。
/** * 新增item数据 * * @param position */ public void addItemData(int position) { mStrlist.add(position, "新增item:" + position); // 会影响效率// notifyDataSetChanged(); // 刷新新增数据位置 notifyItemInserted(position); // 更新下方所有item position if (position != mStrlist.size()) { notifyItemRangeChanged(position, mStrlist.size() - position); } } /** * 删除item数据 * * @param position */ public void removeItemData(int position) { mStrlist.remove(position);// notifyDataSetChanged(); // 刷新removed位置数据 notifyItemRemoved(position); // 更新下方所有item position if (position != mStrlist.size()) { notifyItemRangeChanged(position, mStrlist.size() - position); } }
这里LZ觉得还是有必要去说明几点,如下:
之前ListView,我们刷新数据通常采用notifyDataSetChanged(),而这里为什么说会影响效率呢?
> notifyDataSetChanged()的作用是刷新所有数据,但是我们操作所影响的只是部分item,甚至仅仅是一条item,那么,现在让你选择,你是希望全部更新?还是仅仅更新变更的那条item呢?结果显而易见了吧。
为什么操作数据之后,我们要对其下方的itemid进行更新呢?
> 简单举个例子:假设现在item有三条,分别为张三,李四,王五,其所对应的下标为0、1、2。那么我现在删除了李四,那么王五的下标是多少呢?是2,原因呢?我们仅仅对数据进行操作,但是其下标,我们没有做处理。So,这里要记得同时更新下标,否则就会出现错乱现象。
GitHub 查看地址
> https://github.com/HLQ-Struggle/MaterialDesignStudy
结束
学习的路上,孤寂,能做的就是坚信自己,保持自己傲气,坚定不移的走下去~
只要愿意花时间,并且找方法,未来就不会离得太过遥远~