RecyclerView初步使用
开篇
放假回来,闲来无事,继续上次的讲解
这次对RecyclerView的讲解差不多可以满足大部分需求
对RecyclerView的简单介绍在上一篇 传送门
保证干货满满
翻了一下上次的文章,感觉篇幅过于长
这次尽量控制到5k字内
开始
添加头
在上篇结尾时,加了一张这个图
原理
这个游戏广告是怎么做到独占一行的
去到RecyclerView的源码中,ctrl+f搜索 notifyItem,往下翻可以看到几行注释
/** *
* @see #notifyItemChanged(int)
* @see #notifyItemInserted(int)
* @see #notifyItemRemoved(int)
* @see #notifyItemRangeChanged(int, int)
* @see #notifyItemRangeInserted(int, int)
* @see #notifyItemRangeRemoved(int, int)
* */
前三个方法分别是
修改item,新增item,删除item
后三个就是批量修改,批量新增,批量删除
方法里写的都是for循环,比如
public void notifyChanged() {
// since onChanged() is implemented by the app, it could do anything, including
// removing itself from {@link mObservers} - and that could cause problems if
// an iterator is used on the ArrayList {@link mObservers}.
// to avoid such problems, just march thru the list in the reverse order.
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
for循环里,get一个集合里index为i的对象,这个对象为适配器观察器
Goolean对这个观察者的解释为
/**
* Observer base class for watching changes to an {@link Adapter}.
* See {@link Adapter#registerAdapterDataObserver(AdapterDataObserver)}.
*/
观察者基类,用于观察Adapter的改变
对于观察者的其他解释,阅读菜鸟教程对此的解释观察者模式
在这里只需要知道这个方法的作用是,当这个mObservers改变时,所有依赖于它的对象都得到通知并被自动更新。
这也就解释了,为什么搜索RecyclerViews刷新时,最常见的是用
adapter.notifyDataSetChanged()方法
public final void notifyDataSetChanged() {
mObservable.notifyChanged();
}
public void notifyChanged() {
// since onChanged() is implemented by the app, it could do anything, including
// removing itself from {@link mObservers} - and that could cause problems if
// an iterator is used on the ArrayList {@link mObservers}.
// to avoid such problems, just march thru the list in the reverse order.
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
总结 : 可以更改mObservable来实现一系列增删刷改
实现步骤
关联外部
首先在自己的Adapter中加一个添加头的方法,供Activity调用
private View mHeaderView;
public void setHeaderView(View headerView) {
mHeaderView = headerView;
notifyItemInserted(0);
}
其次,给这个view设置一个标识
public static final int HEAD = 1;//头类型
public static final int ITEM = 0;//普通item类型
创建 onCreateViewHolder()
上期说过的onCreateViewHolder()
返回的Holder,这些都是item的布局
头部和item不一样,这时就要返回不一样的Holder
同时更改这个viewType的值
/**
* 创建布局
*/
@NonNull
@Override
public Holder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//如果添加了头并且类型是头,返回头
if (mHeaderView != null && viewType == HEAD) return new Holder(mHeaderView);
//创建ViewHolder,返回每一项的布局
butView = LayoutInflater.from(context).inflate(R.layout.recy_list2, parent, false);
RecyclerAdapter3.Holder butViewHolder = new RecyclerAdapter3.Holder(butView);
butView.setOnClickListener(this);
return butViewHolder;
}
创建 onBindViewHolder()
上期也说过onBindViewHolder()传来的两个参数分别是绑定时的Holder和下标position
既然加了头,那position就需要改变
如果添加了头,就要把position-1,否则就会出现普通item第一个的position就会变成1,而头的position就会变成0,对接下来的pos判断造成误差
至于为什么return,因为在上面的setHeaderView就要对头进行处理,挖坑!!
/**
* 绑定控件
*/
@Override
public void onBindViewHolder(@NonNull Holder holder, int position) {
int pos = getRealPosition(holder);
if (getItemViewType(position) == HEAD) return;
//将数据和控件绑定
....
}
public int getRealPosition(RecyclerView.ViewHolder holder) {
int position = holder.getLayoutPosition();
return mHeaderView == null ? position : position - 1;
}
创建 getItemCount()
既然对pos做了处理,那Count也不能落下
需要注意的是,pos是下标,是RecyclerView计算都得出的
Count是数量,是自己定义的
为了方便阅读,我这里不传入list,而是传入一个num,决定显示的item个数
/**
* 返回数量
*/
@Override
public int getItemCount() {
return mHeaderView == null ? num : num + 1;
}
绑定类型
创建方法,重写自RecyclerView
对于这个方法,官方给出的解释为(来自谷歌翻译)
返回该项目的视图类型,以用于视图回收。
该方法的默认实现返回0,并假设适配器的单一视图类型。
与ListView适配器不同,类型不需要是连续的。
考虑使用ID资源来唯一标识项目视图类型。
/**
* Return the view type of the item at <code>position</code> for the purposes
* of view recycling.
*
* <p>The default implementation of this method returns 0, making the assumption of
* a single view type for the adapter. Unlike ListView adapters, types need not
* be contiguous. Consider using id resources to uniquely identify item view types.
*
* @param position position to query
* @return integer value identifying the type of the view needed to represent the item at
* <code>position</code>. Type codes need not be contiguous.
*/
public int getItemViewType(int position) {
return 0;
}
大致意思就是默认0.可以通过这个方法拿到item的类型
最重要的是,它传来个position
/**
* 设置item类型
*/
@Override
public int getItemViewType(int position) {
//判断是否添加了头
if (mHeaderView == null) {
return ITEM;
}
//添加了头,在判断pos是不是0
//0是第一个item,返回HEAD
//如果要放其他位置,可以根据其他条件判断后返回
if (position == 0) {
return HEAD;
}
//添加了头,可pos不是0,那剩下的都是item
return ITEM;
}
细节处理
最后是内部类Holder,要在绑定控件时判断
/**
* 内部类
*/
public class Holder extends RecyclerView.ViewHolder {
ImageView head;
LinearLayout layout;
public Holder(View itemView) {
super(itemView);
//判断
if (itemView == mHeaderView) return;
head = itemView.findViewById(R.id.head);
layout = itemView.findViewById(R.id.layout);
}
}
使用
准备工作到此结束
在Activity中使用setHeader方法即可添加头
onCreate中
private RecyclerView recycler;
private RecyclerAdapter3 adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recycler = findViewById(R.id.recycler);
//传入context和需要显示的条数
adapter = new RecyclerAdapter3(this,4);
//表格布局
GridLayoutManager manager = new GridLayoutManager(this,2);
//设置布局
recycler.setLayoutManager(manager);
//设置动画
recycler.setItemAnimator(new DefaultItemAnimator());
//设置适配器
recycler.setAdapter(adapter);
//添加头
setHeader(recycler);
}
添加头单独写一个方法
使用上期说的LayoutInflater创建布局
顺带对布局的控件进行修改
(填坑)
这就是为什么挖坑处要return,这里可以独自处理头
又或者你不想独自处理,也可以和item一起处理
private void setHeader(RecyclerView view) {
//创建view,必要
View header = LayoutInflater.from(context).inflate(R.layout.review_one_inf, view, false);
//绑定控件,非必要
TextView text = header.findViewById(R.id.text);
ImageView img = header.findViewById(R.id.img);
//修改控件显示数据,非必要
text.setText(headName);
Picasso.get().load(headCover).into(img);
//头部点击事件,非必要
header.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//点击事件
}
});
//执行adapter的setHeaderView,传入处理好的头,必要
adapter.setHeaderView(header);
}
至于怎么把头部点击事件一起放入item点击事件
或将头和item融合在一起写,动手实验一下即可
实现图
结尾
这次详细写了一下简单的RecyclerView添加头方法
继续徜徉于码海中,要等发现新的有趣知识点再写了
如果有什么有趣的知识点,想我详细描述,欢迎留言或私信
最后
对本文有任何意见或疑问,或者认为其中说法有误,欢迎在评论区留言!!!
转载请注明出处!!