Android ListView加载初期滚动跳回顶部故障问题求助
解决ListView加载初期滚动跳回顶部的问题
嘿,我来帮你排查这个ListView滚动跳回顶部的问题!看了你的代码,几个关键问题导致了加载后前3秒的滚动异常,咱们一步步来修复:
核心问题分析
- ConvertView完全没复用:你在
getView里每次都重新inflate布局,这会让ListView滚动时疯狂创建新View,加上图片异步加载的开销,前3秒UI线程严重卡顿,直接导致滚动跳回顶部。 - ProgressBar频繁触发UI重绘:每一行的
getView都把进度条设为不可见,这会导致多次不必要的UI刷新,干扰滚动的流畅性。 - 选中状态更新时全量刷新列表:点击列表项时调用
notifyDataSetChanged(),会让整个ListView重新绘制,在加载初期雪上加霜。 - 图片加载无占位处理:Picasso加载图片时没有占位图,布局会因为图片加载完成而跳动,进一步影响滚动稳定性。
具体修复方案
1. 实现ViewHolder模式复用ConvertView
这是ListView优化的基础,能大幅减少View创建次数,提升性能:
@NonNull @Override public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { ViewHolder holder; // 只有当convertView为空时才初始化布局 if (convertView == null) { convertView = inflater.inflate(resource, parent, false); holder = new ViewHolder(); holder.countryName = convertView.findViewById(R.id.countryNamelbl); holder.countryFlag = convertView.findViewById(R.id.countryFlagimg); holder.checked = convertView.findViewById(R.id.countryCheckedimg); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } final CountryModel countryModel = countryModels.get(position); assert countryModel != null; holder.countryName.setText(countryModel.getName()); // 优化Picasso加载,添加占位图 Picasso.get() .load(countryModel.getImage()) .placeholder(R.drawable.flag_placeholder) // 替换成你的占位图资源 .fit() .into(holder.countryFlag); // 更新选中状态 if(!useFilter) { holder.checked.setVisibility(selectedCountryId == countryModel.getId() ? View.VISIBLE : View.INVISIBLE); } // 点击事件逻辑 convertView.setOnClickListener(v -> { if(!useFilter) { if (holder.checked.getVisibility() == View.VISIBLE) { holder.checked.setVisibility(View.INVISIBLE); selectedCountryId = -1; } else { // 先隐藏之前选中项的选中状态 if (selectedCountryId != -1) { int prevPosition = getPositionById(selectedCountryId); if (prevPosition != -1) { View prevView = parent.getChildAt(prevPosition - parent.getFirstVisiblePosition()); if (prevView != null) { ViewHolder prevHolder = (ViewHolder) prevView.getTag(); prevHolder.checked.setVisibility(View.INVISIBLE); } } } holder.checked.setVisibility(View.VISIBLE); selectedCountryId = countryModel.getId(); } selectedCountryIdtxt.get().setText(String.valueOf(selectedCountryId)); } else { Intent i = new Intent(getContext(), PicturesActivity.class); i.putExtra("countryId",countryModel.getId()); i.putExtra("countryName", countryModel.getName()); getContext().startActivity(i); } }); return convertView; } // ViewHolder内部类 private static class ViewHolder { TextView countryName; ImageView countryFlag; ImageView checked; } // 辅助方法:通过id获取列表位置 private int getPositionById(int countryId) { for (int i = 0; i < countryModels.size(); i++) { if (countryModels.get(i).getId() == countryId) { return i; } } return -1; }
2. 调整ProgressBar的显示逻辑
不要在getView里每一行都设置进度条,应该在数据源加载完成后统一设置一次,比如在你初始化适配器或者更新数据源的地方:
// 比如在数据加载完成后调用 progressBarWeakReference.get().setVisibility(View.INVISIBLE);
3. 移除不必要的全量刷新
之前点击时调用notifyDataSetChanged()会刷新整个列表,现在通过直接更新前后选中项的View来替代,避免全量刷新带来的性能开销。
额外优化建议
- 确保你的
CountryModel的hashCode()实现稳定,避免getItemId()返回值频繁变化。 - 如果列表数据量很大,考虑使用
RecyclerView替代ListView,它的性能优化更完善。 - 可以给Picasso设置内存缓存和磁盘缓存的大小,提升图片加载速度:
Picasso picasso = new Picasso.Builder(getContext()) .memoryCache(new LruCache(24 * 1024 * 1024)) // 24MB内存缓存 .build(); Picasso.setSingletonInstance(picasso);
内容的提问来源于stack exchange,提问作者Benjamin Gatt
相关产品推荐
相关产品推荐

