首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Android 開發:處理配置變更時手動切換橫豎屏佈局的完整方案

     編輯:程式設計綜合問答

背景問題

開發者在 Manifest 中設置 android:configChanges="orientation|keyboard|keyboardHidden" 阻止 Activity 重建,但導致系統自動切換 layout-land/layout-port 佈局失效。需手動在 onConfigurationChanged 中加載對應佈局。

完整實現方案

1. AndroidManifest.xml 配置

<activity 
    android:name=".MyActivity"
    android:configChanges="orientation|screenSize|keyboard|keyboardHidden" 
    >
</activity>

關鍵:Android 3.2+ (API 13+) 必須包含 screenSize,否則屏幕尺寸變化仍會觸發重建。

2. 佈局文件準備

  • res/layout/activity_main.xml - 豎屏佈局
  • res/layout-land/activity_main.xml - 橫屏佈局(或命名為 activity_main_landscape.xml 放在 layout 目錄)

3. Activity 核心代碼

public class MyActivity extends AppCompatActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 初始佈局加載(根據當前方向)
        setContentViewBasedOnOrientation();
        initViews(); // 初始化視圖引用、設置監聽器
    }
    
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        
        // 保存當前狀態(如滾動位置、輸入框焦點、數據狀態)
        Bundle savedState = saveUIState();
        
        // 切換佈局
        setContentViewBasedOnOrientation();
        
        // 重新初始化視圖(findViewById 等)
        initViews();
        
        // 恢復狀態
        restoreUIState(savedState);
    }
    
    private void setContentViewBasedOnOrientation() {
        int orientation = getResources().getConfiguration().orientation;
        if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
            setContentView(R.layout.activity_main_landscape); // 或 layout-land 下的同名文件
        } else {
            setContentView(R.layout.activity_main);
        }
    }
    
    private void initViews() {
        // 重新 findViewById 所有視圖
        // 重新設置 OnClickListener、Adapter 等
        // 示例:
        // textView = findViewById(R.id.text_view);
        // recyclerView = findViewById(R.id.recycler_view);
        // button.setOnClickListener(this);
    }
    
    private Bundle saveUIState() {
        Bundle bundle = new Bundle();
        // 保存滾動位置
        // bundle.putInt("scroll_y", scrollView.getScrollY());
        // 保存編輯框文本
        // bundle.putString("input_text", editText.getText().toString());
        // 保存選中項
        // bundle.putInt("selected_pos", listView.getCheckedItemPosition());
        return bundle;
    }
    
    private void restoreUIState(Bundle state) {
        if (state == null) return;
        // 恢復滾動位置
        // scrollView.post(() -> scrollView.scrollTo(0, state.getInt("scroll_y")));
        // 恢復編輯框
        // editText.setText(state.getString("input_text"));
    }
}

關鍵注意事項

問題解決方案
findViewById 返回 nullsetContentView 後必須重新 findViewById,舊引用失效
監聽器失效重新設置 OnClickListener、OnTouchListener、Adapter 等
Fragment 狀態丟失使用 FragmentManager 保存/恢復,或 ViewModel 存儲數據
輸入法狀態異常手動控制 InputMethodManager 顯示/隱藏
圖片加載重複使用 Glide/Picasso 等庫自動緩存,或手動管理 ImageView 引用

現代替代方案:ViewModel + DataBinding

使用 ViewModel 存儲 UI 狀態數據,DataBinding 自動綁定佈局,配置變更時僅需重新綁定,大幅減少樣板代碼:

// ViewModel 持有數據,不隨 Activity 重建銷毀
public class MyViewModel extends ViewModel {
    public MutableLiveData inputText = new MutableLiveData<>();
    public MutableLiveData scrollPosition = new MutableLiveData<>();
}

// Activity 中
MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
binding.setViewModel(viewModel);
binding.setLifecycleOwner(this);
// 配置變更後重新 setContentView 並綁定即可,數據自動保留

Manifest 權限說明

原答案提到的 <uses-permission android:name="android.permission.CHANGE_CONFIGURATION"> 不需要,此權限用於系統級應用修改全局配置,普通應用處理自身配置變更無需聲明。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved