转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/111240529
本文出自【赵彦军的博客】
目录
相关文章
https://blog.csdn.net/smileiam/article/details/88396546
https://zhuanlan.zhihu.com/p/49338922
https://www.jianshu.com/p/29352def27e6
SetHasStableIds(true) + getItemId 探究
https://www.jianshu.com/p/66d0feab2b5b
Android AsyncListDiffer-RecyclerView最好的伙伴
recyclerView.setHasFixedSize(true);
当Item的高度如是固定的,设置这个属性为true可以提高性能,尤其是当RecyclerView有条目插入、删除时性能提升更明显。
RecyclerView
在条目数量改变,会重新测量、布局各个item,如果设置了 setHasFixedSize(true)
,由于item的宽高都是固定的,adapter
的内容改变时,RecyclerView
不会整个布局都重绘。具体可用以下伪代码表示:
void onItemsInsertedOrRemoved() {
if (hasFixedSize) layoutChildren();
else requestLayout();
LinearLayoutManager.setInitialItemPrefetchCount()
RecyclerView 嵌套 RecyclerView 实现横向滑动,设置 LinearLayoutManager.setInitialItemPrefetchCount()
设置 横向 滚动布局预加载个数,避免UI 卡顿
- 只有 LinearLayoutManager 有这个api,其他 LayoutManager 没有
- 只有 RecyclerView 嵌套 RecyclerView 才有效
局部刷新
notifyItemChanged(int position)
notifyItemInserted(int position)
notifyItemRemoved(int position)
notifyItemMoved(int fromPosition, int toPosition)
notifyItemRangeChanged(int positionStart, int itemCount)
notifyItemRangeInserted(int positionStart, int itemCount)
notifyItemRangeRemoved(int positionStart, int itemCount)
DiffUtil
https://blog.csdn.net/zxt0601/article/details/52562770
DiffUtil.Callback 是一个抽象类
public abstract static class Callback {
public abstract int getOldListSize();//老数据集size
public abstract int getNewListSize();//新数据集size
public abstract boolean areItemsTheSame(int oldItemPosition, int newItemPosition);//新老数据集在同一个postion的Item是否是一个对象?(可能内容不同,如果这里返回true,会调用下面的方法)
public abstract boolean areContentsTheSame(int oldItemPosition, int newItemPosition);//这个方法仅仅是上面方法返回ture才会调用,我的理解是只有notifyItemRangeChanged()才会调用,判断item的内容是否有变化
//该方法在DiffUtil高级用法中用到 ,暂且不提
@Nullable
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
return null;
}
}
1、继承自DiffUtil.Callback的类,实现它的四个abstract方法
/**
* 介绍:核心类 用来判断 新旧Item是否相等
* 作者:zhangxutong
* 邮箱:zhangxutong@imcoming.com
* 时间: 2016/9/12.
*/
public class DiffCallBack extends DiffUtil.Callback {
private List<TestBean> mOldDatas, mNewDatas;//看名字
public DiffCallBack(List<TestBean> mOldDatas, List<TestBean> mNewDatas) {
this.mOldDatas = mOldDatas;
this.mNewDatas = mNewDatas;
}
//老数据集size
@Override
public int getOldListSize() {
return mOldDatas != null ? mOldDatas.size() : 0;
}
//新数据集size
@Override
public int getNewListSize() {
return mNewDatas != null ? mNewDatas.size() : 0;
}
/**
* Called by the DiffUtil to decide whether two object represent the same Item.
* 被DiffUtil调用,用来判断 两个对象是否是相同的Item。
* For example, if your items have unique ids, this method should check their id equality.
* 例如,如果你的Item有唯一的id字段,这个方法就 判断id是否相等。
* 本例判断name字段是否一致
*
* @param oldItemPosition The position of the item in the old list
* @param newItemPosition The position of the item in the new list
* @return True if the two items represent the same object or false if they are different.
*/
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return mOldDatas.get(oldItemPosition).getName().equals(mNewDatas.get(newItemPosition).getName());
}
/**
* Called by the DiffUtil when it wants to check whether two items have the same data.
* 被DiffUtil调用,用来检查 两个item是否含有相同的数据
* DiffUtil uses this information to detect if the contents of an item has changed.
* DiffUtil用返回的信息(true false)来检测当前item的内容是否发生了变化
* DiffUtil uses this method to check equality instead of {@link Object#equals(Object)}
* DiffUtil 用这个方法替代equals方法去检查是否相等。
* so that you can change its behavior depending on your UI.
* 所以你可以根据你的UI去改变它的返回值
* For example, if you are using DiffUtil with a
* {@link android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter}, you should
* return whether the items' visual representations are the same.
* 例如,如果你用RecyclerView.Adapter 配合DiffUtil使用,你需要返回Item的视觉表现是否相同。
* This method is called only if {@link #areItemsTheSame(int, int)} returns
* {@code true} for these items.
* 这个方法仅仅在areItemsTheSame()返回true时,才调用。
* @param oldItemPosition The position of the item in the old list
* @param newItemPosition The position of the item in the new list which replaces the
* oldItem
* @return True if the contents of the items are the same or false if they are different.
*/
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
TestBean beanOld = mOldDatas.get(oldItemPosition);
TestBean beanNew = mNewDatas.get(newItemPosition);
if (!beanOld.getDesc().equals(beanNew.getDesc())) {
return false;//如果有内容不同,就返回false
}
if (beanOld.getPic() != beanNew.getPic()) {
return false;//如果有内容不同,就返回false
}
return true; //默认两个data内容是相同的
}
梳理流程图如下
2、使用方法
注释掉你以前写的notifyDatasetChanged()方法吧
在将newDatas 设置给Adapter之前,先调用DiffUtil.calculateDiff()方法,计算出新老数据集转化的最小更新集,就是DiffUtil.DiffResult对象。
//第一个参数是DiffUtil.Callback对象,
//第二个参数代表是否检测Item的移动,改为false算法效率更高,按需设置,我们这里是true。
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffCallBack(mDatas, newDatas), true);
3、DiffUtil.DiffResult
对象的 dispatchUpdatesTo()
方法,传入RecyclerView的Adapter,替代原来的mAdapter.notifyDataSetChanged()
方法。
diffResult.dispatchUpdatesTo(mAdapter);
AsyncListDiff
public class UserAdapter extends RecyclerView.Adapter<UserAdapter.UserViewHodler> {
private AsyncListDiffer<User> mDiffer;
private DiffUtil.ItemCallback<User> diffCallback = new DiffUtil.ItemCallback<User>() {
@Override
public boolean areItemsTheSame(User oldItem, User newItem) {
return TextUtils.equals(oldItem.getId(), newItem.getId());
}
@Override
public boolean areContentsTheSame(User oldItem, User newItem) {
return oldItem.getAge() == newItem.getAge();
}
};
public UserAdapter() {
mDiffer = new AsyncListDiffer<>(this, diffCallback);
}
@Override
public int getItemCount() {
return mDiffer.getCurrentList().size();
}
public void submitList(List<User> data) {
mDiffer.submitList(data);
}
public User getItem(int position) {
return mDiffer.getCurrentList().get(position);
}
@NonNull
@Override
public UserAdapter.UserViewHodler onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_user_list, parent, false);
return new UserViewHodler(itemView);
}
@Override
public void onBindViewHolder(@NonNull UserAdapter.UserViewHodler holder, int position) {
holder.setData(getItem(position));
}
class UserViewHodler extends RecyclerView.ViewHolder {
//此处省略一段
public UserViewHodler(View itemView) {
super(itemView);
}
public void setData(User data) {
tvName.setText(data.getName());
tvAge.setText(String.valueOf(data.getAge()));
}
}
}
使用方法:
List<User> users = new ArrayList<>();
for (int i = 0; i < 10; i++) {
users.add(new User(String.valueOf(i), "用户" + i, i + 20));
}
mAdapter.submitList(users);
getExtraLayoutSpace
在RecyclerView的元素比较高,一屏只能显示一个元素的时候,第一次滑动到第二个元素会卡顿。
RecyclerView (以及其他基于adapter的view,比如ListView、GridView等)使用了缓存机制重用子 view(即系统只将屏幕可见范围之内的元素保存在内存中,在滚动的时候不断的重用这些内存中已经存在的view,而不是新建view)。
这个机制会导致一个问题,启动应用之后,在屏幕可见范围内,如果只有一张卡片可见,当滚动的时 候,RecyclerView找不到可以重用的view了,它将创建一个新的,因此在滑动到第二个feed的时候就会有一定的延时,但是第二个feed之 后的滚动是流畅的,因为这个时候RecyclerView已经有能重用的view了。
如何解决这个问题呢,其实只需重写getExtraLayoutSpace()方法。根据官方文档的描述 getExtraLayoutSpace将返回LayoutManager应该预留的额外空间(显示范围之外,应该额外缓存的空间)。
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this) {
@Override
protected int getExtraLayoutSpace(RecyclerView.State state) {
return 300;
}
};
避免创建过多 OnClickListener 对象
onCreateViewHolder 和 onBindViewHolder 对时间都比较敏感,尽量避免繁琐的操作和循环创建对象。例如创建 OnClickListener,可以全局创建一个。
同时onBindViewHolder调用次数会多于onCreateViewHolder的次数,如从RecyclerViewPool缓存池中取到的View都需要重新bindView,所以我们可以把监听放到CreateView中进行。
优化前:
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//do something
}
});
}
优化后:
private class XXXHolder extends RecyclerView.ViewHolder {
private EditText mEt;
EditHolder(View itemView) {
super(itemView);
mEt = (EditText) itemView;
mEt.setOnClickListener(mOnClickListener);
}
}
private View.OnClickListener mOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
//do something
}
}
复用RecycledViewPool
在TabLayout+ViewPager+RecyclerView的场景中,当多个RecyclerView有相同的item布局结构时,多个RecyclerView共用一个RecycledViewPool可以避免创建ViewHolder的开销,避免GC。RecycledViewPool对象可通过RecyclerView对象获取,也可以自己实现。
RecycledViewPool mPool = mRecyclerView1.getRecycledViewPool();
//下一个RecyclerView可直接进行setRecycledViewPool
mRecyclerView2.setRecycledViewPool(mPool);
mRecyclerView3.setRecycledViewPool(mPool);
减少过度绘制
减少布局层级