概述
2017.1.1开始写这篇博客。
目标:理清RecyclerView的来龙去脉,包括:发布历史,简单使用,架构分析及各个模块具体分析,实现原理,横向对比,以及实现各种通用性功能并对部分效果进行分析。
其他目标:希望通过这次学习能逐步形成一套适用于自我的学习方法和学习路径。
RecyclerView
是在2014年谷歌I/O大会上随Android L预览一起发布的,在预览版API文档中是这样描述的:
一个非常灵活的用于在有限的窗口范围内显示大量数据的控件。
所以RecyclerView
适用于那些有大量同类的View但是不能同时在屏幕中显示的情况,比如联系人、用户列表、音乐文件列表等等。想看到更多信息需要滚动试图,同时对离开屏幕的视图进行回收和重用。
简介
RecyclerView本身不关心视图相关的问题
由于ListView的紧耦合问题,谷歌的改进就是RecyclerView
本身不参与任何视图相关的问题。它不关心如何将子View放在合适的位置,也不关心如何分割这些子View,更不关心每个子View各自的外观。进一步来说,RecyclerView
只负责回收和重用的工作,这就是它名字的由来。
所有关于布局、绘制和其他相关的问题,也就是跟数据展示相关的所有问题,都被委派给了一些”插件化”的类来处理。这使得RecyclerView
的API变得非常灵活。你需要一个新的布局么?接入另一个LayoutManager
就可以了!你想要不同的动画么?接入一个新的ItemAnimator
就可以了,诸如此类。
以下是RecyclerView
中用于数据展示的一些重要的类,他们都是RecyclerView的内部类:
Adapter
:包装数据集合并且为每个条目创建视图。ViewHolder
:保存用于显示每个数据条目的子View。LayoutManager
:将每个条目的视图放置于适当的位置。ItemDecoration
:在每个条目的视图的周围或上面绘制一些装饰视图。ItemAnimator
:在条目被添加、移除或者重排序时添加动画效果。
基本使用
RecyclerView 是Support Library的一部分。所以只需要在app/build.gradle中添加以下依赖,便能立即使用:
dependencies {
compile 'com.android.support:recyclerview-v7:23.2.0'
}
官方使用案例:
googlesamples/android-RecyclerView
官网API:
RecyclerView | Android Developers
主要步骤如下:
- 获取
RecyclerView
的引用 - 创建一个
LayoutManager
并添加 - 创建一个
Adapter
并添加 - 如果需要的话,创建1个或多个
ItemDecoration
并添加 - 如果需要的话,创建
ItemAnimator
并添加 - 如果需要的话,创建1个或多个监听器并添加
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false);
GridLayoutManager gridLayoutManager = new GridLayoutManager(this,2,GridLayoutManager.HORIZONTAL,false);
StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(staggeredGridLayoutManager);
adapter = new RecyclerViewAdapter(appBeanArrayList);
adapter.setItemClickListener(this);
recyclerView.setAdapter(adapter);
子项分析
类名 | 作用 |
---|---|
RecyclerView.LayoutManager | 负责Item视图的布局的显示管理 |
RecyclerView.ItemDecoration | 添加每个Item的装饰,给每一项Item视图添加子View,例如可以进行画分隔线之类 |
RecyclerView.ItemAnimator | 负责处理数据添加移除重排序时的动画效果 |
RecyclerView.Adapter | 为每一项Item创建视图 |
RecyclerView.ViewHolder | 承载Item视图的子布局 |
LayoutManager
RecyclerView
将所有的显示规则交给一个叫 LayoutManager
的类去完成了。
LayoutManager
是一个抽象类,系统已经为我们提供了三个默认的实现类,分别是 LinearLayoutManager 、 GridLayoutManager 、StaggeredGridLayoutManager
,从名字我们就能看出来了,分别是:线性显示、网格显示、瀑布流显示。当然你也可以通过继承这些类来扩展实现自己的 LayougManager。
java.lang.Object
↳ android.view.View
↳ android.view.ViewGroup
↳ android.support.v7.widget.RecyclerView
首先是 RecyclerView 继承关系,可以看到,与 ListView 不同,他是一个 ViewGroup。既然是一个 View,那么就不可少的要经历 onMeasure()、onLayout()、onDraw() 这三个方法。 实际上,RecyclerView 就是将 onMeasure()、onLayout() 交给了 LayoutManager 去处理,因此如果给 RecyclerView 设置不同的 LayoutManager 就可以达到不同的显示效果,因为onMeasure()、onLayout()都不同了嘛。
ItemDecoration
ItemDecoration 是为了显示每个 item 之间分隔样式的。它的本质实际上就是一个 Drawable。当 RecyclerView 执行到 onDraw() 方法的时候,就会调用到他的 onDraw(),这时,如果你重写了这个方法,就相当于是直接在 RecyclerView 上画了一个 Drawable 表现的东西。 而最后,在他的内部还有一个叫getItemOffsets()的方法,从字面就可以理解,他是用来偏移每个 item 视图的。当我们在每个 item 视图之间强行插入绘画了一段 Drawable,那么如果再照着原本的逻辑去绘 item 视图,就会覆盖掉 Decoration 了,所以需要getItemOffsets()这个方法,让每个 item 往后面偏移一点,不要覆盖到之前画上的分隔样式了。
ItemAnimator
每一个 item 在特定情况下都会执行的动画。说是特定情况,其实就是在视图发生改变,我们手动调用notifyxxxx()的时候。通常这个时候我们会要传一个下标,那么从这个标记开始一直到结束,所有 item 视图都会被执行一次这个动画。
github上已经有很多类似的项目了,这里我们直接引用下:RecyclerViewItemAnimators,大家自己下载查看。
提供了SlideInOutLeftItemAnimator,SlideInOutRightItemAnimator,
SlideInOutTopItemAnimator,SlideInOutBottomItemAnimator等动画效果。
gabrielemariotti/RecyclerViewItemAnimators: An Android library which provides simple Item animations to RecyclerView items
Adapter
首先是适配器,适配器的作用都是类似的,用于提供每个 item 视图,并返回给 RecyclerView 作为其子布局添加到内部。
但是,与 ListView 不同的是,ListView 的适配器是直接返回一个 View,将这个 View 加入到 ListView 内部。而 RecyclerView 是返回一个 ViewHolder 并且不是直接将这个 holder 加入到视图内部,而是加入到一个缓存区域,在视图需要的时候去缓存区域找到 holder 再间接的找到 holder 包裹的 View。
ViewHolder
每个 ViewHolder 的内部是一个 View,并且 ViewHolder 必须继承自RecyclerView.ViewHolder类。 这主要是因为 RecyclerView 内部的缓存结构并不是像 ListView 那样去缓存一个 View,而是直接缓存一个 ViewHolder ,在 ViewHolder 的内部又持有了一个 View。既然是缓存一个 ViewHolder,那么当然就必须所有的 ViewHolder 都继承同一个类才能做到了。
Item的点击事件
用习惯了 ListView 的 OnItemClickListener ,RecyclerView 你的 OnItemClickListener 呢?
原来是 Google 的工程师分不清究竟是改给 listview 的 item 添加点击事件,还是应该给每个 item 的 view 添加点击事件,索性就不给 OnItemClickListener 了,然后在 support demo里面,你就会发现,RecyclerView 的 item 点击事件都是写在了 adapter 的 ViewHolder 里面。
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ItemViewHolder> implements View.OnClickListener{
private ArrayList<AppBean> appBeanArrayList = new ArrayList<>();
public RecyclerViewAdapter(ArrayList<AppBean> appBeanArrayList) {
this.appBeanArrayList = appBeanArrayList;
}
@Override
public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = View.inflate(parent.getContext(),R.layout.item_recyler,null);
view.setOnClickListener(this);
return new ItemViewHolder(view);
}
@Override
public void onBindViewHolder(ItemViewHolder holder, int position) {
AppBean appBean = appBeanArrayList.get(position);
holder.itemView.setTag(position);
holder.img_icon.setImageDrawable(appBean.getDrawable());
holder.tv_name.setText(appBean.getAppName());
}
@Override
public int getItemCount() {
return appBeanArrayList.size();
}
@Override
public void onClick(View v) {
if (itemClickListener != null) {
//注意这里使用getTag方法获取数据
itemClickListener.onItemClick(v,(int)v.getTag());
}
}
public class ItemViewHolder extends RecyclerView.ViewHolder{
ImageView img_icon;
TextView tv_name;
public ItemViewHolder(View itemView) {
super(itemView);
img_icon = (ImageView) itemView.findViewById(R.id.img_icon);
tv_name = (TextView) itemView.findViewById(R.id.tv_name);
}
}
private ItemClickListener itemClickListener;
public interface ItemClickListener {
void onItemClick(View view , int position);
}
public void setItemClickListener(ItemClickListener itemClickListener){
this.itemClickListener = itemClickListener ;
}
}
item的点击事件可以写在onCreateViewHolder里面也可以写在onBindViewHolder里面,还可以写在ItemViewHolder的构造方法里面。
- onCreateViewHolder里面需要在onBindViewHolder时对itemView设置tag,以获取position
- 在ItemViewHolder的构造方法里面,可以对itemView的容器里面的各个子view添加点击事件
- 在onBindViewHolder里面,应该是最方便最优美的。
添加分隔线
参考开源实验室 - kymjs张涛的封装
public class Divider extends RecyclerView.ItemDecoration {
private Drawable mDivider;
private int leftMargin, rightMargin, topMargin, bottomMargin;
private int width, height;
private int mOrientation;
public Divider(Drawable divider, int orientation) {
setDivider(divider);
setOrientation(orientation);
}
private void setDivider(Drawable divider) {
this.mDivider = divider;
if (mDivider == null) {
mDivider = new ColorDrawable(0xffff0000);
}
width = mDivider.getIntrinsicWidth();
height = mDivider.getIntrinsicHeight();
}
private void setOrientation(int orientation) {
if (orientation != LinearLayoutManager.HORIZONTAL && orientation != LinearLayoutManager.VERTICAL) {
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}
public void setMargin(int left, int top, int right, int bottom) {
this.leftMargin = left;
this.topMargin = top;
this.rightMargin = right;
this.bottomMargin = bottom;
}
public void setHeight(int height) {
this.height = height;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public int getWidth() {
return width;
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
if (mOrientation == LinearLayoutManager.HORIZONTAL) {
drawHorizontal(c, parent);
} else {
drawVertical(c, parent);
}
}
public void drawHorizontal(Canvas c, RecyclerView parent) {
final int top = parent.getPaddingTop() + topMargin;
final int bottom = parent.getHeight() - parent.getPaddingBottom() - bottomMargin;
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 + leftMargin;
final int right = left + width;
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
public void drawVertical(Canvas c, RecyclerView parent) {
final int left = parent.getPaddingLeft() + leftMargin;
final int right = parent.getWidth() - parent.getPaddingRight() - rightMargin;
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 top = child.getBottom() + params.bottomMargin + topMargin;
final int bottom = top + height;
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
if (mOrientation == LinearLayoutManager.HORIZONTAL) {
outRect.set(0, 0, leftMargin + width + rightMargin, 0);
} else {
outRect.set(0, 0, 0, topMargin + height + bottomMargin);
}
}
}
简单说下:
- onDraw就是每个item绘制之前绘制,在item下面
- onDrawOver就是每个item绘制之后绘制,在item上面
- getItemOffsets就是设置item四周的间隔,方便onDraw和onDrawOver的展示
ViewHolder封装
public class ViewHolder extends RecyclerView.ViewHolder {
private SparseArray<View> mViews;//集合类,layout里包含的View,以view的id作为key,value是view对象
private View mConvertView;
private Context mContext;
public ViewHolder(Context context, View itemView) {
super(itemView);
mContext = context;
mConvertView = itemView;
mViews = new SparseArray<View>();
}
public static ViewHolder createViewHolder(Context context, View itemView) {
ViewHolder holder = new ViewHolder(context, itemView);
return holder;
}
public static ViewHolder createViewHolder(Context context,
ViewGroup parent, int layoutId) {
View itemView = LayoutInflater.from(context).inflate(layoutId, parent,
false);
ViewHolder holder = new ViewHolder(context, itemView);
return holder;
}
/**
* 通过viewId获取控件
*
* @param viewId
* @return
*/
public <T extends View> T getView(int viewId) {
View view = mViews.get(viewId);
if (view == null) {
view = mConvertView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T) view;
}
public View getConvertView() {
return mConvertView;
}
/****以下为辅助方法*****/
/**
* 设置TextView的值
*
* @param viewId
* @param text
* @return
*/
public ViewHolder setText(int viewId, String text) {
TextView tv = getView(viewId);
tv.setText(text);
return this;
}
public ViewHolder setImageResource(int viewId, int resId) {
ImageView view = getView(viewId);
view.setImageResource(resId);
return this;
}
public ViewHolder setImageBitmap(int viewId, Bitmap bitmap) {
ImageView view = getView(viewId);
view.setImageBitmap(bitmap);
return this;
}
public ViewHolder setImageDrawable(int viewId, Drawable drawable) {
ImageView view = getView(viewId);
view.setImageDrawable(drawable);
return this;
}
public ViewHolder setBackgroundColor(int viewId, int color) {
View view = getView(viewId);
view.setBackgroundColor(color);
return this;
}
public ViewHolder setBackgroundRes(int viewId, int backgroundRes) {
View view = getView(viewId);
view.setBackgroundResource(backgroundRes);
return this;
}
public ViewHolder setTextColor(int viewId, int textColor) {
TextView view = getView(viewId);
view.setTextColor(textColor);
return this;
}
public ViewHolder setTextColorRes(int viewId, int textColorRes) {
TextView view = getView(viewId);
view.setTextColor(mContext.getResources().getColor(textColorRes));
return this;
}
@SuppressLint("NewApi")
public ViewHolder setAlpha(int viewId, float value) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
getView(viewId).setAlpha(value);
} else {
// Pre-honeycomb hack to set Alpha value
AlphaAnimation alpha = new AlphaAnimation(value, value);
alpha.setDuration(0);
alpha.setFillAfter(true);
getView(viewId).startAnimation(alpha);
}
return this;
}
public ViewHolder setVisible(int viewId, boolean visible) {
View view = getView(viewId);
view.setVisibility(visible ? View.VISIBLE : View.GONE);
return this;
}
public ViewHolder linkify(int viewId) {
TextView view = getView(viewId);
Linkify.addLinks(view, Linkify.ALL);
return this;
}
public ViewHolder setTypeface(Typeface typeface, int... viewIds) {
for (int viewId : viewIds) {
TextView view = getView(viewId);
view.setTypeface(typeface);
view.setPaintFlags(view.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG);
}
return this;
}
public ViewHolder setProgress(int viewId, int progress) {
ProgressBar view = getView(viewId);
view.setProgress(progress);
return this;
}
public ViewHolder setProgress(int viewId, int progress, int max) {
ProgressBar view = getView(viewId);
view.setMax(max);
view.setProgress(progress);
return this;
}
public ViewHolder setMax(int viewId, int max) {
ProgressBar view = getView(viewId);
view.setMax(max);
return this;
}
public ViewHolder setRating(int viewId, float rating) {
RatingBar view = getView(viewId);
view.setRating(rating);
return this;
}
public ViewHolder setRating(int viewId, float rating, int max) {
RatingBar view = getView(viewId);
view.setMax(max);
view.setRating(rating);
return this;
}
public ViewHolder setTag(int viewId, Object tag) {
View view = getView(viewId);
view.setTag(tag);
return this;
}
public ViewHolder setTag(int viewId, int key, Object tag) {
View view = getView(viewId);
view.setTag(key, tag);
return this;
}
public ViewHolder setChecked(int viewId, boolean checked) {
Checkable view = (Checkable) getView(viewId);
view.setChecked(checked);
return this;
}
/**
* 关于事件的
*/
public ViewHolder setOnClickListener(int viewId,
View.OnClickListener listener) {
View view = getView(viewId);
view.setOnClickListener(listener);
return this;
}
public ViewHolder setOnTouchListener(int viewId,
View.OnTouchListener listener) {
View view = getView(viewId);
view.setOnTouchListener(listener);
return this;
}
public ViewHolder setOnLongClickListener(int viewId,
View.OnLongClickListener listener) {
View view = getView(viewId);
view.setOnLongClickListener(listener);
return this;
}
}
首先我们确定下ViewHolder
的主要的作用,实际上是通过成员变量存储对应的convertView
中需要操作的字View,避免每次findViewById
,从而提升运行的效率。那么既然是通用的View,那么对于不同的ItemType肯定没有办法确定创建哪些成员变量View,取而代之的只能是个集合来存储了,然后封装findViewById
的方法和各种set方法。
参考:
为RecyclerView打造通用Adapter 让RecyclerView更加好用 - Hongyang - 博客频道 - CSDN.NET
Adapter封装
先来看下最终的封装结果:
public abstract class RecyclerViewAdapter<T> extends RecyclerView.Adapter<ItemViewHolder> {
protected Context mContext;
protected int mItemLayoutId;
protected List<T> arrayList;
protected LayoutInflater mInflater;
public RecyclerViewAdapter(Context context,ArrayList<T> arrayList, int mItemLayoutId) {
this.mContext = context;
this.mInflater = LayoutInflater.from(context);
this.arrayList = arrayList;
this.mItemLayoutId = mItemLayoutId;
}
/**
* onBindViewHolder这里主要用于数据、事件绑定,我们这里直接抽象出去,让用户去操作
* @param holder
* @param position
*/
abstract public void bindData(ItemViewHolder holder, int position);
@Override
public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ItemViewHolder itemViewHolder = ItemViewHolder.get(parent.getContext(),parent,mItemLayoutId);
return itemViewHolder;
}
@Override
public void onBindViewHolder(ItemViewHolder holder, final int position) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
itemClickListener.onItemClick(v, position);
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
itemClickListener.onItemLongClick(v, position);
return true;
}
});
bindData(holder, position);
}
@Override
public int getItemViewType(int position) {
return super.getItemViewType(position);
}
@Override
public int getItemCount() {
return arrayList.size();
}
public void addItem(T appBean, int position) {
arrayList.add(position, appBean);
notifyItemInserted(position); //Attention!
}
public void removeItem(T model) {
int position = arrayList.indexOf(model);
arrayList.remove(position);
notifyItemRemoved(position);//Attention!
}
private OnItemClickListener itemClickListener;
public interface OnItemClickListener {
void onItemClick(View itemView, int pos);
void onItemLongClick(View itemView, int pos);
}
public void setOnItemClickListener(OnItemClickListener listener) {
itemClickListener = listener;
}
}
使用:
adapter = new RecyclerViewAdapter<AppBean>(appBeanArrayList, R.layout.item_recyler) {
@Override
public void bindData(ItemViewHolder holder, int position) {
if(position%2 == 0){
holder.itemView.setBackgroundResource(0);
}
AppBean appBean = appBeanArrayList.get(position);
holder.setText(R.id.tv_name,appBean.getAppName()).getImageView(R.id.img_icon).setImageDrawable(appBean.getDrawable());
}
};
//添加item点击事件监听
((RecyclerViewAdapter) adapter).setOnItemClickListener(this);
recyclerView.setAdapter(adapter);
多ItemViewType
多种ItemViewType,一般我们的写法是:
- 复写getItemViewType,根据我们的bean去返回不同的类型
- onCreateViewHolder中根据itemView去生成不同的ViewHolder
引入一个接口:
public interface MultiItemTypeSupport<T>
{
int getLayoutId(int itemType);
int getItemViewType(int position, T t);
}
那么MultiItemCommonAdapter就是下面这样了:
public abstract class MultiItemCommonAdapter<T> extends CommonAdapter<T>
{
protected MultiItemTypeSupport<T> mMultiItemTypeSupport;
public MultiItemCommonAdapter(Context context, List<T> datas,
MultiItemTypeSupport<T> multiItemTypeSupport)
{
super(context, -1, datas);
mMultiItemTypeSupport = multiItemTypeSupport;
}
@Override
public int getItemViewType(int position)
{
return mMultiItemTypeSupport.getItemViewType(position, mDatas.get(position));
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
int layoutId = mMultiItemTypeSupport.getLayoutId(viewType);
ViewHolder holder = ViewHolder.get(mContext, parent, layoutId;
return holder;
}
}
ExpandRecyclerView
类似与ExpandListView的分级效果:
参考:
一个RecyclerView实现多级折叠列表(TreeRecyclerView) - 简书
Android 打造任意层级树形控件 考验你的数据结构和设计 - Hongyang - 博客频道 - CSDN.NET
Android城市选择列表(一)——RecyclerView数据分组 - 犀利的小牛 - 博客频道 - CSDN.NET
http://blog.csdn.net/a1533588867/article/details/52933145
添加Header
参考:
ItemDecoration解析(三) 实现stickyHeader效果 - 简书
RecyclerView添加Header的正确方式 - Loader’s Blog - 博客频道 - CSDN.NET
Android 优雅的为RecyclerView添加HeaderView和FooterView - Hongyang - 博客频道 - CSDN.NET
【Android】让HeaderView也参与回收机制,自我感觉是优雅的为 RecyclerView 添加 HeaderView (FooterView)的解决方案 - zxt0601的博客 - 博客频道 - CSDN.NET
【Android 仿微信通讯录 导航分组列表-上】使用ItemDecoration为RecyclerView打造带悬停头部的分组列表 - zxt0601的博客 - 博客频道 - CSDN.NET
下拉刷新和上拉加载
拆解轮子之XRecyclerView - 彩笔学长 - 博客频道 - CSDN.NET
http://blog.csdn.net/xsf50717/article/details/51366922
click和touch一起处理
CoreModule/RecyclerItemClickListener.java at master · kymjs/CoreModule
https://github.com/kymjs/CoreModule/blob/master/CoreModule/recycler/src/main/java/com/kymjs/recycler/RecyclerItemClickListener.java
侧滑删除
animator
源码解析
引用:
RecyclerView初探 - 泡在网上的日子
深入浅出 RecyclerView|开源实验室-张涛
偷懒新姿势,打造属于RecyclerView的万能适配器Adapter和ViewHolder
RecyclerView使用介绍 - 泡在网上的日子
深入理解 RecyclerView 系列之一:ItemDecoration - Piasy的博客 | Piasy Blog
深入理解 RecyclerView 系列之二:ItemAnimator - Piasy的博客 | Piasy Blog
Android RecyclerView 使用完全解析 体验艺术般的控件 - Hongyang - 博客频道 - CSDN.NET
你必须了解的 RecyclerView 的五大开源项目 - 解决上拉加载、下拉刷新和添加 Header、Footer 等问题 - 掘金
https://gold.xitu.io/entry/570f716a1ea493006b5ecfbb
一种新的 Header View + RecyclerView 实现方式 | Drakeet 的个人博客
https://drakeet.me/recyclerview-with-header-new-practice
- | - |
---|---|
RecyclerView | 深入浅出 RecyclerView |
RecyclerView | XRecyclerView - 列表 (ListView) - 开源代码 |
RecyclerView | 真实项目运用-RecyclerView封装 - u014315849的博客 - 博客频道 - CSDN.NET |
RecyclerView | ItemDecoration解析(三) 实现stickyHeader效果 - 简书 |
RecyclerView | 专栏:玩转RecyclerView的方方面面 - 博客频道 - CSDN.NET |
RecyclerView | Android一步一步带你实现RecyclerView的拖拽和侧滑删除功能 |
RecyclerView | 用Recyclerview实现列表分组、下拉刷新以及上拉加载更多 - 简书 |
RecyclerView | 关于RecyclerView的文章 - 泡在网上的日子 |
RecyclerView | RecyclerView技术栈 - 泡在网上的日子 |
RecyclerView | 为RecyclerView打造通用Adapter 让RecyclerView更加好用 - Hongyang - 博客频道 - CSDN.NET |
RecyclerView | RecyclerView学习(二)—-高仿网易新闻栏目动画效果 - Young_Kai - 博客频道 - CSDN.NET |
RecyclerView | RecyclerView学习(三)—-高仿知乎的侧滑删除 - Young_Kai - 博客频道 - CSDN.NET |
RecyclerView | RecyclerView学习(四)—-城市导航列表的实现(上) - Young_Kai - 博客频道 - CSDN.NET |
RecyclerView | RecyclerView学习(五)—-SwipeRefreshLayout的下拉刷新与上拉加载 - Young_Kai - 博客频道 - CSDN.NET |
学习思路:
1、首先学习一项新知识,肯定是去官网最好,达到简单使用的目的
2、在了解如何使用之后,对各个子项进行分析,达到灵活使用的目的
3、对控件的设计和架构及实现原理进行分析,达到能对性能进行优化,并进行复杂设计
4、横向对比
5、各种demo
找用例,在泡网上面搜索RecyclerView和Github开源
最后
以上就是勤奋可乐为你收集整理的RecyclerView简介基本使用子项分析Item的点击事件添加分隔线ViewHolder封装Adapter封装多ItemViewTypeExpandRecyclerView添加Header下拉刷新和上拉加载侧滑删除animator源码解析的全部内容,希望文章能够帮你解决RecyclerView简介基本使用子项分析Item的点击事件添加分隔线ViewHolder封装Adapter封装多ItemViewTypeExpandRecyclerView添加Header下拉刷新和上拉加载侧滑删除animator源码解析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复