如何在Fragment恢复时让ViewPager2从停止位置续启自动滚动
解决方案
针对ViewPager2在Fragment不可见时停止自动滚动、恢复时继续滚动的问题,我们可以通过绑定Fragment生命周期+封装滚动控制逻辑来解决,以下是具体实现:
修改后的完整代码
public class ChannelPagerViewHolder extends RecyclerView.ViewHolder { private static final int LIMIT = 7; private static final @ViewPager2.OffscreenPageLimit int OFFSCREEN_PAGE_LIMIT = 2; private static final long AUTO_SCROLL_INTERVAL = 1000; // 自动滚动间隔(毫秒) private Context mContext; private LifecycleOwner mLifecycleOwner; private ViewPager2 viewPager; private LiveData<List<ChannelItem>> mChannelItemList; private BannerAdapter bannerAdapter; private Handler mHandler; private int currentPage; private int prevPageNumber; private Timer autoScrollTimer; private TimerTask autoScrollTask; public ChannelPagerViewHolder(View view, Context context, LifecycleOwner lifecycleOwner, int placement) { super(view); Log.d("ChannelPagerViewHolder", ""); mContext = context; mLifecycleOwner = lifecycleOwner; viewPager = view.findViewById(R.id.view_pager); viewPager.setOffscreenPageLimit(OFFSCREEN_PAGE_LIMIT); viewPager.getLayoutParams().width = (int) (Resources.getSystem().getDisplayMetrics().widthPixels); viewPager.getLayoutParams().height = viewPager.getLayoutParams().width / 2; bannerAdapter = new BannerAdapter(mContext, placement); viewPager.setAdapter(bannerAdapter); viewPager.setClipToPadding(false); viewPager.setPadding(24, 0, 24, 0); // 显示左右两侧部分banner的hack RecyclerView recyclerView = ((RecyclerView) viewPager.getChildAt(0)); recyclerView.setClipToPadding(false); UserEventLogger.getInstance(context).addImpressionLogger( recyclerView, placement ); mHandler = new Handler(Looper.getMainLooper()); // 指定主线程Looper,避免异常 currentPage = 0; viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { @Override public void onPageSelected(int position) { super.onPageSelected(position); currentPage = position; // 页面选中时才更新当前页,确保准确性 } }); // 绑定Fragment生命周期,控制滚动启停 mLifecycleOwner.getLifecycle().addObserver(new LifecycleEventObserver() { @Override public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { switch (event) { case ON_RESUME: startAutoScroll(); break; case ON_PAUSE: stopAutoScroll(); break; case ON_DESTROY: stopAutoScroll(); mHandler.removeCallbacksAndMessages(null); // 清理Handler任务,避免内存泄漏 break; } } }); } /** * 启动自动滚动 */ private void startAutoScroll() { // 先停止已有任务,避免重复启动导致滚动加速 stopAutoScroll(); autoScrollTimer = new Timer(); autoScrollTask = new TimerTask() { @Override public void run() { mHandler.post(() -> { // 计算下一页,实现循环滚动 int nextPage = currentPage + 1; if (nextPage >= bannerAdapter.getItemCount()) { nextPage = 0; } viewPager.setCurrentItem(nextPage, true); // true表示带动画滚动 }); } }; // 延迟1秒启动,之后每隔1秒滚动一次 autoScrollTimer.schedule(autoScrollTask, AUTO_SCROLL_INTERVAL, AUTO_SCROLL_INTERVAL); } /** * 停止自动滚动 */ private void stopAutoScroll() { if (autoScrollTask != null) { autoScrollTask.cancel(); autoScrollTask = null; } if (autoScrollTimer != null) { autoScrollTimer.cancel(); autoScrollTimer.purge(); // 清除已取消的任务 autoScrollTimer = null; } } }
关键修改说明
封装滚动控制逻辑
- 把自动滚动的启动/停止抽成
startAutoScroll()和stopAutoScroll()方法,统一管理Timer和TimerTask,避免重复创建任务导致滚动加速。
- 把自动滚动的启动/停止抽成
绑定Fragment生命周期
- 通过
mLifecycleOwner监听Fragment的生命周期事件:ON_RESUME:Fragment恢复可见时,从当前页面启动自动滚动。ON_PAUSE:Fragment不可见时,立即停止滚动。ON_DESTROY:彻底清理Timer和Handler任务,避免内存泄漏。
- 通过
修正当前页记录
- 将
currentPage的更新从onPageScrolled移到onPageSelected,因为onPageScrolled会在滚动过程中多次触发,而onPageSelected仅在页面选中时触发,能准确记录当前显示的页码。
- 将
循环滚动处理
- 滚动时判断下一页是否超出适配器条目数,超出则回到第0页,解决原代码越界导致的空白问题。
内容的提问来源于stack exchange,提问作者Paras
相关产品推荐
相关产品推荐

