Android TabLayout切换Fragment时如何保留WebView状态
问题原因
默认配合TabLayout使用的ViewPager/ViewPager2对Fragment的离屏保留阈值默认为1,即仅会缓存当前展示标签左右相邻各1个标签页的Fragment视图。当切换到间隔1个及以上的标签时,超出缓存范围的原标签Fragment会触发onDestroyView销毁视图层级,下次切回该标签时会重新执行onCreateView重建视图。
当前实现把WebView初始化、页面加载逻辑全部写在onCreateView生命周期中,视图重建时自然会触发WebView重新加载,丢失原有浏览状态。相邻标签切换不重载,刚好符合默认缓存阈值为1的表现。
具体实现步骤
- 调整ViewPager离屏缓存数量
在初始化ViewPager的代码中,将离屏缓存上限设置为「总标签数-1」,让所有标签页的Fragment视图常驻内存,不会因为切换被销毁,从根源上避免视图重建触发重载。
在Activity/Fragment中初始化适配器后添加如下代码:
如果使用ViewPager2,设置方法完全一致,同样调用// 总共有4个标签页,两侧各保留3个即可覆盖全部页面 viewPager.offscreenPageLimit = 3setOffscreenPageLimit(3)即可。 - 优化Fragment内WebView初始化逻辑
避免每次视图重建都重复执行WebView配置、url加载操作,将WebView实例设为Fragment的成员变量,仅在首次初始化时执行加载逻辑。同时绑定Fragment生命周期处理WebView的销毁,避免内存泄漏。
修正后的BrowseFragment代码如下:class BrowseFragment : Fragment() { private var webViewBrowse: WebView? = null private val targetUrl = "https://websitehome.page" override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val myView = inflater.inflate(R.layout.fragment_browse, container, false) if (webViewBrowse == null) { webViewBrowse = myView.findViewById(R.id.webViewBrowse) webViewBrowse?.apply { webViewClient = WebViewClient() settings.javaScriptEnabled = true loadUrl(targetUrl) } } else { // WebView已初始化时,从原有父布局移除后挂载到当前新创建的视图上 (webViewBrowse?.parent as? ViewGroup)?.removeView(webViewBrowse) myView.addView(webViewBrowse) } return myView } override fun onDestroyView() { super.onDestroyView() // 视图销毁时不回收WebView实例,仅解除父布局挂载 (webViewBrowse?.parent as? ViewGroup)?.removeView(webViewBrowse) } override fun onDestroy() { // Fragment彻底销毁时再回收WebView,避免内存泄漏 webViewBrowse?.destroy() webViewBrowse = null super.onDestroy() } } - 修正PagerAdapter初始化参数
原有FragmentPagerAdapter单参数构造方法已被废弃,传入正确的生命周期行为标记,避免Fragment生命周期调度异常:class SectionsPagerAdapter(private val context: Context, fm: FragmentManager) : FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { override fun getItem(position: Int): Fragment { return when (position) { 0 -> BrowseFragment() 1 -> SecondFragment() 2 -> ThirdFragment() 3 -> FourthFragment() else -> BrowseFragment() } } override fun getPageTitle(position: Int): CharSequence? { return context.resources.getString(TAB_TITLES[position]) } override fun getCount(): Int = 4 }
补充说明
如果后续标签页数量增加、不适合将所有页面都加入离屏缓存,可以额外实现WebView的状态保存与恢复逻辑:在onSaveInstanceState中调用webView.saveState(bundle)保存浏览状态(包含滚动位置、表单内容、页面历史栈),切回重建时调用webView.restoreState(bundle)恢复状态即可,不需要重新加载url。
内容的提问来源于stack exchange,提问作者Lorenzo
相关产品推荐
相关产品推荐

