場景:Activity 含 4 個 Fragment,每個 Fragment 內嵌一個 WebView。Activity 統一攔截返回鍵,調用當前 Fragment 的 `webView.goBack()`。現象:進入 A Fragment → 切換到 B Fragment(不操作) → 返回 A Fragment,此時 `webView.canGoBack()` 返回 `false`,直接觸發退出邏輯。
| 現象 | 原因 |
|---|---|
| B Fragment 的 WebView 從未加載過頁面/未發生導航 | `canGoBack()` 只有在歷史棧長度 > 1 時才返回 true。剛建立、未跳轉的 WebView 歷史棧只有初始頁,故為 false |
| 返回 A Fragment 時,邏輯錯誤地檢查了 B Fragment 的 WebView | 代碼可能在 `onBackPressed` 中遍歷所有 Fragment 的 WebView,或引用了錯誤的 Fragment 實例 |
| Fragment 重建導致 WebView 實例變化 | 若使用 `replace` + `addToBackStack`,返回時可能重新 `onCreateView` 建立新 WebView,歷史棧丟失 |
public class WebFragment extends Fragment {
private WebView webView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_web, container, false);
webView = view.findViewById(R.id.webView);
webView.setWebViewClient(new WebViewClient());
webView.loadUrl("https://example.com");
return view;
}
// 供 Activity 調用
public boolean handleBackPress() {
if (webView != null && webView.canGoBack()) {
webView.goBack();
return true; // 消費事件
}
return false; // 未消費,交給 Activity 處理(如退出應用)
}
}
public class MainActivity extends AppCompatActivity {
@Override
public void onBackPressed() {
Fragment current = getSupportFragmentManager().findFragmentById(R.id.container);
if (current instanceof WebFragment) {
if (((WebFragment) current).handleBackPress()) {
return; // Fragment 已處理
}
}
// 也可檢查 ViewPager 當前頁面
// Fragment current = adapter.getItem(viewPager.getCurrentItem());
super.onBackPressed(); // 默認退出/彈棧
}
}
// 切換 Fragment 時
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if (targetFragment.isAdded()) {
ft.hide(currentFragment).show(targetFragment);
} else {
ft.hide(currentFragment).add(R.id.container, targetFragment);
}
ft.commit();
// 這樣 WebView 實例不銷毀,歷史棧、滾動位置、JS 狀態全保留
// ❌ 錯誤:在 Activity 直接持有 4 個 WebView 引用,切換時引用錯亂
WebView wv1, wv2, wv3, wv4;
@Override public void onBackPressed() {
if (wv1.canGoBack()) { wv1.goBack(); return; } // 若當前是 Fragment2,wv1 可能不可見但仍持有舊實例
...
}
// ✅ 正確:Fragment 封裝 WebView 並暴露 handleBackPress()
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
webView.saveState(outState); // 保存歷史棧、滾動、表單數據
}
@Override
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
if (savedInstanceState != null) {
webView.restoreState(savedInstanceState);
}
}
核心原則:**WebView 返回邏輯封裝在 Fragment 內部,Activity 僅委派給當前 Fragment;使用 hide/show 保持實例狀態**。即可解決「切換後 canGoBack 失效」等疑難雜症。
