在 Fragment 中,開發者嘗試通過按鈕點擊動態替換當前視圖(如從設置頁面切換到編輯頁面)。Activity 中可用 setContentView() 直接替換整個視圖,但 Fragment 的視圖層級由 onCreateView() 返回的 View 決定,無法直接調用 setContentView()。
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
LinearLayout theLayout = (LinearLayout) inflater.inflate(R.layout.settings, container, false);
Button edit = theLayout.findViewById(R.id.btn_edit);
edit.setOnClickListener(v -> {
// 錯誤:嘗試直接重新 inflate 並賦值給局部變量,不會更新 Fragment 視圖
theLayout = (LinearLayout) inflater.inflate(R.layout.edit_settings, container, false);
});
return theLayout;
}
onCreateView() 返回後由系統管理,直接修改局部變量 theLayout 不會觸發 UI 更新。inflater.inflate() 僅創建新 View 層級,未將其掛載到容器中。setContentView(),其視圖由 onCreateView() 返回的根 View 決定。將「設置頁」和「編輯頁」拆分為兩個獨立 Fragment,使用 FragmentTransaction.replace() 切換:
edit.setOnClickListener(v -> {
Fragment editFragment = new EditSettingsFragment();
getParentFragmentManager().beginTransaction()
.replace(R.id.fragment_container, editFragment)
.addToBackStack(null)
.commit();
});
優點:符合 Fragment 設計初衷、生命週期獨立、支持返回棧、易於維護擴展。
在 Fragment 佈局中使用 ViewFlipper 包裹多個子視圖:
<ViewFlipper
android:id="@+id/viewFlipper"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/settings" />
<include layout="@layout/edit_settings" />
</ViewFlipper>
ViewFlipper flipper = view.findViewById(R.id.viewFlipper);
edit.setOnClickListener(v -> flipper.showNext()); // 或 flipper.setDisplayedChild(1);
優點:無需 Fragment 事務、切換流暢、適合簡單頁面切換。
ViewGroup container = (ViewGroup) getView().getParent(); // 獲取父容器
if (container != null) {
container.removeAllViews(); // 移除舊視圖
View newView = inflater.inflate(R.layout.edit_settings, container, false);
container.addView(newView); // 添加新視圖
}
缺點:破壞 Fragment 視圖層級契約、可能導致內存洩漏、生命週期異常、不支持返回鍵導航。
使用 ViewBinding + ViewStub 或可見性控制:
<FrameLayout ...>
<ViewStub
android:id="@+id/stub_settings"
android:layout="@layout/settings"
android:inflatedId="@+id/layout_settings" />
<ViewStub
android:id="@+id/stub_edit"
android:layout="@layout/edit_settings"
android:inflatedId="@+id/layout_edit" />
</FrameLayout>
ViewStub stubSettings = view.findViewById(R.id.stub_settings);
ViewStub stubEdit = view.findViewById(R.id.stub_edit);
boolean isEditMode = false;
edit.setOnClickListener(v -> {
isEditMode = !isEditMode;
if (isEditMode) {
stubSettings.setVisibility(View.GONE);
if (stubEdit.getParent() instanceof ViewStub) {
stubEdit.inflate(); // 首次 inflate
} else {
stubEdit.setVisibility(View.VISIBLE);
}
} else {
stubEdit.setVisibility(View.GONE);
stubSettings.setVisibility(View.VISIBLE);
}
});
優點:按需加載佈局、內存友好、單 Fragment 管理多狀態。
| 場景 | 推薦方案 | 理由 |
|---|---|---|
| 功能模塊獨立、邏輯複雜 | Fragment 替換 | 生命週期隔離、返回棧支持、解耦 |
| 簡單頁面切換、共享數據 | ViewFlipper / ViewStub | 輕量、無事務開銷、狀態保持 |
| 表單編輯/預覽模式切換 | 可見性控制 / ViewStub | 數據綁定同步、用戶體驗連貫 |
總結:Fragment 中切換視圖不應嘗試模仿 Activity 的 setContentView()。根據業務複雜度選擇 Fragment 事務替換、ViewFlipper 切換或 ViewStub 按需加載,才是符合 Android 架構規範的做法。