RecyclerView优化:首图加载完成后逐张加载至填满屏幕的实现方案
解决RecyclerView渐进式图片加载问题
这问题我做图片相册类功能时也踩过坑,一次性批量加载12张图不仅拖慢初始加载速度,还容易让用户在等待时失去耐心。咱们来调整成先加载首张图,依次填充至屏幕满,滚动到空白区域再按需加载的模式,分这几步来改:
1. 初始加载:计算屏幕容纳量,替代固定12张加载
别一上来就硬加载12张,先算出当前屏幕能放下多少张图片,只加载这个数量,剩下的等首张加载完成后再逐步补充。
实现屏幕可容纳Item数量计算
private int CalculateInitialLoadCount() { // 获取RecyclerView的可见高度 int recyclerViewHeight = mRecyclerView.Height; // 这里替换成你的图片Item实际高度(可提前测量或从Adapter中获取) int itemHeight = 200; // 计算能容纳的行数,再乘以每行数量得到初始加载数 int rows = (int)Math.Ceiling((double)recyclerViewHeight / itemHeight); int initialCount = rows * picturesinarow; // 兜底至少加载1张,防止计算异常 return Math.Max(initialCount, 1); }
修改初始化代码中的加载逻辑
private void InitRecView() { mRecyclerView = FindViewById<RecyclerView>(Resource.Id.recyclerView); // 替换固定12张为计算出的初始加载量 int initialLoadCount = CalculateInitialLoadCount(); loadPhotos.LoadMoreItems(initialLoadCount); mLayoutManager = new GridLayoutManager (this, this.picturesinarow, GridLayoutManager.Vertical, false); mRecyclerView.SetLayoutManager(mLayoutManager); mAdapter = new PhotoAlbumAdapter (loadPhotos, this.picturesinarow, (RecyclerLayout)this.recyclerLayout, this.hasHeader, this.thumbnails, this); mRecyclerView.SetAdapter(mAdapter); mLayoutManager.SetSpanSizeLookup(new GridViewSpansizeLookup(mAdapter, mLayoutManager)); // 滚动监听逻辑后面再修改,先保留原有结构 }
2. 监听图片加载完成,依次填充屏幕
要实现“首张加载完再加载下一张”,需要在Adapter里给图片加载添加完成回调,用Glide/Picasso这类库都能实现:
改造Adapter的图片加载逻辑
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position) { PhotoViewHolder photoHolder = holder as PhotoViewHolder; PictureItem currentItem = loadPhotos.pictures[position]; // 以Glide为例,添加加载完成监听 Glide.With(context) .Load(currentItem.ThumbnailUrl) .Placeholder(Resource.Drawable.default_placeholder) // 占位图避免空白 .Into(photoHolder.ImageView) .AddListener(new RequestListener<Drawable>() { public bool OnLoadFailed(Exception e, object model, Target<Drawable> target, bool isFirstResource) { // 加载失败也继续加载下一张 LoadNextItemIfNeeded(position); return false; } public bool OnResourceReady(Drawable resource, object model, Target<Drawable> target, DataSource dataSource, bool isFirstResource) { // 当前图片加载完成,检查是否需要补全屏幕 LoadNextItemIfNeeded(position); return false; } }); } // 判断是否需要加载下一张来填满屏幕 private void LoadNextItemIfNeeded(int currentLoadedPos) { int lastVisiblePos = mLayoutManager.FindLastVisibleItemPosition(); // 如果当前加载的位置接近最后可见项,且还有未加载的图片,就加载1张 if (currentLoadedPos >= lastVisiblePos - 1 && loadPhotos.HasMoreItems()) { int oldCount = loadPhotos.pictures.Count; loadPhotos.LoadMoreItems(1); mAdapter.NotifyItemInserted(oldCount); } }
这里的HasMoreItems()需要你在loadPhotos类中实现,用来判断是否还有未加载的图片资源。
3. 滚动监听:仅在空白区域触发少量加载
把原来一次性加载12张的逻辑,改成只加载能填满剩余空白的少量图片(比如3-6张):
修改滚动监听代码
var onScrollListener = new XamarinRecyclerViewOnScrollListener(mLayoutManager); onScrollListener.LoadMoreEvent += (object sender, EventArgs e) => { if (!loadPhotos.HasMoreItems()) return; // 没有更多资源直接返回 // 每次最多加载6张,避免批量加载压力 int loadCount = Math.Min(6, loadPhotos.GetRemainingItemCount()); if (loadCount <= 0) return; int oldCount = loadPhotos.pictures.Count; loadPhotos.LoadMoreItems(loadCount); if (!mRecyclerView.IsComputingLayout) mAdapter.NotifyItemRangeInserted(oldCount, loadCount); }; mRecyclerView.AddOnScrollListener(onScrollListener);
GetRemainingItemCount()需要你实现,用来返回还剩多少未加载的图片数量。
4. 额外优化:开启图片缓存提升体验
给图片加载库开启内存和磁盘缓存,避免重复加载已显示过的图片:
Glide.With(context) .Load(currentItem.ThumbnailUrl) .Placeholder(Resource.Drawable.default_placeholder) .DiskCacheStrategy(DiskCacheStrategy.All) // 开启磁盘缓存 .Into(photoHolder.ImageView);
这样调整后,初始加载只会填充屏幕所需的最少图片,每张加载完成后自动补全剩余屏幕空间,滚动到空白区域时再少量加载,用户体验会顺畅很多。
内容的提问来源于stack exchange,提问作者innomotion media
相关产品推荐
相关产品推荐

