Android開發(fā)教程之屏幕變更事件
一、什么是屏幕變更事件?
當(dāng)設(shè)備的配置(Configuration) 發(fā)生變化時(shí),Android 系統(tǒng)會(huì)觸發(fā) Configuration Change 事件。
常見的配置變更類型
| 變更類型 | 示例 |
|---|---|
orientation | 屏幕旋轉(zhuǎn)(豎屏 ↔ 橫屏) |
screenLayout | 屏幕尺寸/密度變化(如折疊屏展開) |
keyboardHidden | 軟鍵盤彈出/隱藏 |
fontScale | 系統(tǒng)字體大小調(diào)整 |
locale | 系統(tǒng)語(yǔ)言切換 |
uiMode | 夜間模式開啟/關(guān)閉 |
?? 默認(rèn)行為:系統(tǒng)會(huì)銷毀并重建 Activity(調(diào)用
onDestroy()→onCreate()),以便加載適配新配置的資源。
二、默認(rèn)行為:Activity 重建
生命周期流程
Activity A (豎屏)
↓ 用戶旋轉(zhuǎn)屏幕
onPause() → onStop() → onDestroy()
↓ 系統(tǒng)重建
onCreate() → onStart() → onResume()
→ Activity A (橫屏)問(wèn)題與挑戰(zhàn)
- 狀態(tài)丟失:
onCreate()中初始化的數(shù)據(jù)可能丟失。 - 性能損耗:重復(fù)執(zhí)行
setContentView()、數(shù)據(jù)庫(kù)查詢、網(wǎng)絡(luò)請(qǐng)求。 - 用戶體驗(yàn)差:頁(yè)面閃退或重新加載。
三、方案一:允許重建 + 正確保存狀態(tài)
如果選擇接受默認(rèn)重建行為,必須確保關(guān)鍵數(shù)據(jù)不丟失。
1. 使用 onSaveInstanceState() 保存臨時(shí)狀態(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);
// 恢復(fù)保存的狀態(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());
}
}? 適用場(chǎng)景:輕量級(jí)狀態(tài)(如文本框內(nèi)容、滾動(dòng)位置)。
? 不適用:大數(shù)據(jù)、文件句柄、網(wǎng)絡(luò)連接。
2. 使用 ViewModel 保留復(fù)雜數(shù)據(jù)
public class MainViewModel extends ViewModel {
private MutableLiveData<List<String>> dataList = new MutableLiveData<>();
public LiveData<List<String>> getDataList() {
return dataList;
}
public void loadData() {
// 模擬耗時(shí)加載
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)勢(shì):生命周期獨(dú)立于 Activity,配置變更時(shí)不銷毀。
四、方案二:阻止重建 + 手動(dòng)處理變更
通過(guò)在 AndroidManifest.xml 中聲明 android:configChanges,可阻止系統(tǒng)自動(dò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>?? 關(guān)鍵屬性說(shuō)明:
orientation:屏幕方向變更(橫/豎屏)screenSize:屏幕尺寸變化(API 13+,常與 orientation 同時(shí)使用)keyboardHidden:軟鍵盤顯示/隱藏
2. 重寫 onConfigurationChanged() 方法
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// 判斷當(dāng)前屏幕方向
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Toast.makeText(this, "已切換到橫屏", Toast.LENGTH_SHORT).show();
// 動(dòng)態(tài)調(diào)整 UI
adjustForLandscape();
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
Toast.makeText(this, "已切換到豎屏", Toast.LENGTH_SHORT).show();
adjustForPortrait();
}
}3. 動(dòng)態(tài)調(diào)整 UI 示例
private void adjustForLandscape() {
// 橫屏下隱藏某些 View
findViewById(R.id.ad_banner).setVisibility(View.GONE);
// 調(diào)整 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);
// 恢復(fù)線性布局
RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
}? 優(yōu)點(diǎn):避免 Activity 重建,提升性能。
? 缺點(diǎn):需手動(dòng)處理所有 UI 變化,代碼復(fù)雜度增加。
五、為不同屏幕提供專屬布局
最優(yōu)雅的適配方式是使用 資源限定符(Resource Qualifiers),讓系統(tǒng)自動(dò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">
<!-- 左側(cè)列表 -->
<fragment
android:id="@+id/fragment_list"
android:name="com.example.NewsListFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<!-- 右側(cè)詳情 -->
<FrameLayout
android:id="@+id/detail_container"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2" />
</LinearLayout>? 優(yōu)勢(shì):完全解耦,UI 設(shè)計(jì)自由度高,適合平板或大屏設(shè)備。
六、高級(jí)技巧與最佳實(shí)踐
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 實(shí)現(xiàn)響應(yīng)式 UI
@Composable
fun ResponsiveLayout() {
val configuration = LocalConfiguration.current
val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
if (isLandscape) {
Row { /* 橫屏布局 */ }
} else {
Column { /* 豎屏布局 */ }
}
}七、常見問(wèn)題與避坑指南
android:configChanges 不生效?確保同時(shí)聲明
orientation和screenSize(API 13+)。橫屏布局未加載?檢查文件夾命名是否正確(
layout-land),且無(wú)拼寫錯(cuò)誤。ViewModel 數(shù)據(jù)在重建后丟失?確保使用
ViewModelProvider(this)而非ViewModelProvider(requireActivity())。動(dòng)畫在旋轉(zhuǎn)后中斷?將動(dòng)畫邏輯放在
ViewModel或使用onRetainNonConfigurationInstance()。
八、結(jié)語(yǔ)
到此這篇關(guān)于Android開發(fā)教程之屏幕變更事件的文章就介紹到這了,更多相關(guān)Android屏幕變更事件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android開發(fā)之permission動(dòng)態(tài)權(quán)限獲取詳解
這篇文章主要為大家詳細(xì)介紹了Android開發(fā)之permission動(dòng)態(tài)權(quán)限獲取,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08
Listview加載的性能優(yōu)化是如何實(shí)現(xiàn)的
在android開發(fā)中Listview是一個(gè)很重要的組件,它以列表的形式根據(jù)數(shù)據(jù)的長(zhǎng)自適應(yīng)展示具體內(nèi)容,用戶可以自由的定義listview每一列的布局,接下來(lái)通過(guò)本文給大家介紹Listview加載的性能優(yōu)化是如何實(shí)現(xiàn)的,對(duì)listview性能優(yōu)化相關(guān)知識(shí)感興趣的朋友一起學(xué)習(xí)吧2016-01-01
Android 代碼寫控件代替XML簡(jiǎn)單實(shí)例
這篇文章主要介紹了Android 代碼寫控件代替XML簡(jiǎn)單實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-05-05
Kotlin編程基礎(chǔ)數(shù)據(jù)類型示例詳解
這篇文章主要為大家介紹了Kotlin編程基礎(chǔ)數(shù)據(jù)類型示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
Android自定義控件簡(jiǎn)單實(shí)現(xiàn)側(cè)滑菜單效果
這篇文章主要為大家詳細(xì)介紹了Android自定義控件打造史上最簡(jiǎn)單的側(cè)滑菜單的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-06-06
Android中利用Xposed框架實(shí)現(xiàn)攔截系統(tǒng)方法
這篇文章主要介紹了Android中利用Xposed框架實(shí)現(xiàn)攔截系統(tǒng)方法的相關(guān)資料,需要的朋友可以參考下2016-11-11
Android學(xué)習(xí)之AppWidget筆記分享
這篇文章主要為大家詳細(xì)介紹了Android學(xué)習(xí)筆記之AppWidget的相關(guān)資料,感興趣的小伙伴們可以參考一下2016-08-08

