赞
踩
android:fadingEdge="none"
ScrollView.setHorizontalFadingEdgeEnabled(false);
2.3及以上
android:overScrollMode="never"
/** * https://www.jianshu.com/p/e372cec819db */ public class SpacesItemDecoration extends RecyclerView.ItemDecoration { private int space; public SpacesItemDecoration(int space) { this.space = space; } @Override public void getItemOffsets(Rect outRect, @NotNull View view, RecyclerView parent, @NotNull RecyclerView.State state) { outRect.left = space; outRect.right = space; outRect.bottom = space; if (parent.getChildLayoutPosition(view) == 0) { outRect.top = space; } } } //调用 recyclerView.addItemDecoration(new SpacesItemDecoration(27));
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(@NotNull RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); if (newState == RecyclerView.SCROLL_STATE_DRAGGING || newState == RecyclerView.SCROLL_STATE_SETTLING) { isScrolling = true; Glide.with(MainActivity.this).pauseRequests(); } else if (newState == RecyclerView.SCROLL_STATE_IDLE) { if (isScrolling) { Glide.with(MainActivity.this).resumeRequests(); } isScrolling = false; } } @Override public void onScrolled(@NotNull RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); } });
Glide.with(mContext)
.load(url)
.dontAnimate()
....
include,merge,ViewStub
等标签,使布局层次尽量少重写ItemAnimator并对RecyclerView进行设置
设置ItemAnimator的动画时间
SimpleItemAnimator simpleItemAnimator = (SimpleItemAnimator) recyclerView.getItemAnimator(); if (simpleItemAnimator != null) { // 默认动画时间 // private long mAddDuration = 120; // private long mRemoveDuration = 120; // private long mMoveDuration = 250; // private long mChangeDuration = 250; simpleItemAnimator.setAddDuration(0); simpleItemAnimator.setChangeDuration(0); simpleItemAnimator.setMoveDuration(0); simpleItemAnimator.setRemoveDuration(0); simpleItemAnimator.setSupportsChangeAnimations(false); } recyclerView.setItemAnimator(simpleItemAnimator);
https://blog.csdn.net/lixinxiaos/article/details/108831390
在 RecyclerView 中,获取第一条可见 item 的 position 有两种方法:
1. 使用 findFirstVisibleItemPosition()
方法
int firstVisiblePosition = recyclerView.findFirstVisibleItemPosition();
该方法返回 RecyclerView 中第一个可见的 item 的 position。需要注意的是,如果第一个 item 完全不可见,则该方法返回 -1。
2. 使用 findViewHolderForAdapterPosition()
方法
ViewHolder viewHolder = recyclerView.findViewHolderForAdapterPosition(0);
int firstVisiblePosition = viewHolder.getAdapterPosition();
该方法根据 adapter position 获取对应的 ViewHolder。如果该 position 对应的 item 可见,则返回的 ViewHolder 非空,否则返回 null。然后,可以通过 ViewHolder 的 getAdapterPosition()
方法获取该 item 的 position。
示例
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
int firstVisiblePosition = recyclerView.findFirstVisibleItemPosition();
// 处理第一个可见 item
}
}
});
注意事项
findFirstVisibleItemPosition()
方法返回的是第一个完全可见的 item 的 position。如果第一个 item 只有部分可见,则该方法返回 -1。findViewHolderForAdapterPosition()
方法返回的是第一个可见 item 的 adapter position。如果第一个 item 只有部分可见,则该方法返回的 ViewHolder 非空。RecyclerView的滚动方法回调有三个:
onScrollStateChanged(RecyclerView recyclerView, int newState)
onScrolled(RecyclerView recyclerView, int dx, int dy)
fling(int velocityX, int velocityY)
**onScrollStateChanged
**方法在滚动状态改变时回调,参数有:
recyclerView
:当前滚动的RecyclerView
newState
:新的滚动状态,取值如下:
RecyclerView.SCROLL_STATE_IDLE
:空闲状态RecyclerView.SCROLL_STATE_DRAGGING
:拖拽状态RecyclerView.SCROLL_STATE_SETTLING
:惯性滚动状态当用户停止滚动时,RecyclerView
会进入SCROLL_STATE_IDLE
状态。即使在惯性滚动过程中,RecyclerView
也会最终进入SCROLL_STATE_IDLE
状态。
**onScrolled
**方法在滚动时回调,参数有:
recyclerView
:当前滚动的RecyclerViewdx
:水平滚动距离dy
:垂直滚动距离**fling
**方法在惯性滚动开始时回调,参数有:
velocityX
:水平方向的初始速度velocityY
:垂直方向的初始速度以下是三个滚动方法回调的具体说明:
**onScrollStateChanged
**方法可以用来监听RecyclerView的滚动状态变化,例如在空闲状态时可以加载更多数据。
**onScrolled
**方法可以用来监听RecyclerView的滚动距离,例如在滚动到顶部或底部时可以显示提示信息。
**fling
**方法可以用来控制RecyclerView的惯性滚动,例如可以设置惯性滚动的最大速度或距离。
以下是一些使用滚动方法回调的示例:
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
// 加载更多数据
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (dy > 0) {
// 滚动向上
if (recyclerView.canScrollVertically(-1)) {
// 滚动到顶部
// 显示提示信息
}
}
}
@Override
public boolean fling(int velocityX, int velocityY) {
// 设置惯性滚动的最大速度
int maxVelocity = 1000;
if (velocityX > maxVelocity) {
velocityX = maxVelocity;
}
if (velocityY > maxVelocity) {
velocityY = maxVelocity;
}
return super.fling(velocityX, velocityY);
}
缓 存 | 涉及对象 | 作用 | 重新创建视图View(onCreateViewHolder) | 重新绑定数据(onBindViewHolder) |
---|---|---|---|---|
一级缓存 | mAttachedScrap | 缓存屏幕中可见范围的ViewHolder | false | false |
二级缓存 | mCachedViews | 缓存滑动时即将与RecyclerView分离的ViewHolder,按子View的position或id缓存,默认最多存放2个 | false | false |
三级缓存 | mViewCacheExtension 开发者自行实现的缓存 | - - | ||
四级缓存 | mRecyclerPool | ViewHolder缓存池,本质上是一个SparseArray,其中key是ViewType(int类型),value存放的是 ArrayList< ViewHolder>,默认每个ArrayList中最多存放5个ViewHolder | false | true |
@Override
public void onBindViewHolder(@NonNull ViewHolder coreViewHolder, int position) {
setTag(coreViewHolder, position);
bindView(coreViewHolder, getItem(position), position);
}
避免绘制方法内部进行耗时操作,绘制超过16ms,那么用户就会感觉到卡顿,所以要缩短方法执行时间,避免耗时方法的调用。
比如:
避免 日期的比较和日期的格式化 formatDate.format(date);
避免 mTextView.setText(Html.fromHtml(data).toString());
所以呢,最好在方法内部只赋值和写逻辑,数据的转换在之前处理。
主要思想是通过
mAdapter.notifyItemRangeInserted(position, count);
mAdapter.notifyItemRangeRemoved(position, count);
mAdapter.notifyItemMoved(fromPosition, toPosition);
mAdapter.notifyItemRangeChanged(position, count, payload);
代替 mAdapter.notifyDataSetChanged()
实现局部item刷新
DiffUtils:https://codechina.csdn.net/mirrors/mcxtzhang/DiffUtils?utm_source=csdn_github_accelerator
如果需要整列表刷新,需要使用 mAdapter.notifyDataSetChanged()
的情况,可以用setHasStableIds(true)
:
//setAdapter前
mAdapter.setHasStableIds(true);
//Adapter内部实现
@Override
public long getItemId(int position) {
return mDatas.get(position).getId();
}
这样也就可以通过 mAdapter.notifyDataSetChanged()
和setHasStableIds(true)
一起使用, 实现局部刷新,避免闪烁的现象。
同时也可以解决 DiffUtils{@link SortedListCallback}
的整列表刷新闪烁的问题。
Prefetch是Support Library版本 25.3.1时,RecyclerView添加的功能。滑动的时候触发。
实现:
LayoutManager.collectAdjacentPrefetchPositions()
setInitialPrefetchItemCount(num)
Prefetch默认是打开的,如果要关闭Prefetch:LayoutManager.setItemPrefetchEnabled(false);
调用方法:
recyclerView.setItemViewCacheSize();
原理:
recyclerView.setItemViewCacheSize();
修改的是mRequestedCacheMax
的值。因为
mViewCacheMax = mRequestedCacheMax + extraCache;
所以mViewCacheMax
被修改。
mViewCacheMax
控制的就是mCachedViews
的长度。(extraCache是由prefetch的时候计算出来的)
mCachedViews
在RecyclerView的缓存机制中负责缓存的是超出屏幕外的ViewHolder。主要用于解决RecyclerView滑动抖动时的情况。
至此,RecyclerView能更加滑动流畅。但是同时因为缓存量变大,内存肯定是占用变大。
所以呢,setItemViewCacheSize
这是空间换时间的手段。
原理:https://blog.csdn.net/tangedegushi/article/details/88790754
recyclerView.setHasFixedSize(true);
item高度是固定的话,来避免requestLayout重复计算浪费资源。
重写Adapter的方法:
onViewRecycled()
:
当 ViewHolder 已经确认被回收,且要放进 RecyclerViewPool 中前,该方法会被回调。
onViewDetachedFromWindow()
:当Item移除的时候调用
根据缓存原理,我们知道onViewRecycled()
方法在item被移除的时候不一定调用;但是onViewDetachedFromWindow()
方法对应的是removeView()
,肯定是调用的,所以回收ViewHodler,还是需要在onViewDetachedFromWindow()
中做。
介绍:
DEFAULT_MAX_SCRAP = 5
,并且缓存是按照itemType来分开存储的。而且这个缓存数量不是指整个缓存池只能缓存这么多,而是每个不同itemType的ViewHolder的缓存数量。//自定义
RecyclerView.RecycledViewPool recycledViewPool = new RecyclerView.RecycledViewPool();
recycledViewPool
.setMaxRecycledViews(VIEW_TYPE, POOL_MAX);
recyclerView.setRecycledViewPool(recycledViewPool);
这样的话,可以优化的方面有两个:
关于共有RecycledViewPool的写法:
转自共享 RecycledViewPool 复用 View 导致的内存泄露问题及其解决:https://blog.csdn.net/hegan2010/article/details/113770217
public abstract class ButtonItemAdapter extends RecyclerView.Adapter<BindingViewHolder<ButtonItemBinding>> { private static final RecyclerView.RecycledViewPool BUTTON_RECYCLER_POOL = new RecyclerView.RecycledViewPool(); protected static final int BUTTON_ITEM_TYPE = 1; static { BUTTON_RECYCLER_POOL.setMaxRecycledViews(BUTTON_ITEM_TYPE, 60); } public static RecyclerView.RecycledViewPool getButtonRecyclerPool() { return BUTTON_RECYCLER_POOL; } @Override public int getItemViewType(int position) { return BUTTON_ITEM_TYPE; } @NonNull @CallSuper @Override public BindingViewHolder<ButtonItemBinding> onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { ButtonItemBinding binding = DataBindingUtil.inflate( LayoutInflater.from(parent.getContext()), R.layout.button_item, parent, false); return new BindingViewHolder<>(binding); } @CallSuper @Override public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { recyclerView.setRecycledViewPool(getButtonRecyclerPool()); RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); if (layoutManager instanceof LinearLayoutManager) { ((LinearLayoutManager) layoutManager).setRecycleChildrenOnDetach(true); } } @CallSuper @Override public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) { recyclerView.setRecycledViewPool(null); } }
在合适的时机,RecycledViewPool会自我清除掉所持有的ViewHolder对象引用,当然你也可以在你认为合适的时机手动调用clear()。
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this) {
@Override
protected int getExtraLayoutSpace(RecyclerView.State state) {
return 300;
}
};
recyclerView.setLayoutManager(linearLayoutManager);
应用场景:屏幕第一次滑动卡顿
getExtraLayoutSpace()
会通过calculateExtraLayoutSpace()
影响可见区域,从而达到预加载ViewHolder的目的。
不清楚这个方法是不是可用,不推荐使用。原因是calculateExtraLayoutSpace()
方法标注:使用会带来显著的性能成本。原理不了解。
chatgpt
RecyclerView加载图片滑动卡顿:https://blog.csdn.net/qq_33240767/article/details/80783077
掉RecyclerView的默认item动画:https://blog.csdn.net/CSDN_LQR/article/details/54766560
RecyclerView性能优化:https://www.cnblogs.com/zgz345/p/13436005.html
RecyclerView Prefetch功能探究:https://blog.csdn.net/crazy_everyday_xrp/article/details/70344638
Android深入理解RecyclerView的缓存机制:https://blog.csdn.net/u013700502/article/details/105058771/
真正带你搞懂 RecyclerView 的缓存机制,再也不怕面试被虐了:https://zhuanlan.zhihu.com/p/80475040
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。