You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关闭应用后重新打开如何回到ListView的同一滚动位置

长ListView滚动位置记忆实现方案

核心实现逻辑分为两步:首先监听列表滚动位置变化,实时持久化存储位置信息;应用重启/页面再次打开时,读取存储的位置信息,等列表渲染完成后主动滚动到对应位置即可。

Android 原生实现

  • 持久化存储选用SharedPreferences即可满足需求
  • 滚动位置监听与保存代码:
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        // 滚动停止后再保存位置,避免频繁IO
        if (scrollState == SCROLL_STATE_IDLE) {
            int firstVisiblePos = listView.getFirstVisiblePosition();
            View firstVisibleView = listView.getChildAt(0);
            int topOffset = firstVisibleView == null ? 0 : firstVisibleView.getTop() - listView.getPaddingTop();
            SharedPreferences sp = getSharedPreferences("list_scroll_config", MODE_PRIVATE);
            sp.edit()
              .putInt("last_first_pos", firstVisiblePos)
              .putInt("last_top_offset", topOffset)
              .apply();
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {}
});
  • 位置恢复代码:
SharedPreferences sp = getSharedPreferences("list_scroll_config", MODE_PRIVATE);
int lastFirstPos = sp.getInt("last_first_pos", 0);
int lastOffset = sp.getInt("last_top_offset", 0);
// 等列表布局渲染完成后再执行滚动,避免失效
listView.post(() -> listView.setSelectionFromTop(lastFirstPos, lastOffset));

Flutter 实现

  • 选用shared_preferences库做持久化,配合ScrollController监听滚动偏移
  • 示例代码:
final ScrollController _listScrollController = ScrollController();
late SharedPreferences _sp;

@override
void initState() {
  super.initState();
  _initStorageAndRestorePosition();
  // 监听滚动偏移,实时存储
  _listScrollController.addListener(() {
    if (_listScrollController.position.hasPixels) {
      _sp.setDouble('list_scroll_offset', _listScrollController.offset);
    }
  });
}

void _initStorageAndRestorePosition() async {
  _sp = await SharedPreferences.getInstance();
  double lastScrollOffset = _sp.getDouble('list_scroll_offset') ?? 0;
  // 等待列表第一帧渲染完成后再滚动
  WidgetsBinding.instance.addPostFrameCallback((_) {
    if (_listScrollController.hasClients) {
      _listScrollController.jumpTo(lastScrollOffset);
    }
  });
}

@override
Widget build(BuildContext context) {
  return ListView.builder(
    controller: _listScrollController,
    itemCount: 1000,
    itemBuilder: (context, index) => ListTile(title: Text('列表项 $index')),
  );
}

iOS 原生实现

  • 选用UserDefaults存储位置信息,监听滚动停止事件
  • 示例代码:
// 滚动停止时保存位置
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    let scrollOffset = tableView.contentOffset.y
    UserDefaults.standard.set(scrollOffset, forKey: "list_last_scroll_offset")
}

// 页面加载完成后恢复位置
override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    let lastOffset = UserDefaults.standard.double(forKey: "list_last_scroll_offset")
    tableView.setContentOffset(CGPoint(x: 0, y: lastOffset), animated: false)
}

注意事项

  • 必须等列表数据加载完成、布局渲染完毕后再执行滚动操作,否则会出现定位失效的问题
  • 如果列表项高度为动态变化的场景,优先存储第一个可见项索引+该项顶部距离列表顶部的偏移量,不要只存全局滚动偏移,避免列表项高度变化后定位不准
  • 滚动监听可添加防抖逻辑,避免滚动过程中频繁触发持久化写入,影响性能

内容的提问来源于stack exchange,提问作者Hicham

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 05:15:03