Android開發(fā)教程之屏幕變更事件
一、什么是屏幕變更事件?
當設備的配置(Configuration) 發(fā)生變化時,Android 系統(tǒng)會觸發(fā) Configuration Change 事件。
常見的配置變更類型
| 變更類型 | 示例 |
|---|---|
orientation | 屏幕旋轉(豎屏 ↔ 橫屏) |
screenLayout | 屏幕尺寸/密度變化(如折疊屏展開) |
keyboardHidden | 軟鍵盤彈出/隱藏 |
fontScale | 系統(tǒng)字體大小調整 |
locale | 系統(tǒng)語言切換 |
uiMode | 夜間模式開啟/關閉 |
?? 默認行為:系統(tǒng)會銷毀并重建 Activity(調用
onDestroy()→onCreate()),以便加載適配新配置的資源。
二、默認行為:Activity 重建
生命周期流程
Activity A (豎屏)
↓ 用戶旋轉屏幕
onPause() → onStop() → onDestroy()
↓ 系統(tǒng)重建
onCreate() → onStart() → onResume()
→ Activity A (橫屏)問題與挑戰(zhàn)
- 狀態(tài)丟失:
onCreate()中初始化的數(shù)據(jù)可能丟失。 - 性能損耗:重復執(zhí)行
setContentView()、數(shù)據(jù)庫查詢、網(wǎng)絡請求。 - 用戶體驗差:頁面閃退或重新加載。
三、方案一:允許重建 + 正確保存狀態(tài)
如果選擇接受默認重建行為,必須確保關鍵數(shù)據(jù)不丟失。
1. 使用 onSaveInstanceState() 保存臨時狀態(tài)
public class MainActivity extends AppCompatActivity {
private String userInput;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EditText editText = findViewById(R.id.edit_text);
// 恢復保存的狀態(tài)
if (savedInstanceState != null) {
userInput = savedInstanceState.getString("USER_INPUT");
editText.setText(userInput);
}
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
// 保存用戶輸入
EditText editText = findViewById(R.id.edit_text);
outState.putString("USER_INPUT", editText.getText().toString());
}
}? 適用場景:輕量級狀態(tài)(如文本框內容、滾動位置)。
? 不適用:大數(shù)據(jù)、文件句柄、網(wǎng)絡連接。
2. 使用 ViewModel 保留復雜數(shù)據(jù)
public class MainViewModel extends ViewModel {
private MutableLiveData<List<String>> dataList = new MutableLiveData<>();
public LiveData<List<String>> getDataList() {
return dataList;
}
public void loadData() {
// 模擬耗時加載
new Handler().postDelayed(() -> {
List<String> data = Arrays.asList("Item 1", "Item 2", "Item 3");
dataList.setValue(data);
}, 2000);
}
}// 在 Activity 中使用
viewModel = new ViewModelProvider(this).get(MainViewModel.class);
viewModel.getDataList().observe(this, list -> {
// 更新 UI,即使 Activity 重建,數(shù)據(jù)依然存在
adapter.submitList(list);
});? 優(yōu)勢:生命周期獨立于 Activity,配置變更時不銷毀。
四、方案二:阻止重建 + 手動處理變更
通過在 AndroidManifest.xml 中聲明 android:configChanges,可阻止系統(tǒng)自動重建 Activity。
1. 聲明要自行處理的配置變更
<activity
android:name=".MainActivity"
android:exported="true"
android:configChanges="orientation|screenSize|keyboardHidden"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>?? 關鍵屬性說明:
orientation:屏幕方向變更(橫/豎屏)screenSize:屏幕尺寸變化(API 13+,常與 orientation 同時使用)keyboardHidden:軟鍵盤顯示/隱藏
2. 重寫 onConfigurationChanged() 方法
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// 判斷當前屏幕方向
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Toast.makeText(this, "已切換到橫屏", Toast.LENGTH_SHORT).show();
// 動態(tài)調整 UI
adjustForLandscape();
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
Toast.makeText(this, "已切換到豎屏", Toast.LENGTH_SHORT).show();
adjustForPortrait();
}
}3. 動態(tài)調整 UI 示例
private void adjustForLandscape() {
// 橫屏下隱藏某些 View
findViewById(R.id.ad_banner).setVisibility(View.GONE);
// 調整 RecyclerView 布局管理器
RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new GridLayoutManager(this, 2));
}
private void adjustForPortrait() {
// 豎屏下顯示廣告
findViewById(R.id.ad_banner).setVisibility(View.VISIBLE);
// 恢復線性布局
RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
}? 優(yōu)點:避免 Activity 重建,提升性能。
? 缺點:需手動處理所有 UI 變化,代碼復雜度增加。
五、為不同屏幕提供專屬布局
最優(yōu)雅的適配方式是使用 資源限定符(Resource Qualifiers),讓系統(tǒng)自動加載合適的布局。
1. 創(chuàng)建橫屏專用布局
res/
├── layout/
│ └── activity_main.xml # 豎屏布局
└── layout-land/
└── activity_main.xml # 橫屏布局2. 橫屏布局示例(layout-land/activity_main.xml)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<!-- 左側列表 -->
<fragment
android:id="@+id/fragment_list"
android:name="com.example.NewsListFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<!-- 右側詳情 -->
<FrameLayout
android:id="@+id/detail_container"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2" />
</LinearLayout>? 優(yōu)勢:完全解耦,UI 設計自由度高,適合平板或大屏設備。
六、高級技巧與最佳實踐
1. 監(jiān)聽軟鍵盤彈出/隱藏
// 在 onConfigurationChanged 中判斷
if (newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO) {
// 軟鍵盤彈出
} else if (newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_YES) {
// 軟鍵盤隱藏
}2. 折疊屏與多窗口支持
<!-- 支持多窗口 -->
<activity
android:name=".MainActivity"
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
android:configChanges="orientation|screenSize|smallestScreenSize">
</activity>3. 使用 Jetpack Compose 實現(xiàn)響應式 UI
@Composable
fun ResponsiveLayout() {
val configuration = LocalConfiguration.current
val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
if (isLandscape) {
Row { /* 橫屏布局 */ }
} else {
Column { /* 豎屏布局 */ }
}
}七、常見問題與避坑指南
android:configChanges 不生效?確保同時聲明
orientation和screenSize(API 13+)。橫屏布局未加載?檢查文件夾命名是否正確(
layout-land),且無拼寫錯誤。ViewModel 數(shù)據(jù)在重建后丟失?確保使用
ViewModelProvider(this)而非ViewModelProvider(requireActivity())。動畫在旋轉后中斷?將動畫邏輯放在
ViewModel或使用onRetainNonConfigurationInstance()。
八、結語
到此這篇關于Android開發(fā)教程之屏幕變更事件的文章就介紹到這了,更多相關Android屏幕變更事件內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Android開發(fā)之permission動態(tài)權限獲取詳解
這篇文章主要為大家詳細介紹了Android開發(fā)之permission動態(tài)權限獲取,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-08-08
Listview加載的性能優(yōu)化是如何實現(xiàn)的
在android開發(fā)中Listview是一個很重要的組件,它以列表的形式根據(jù)數(shù)據(jù)的長自適應展示具體內容,用戶可以自由的定義listview每一列的布局,接下來通過本文給大家介紹Listview加載的性能優(yōu)化是如何實現(xiàn)的,對listview性能優(yōu)化相關知識感興趣的朋友一起學習吧2016-01-01
Android中利用Xposed框架實現(xiàn)攔截系統(tǒng)方法
這篇文章主要介紹了Android中利用Xposed框架實現(xiàn)攔截系統(tǒng)方法的相關資料,需要的朋友可以參考下2016-11-11

