Android开发教程之屏幕变更事件
作者:咖啡の猫
一、什么是屏幕变更事件?
当设备的配置(Configuration) 发生变化时,Android 系统会触发 Configuration Change
事件。
常见的配置变更类型
变更类型 | 示例 |
---|---|
orientation | 屏幕旋转(竖屏 ↔ 横屏) |
screenLayout | 屏幕尺寸/密度变化(如折叠屏展开) |
keyboardHidden | 软键盘弹出/隐藏 |
fontScale | 系统字体大小调整 |
locale | 系统语言切换 |
uiMode | 夜间模式开启/关闭 |
⚠️ 默认行为:系统会销毁并重建 Activity(调用
onDestroy()
→onCreate()
),以便加载适配新配置的资源。
二、默认行为:Activity 重建
生命周期流程
Activity A (竖屏) ↓ 用户旋转屏幕 onPause() → onStop() → onDestroy() ↓ 系统重建 onCreate() → onStart() → onResume() → Activity A (横屏)
问题与挑战
- 状态丢失:
onCreate()
中初始化的数据可能丢失。 - 性能损耗:重复执行
setContentView()
、数据库查询、网络请求。 - 用户体验差:页面闪退或重新加载。
三、方案一:允许重建 + 正确保存状态
如果选择接受默认重建行为,必须确保关键数据不丢失。
1. 使用 onSaveInstanceState() 保存临时状态
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); // 恢复保存的状态 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()); } }
✅ 适用场景:轻量级状态(如文本框内容、滚动位置)。
❌ 不适用:大数据、文件句柄、网络连接。
2. 使用 ViewModel 保留复杂数据
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 重建,数据依然存在 adapter.submitList(list); });
✅ 优势:生命周期独立于 Activity,配置变更时不销毁。
四、方案二:阻止重建 + 手动处理变更
通过在 AndroidManifest.xml
中声明 android:configChanges
,可阻止系统自动重建 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(); // 动态调整 UI adjustForLandscape(); } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { Toast.makeText(this, "已切换到竖屏", Toast.LENGTH_SHORT).show(); adjustForPortrait(); } }
3. 动态调整 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)); }
✅ 优点:避免 Activity 重建,提升性能。
❌ 缺点:需手动处理所有 UI 变化,代码复杂度增加。
五、为不同屏幕提供专属布局
最优雅的适配方式是使用 资源限定符(Resource Qualifiers),让系统自动加载合适的布局。
1. 创建横屏专用布局
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>
✅ 优势:完全解耦,UI 设计自由度高,适合平板或大屏设备。
六、高级技巧与最佳实践
1. 监听软键盘弹出/隐藏
// 在 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 实现响应式 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 数据在重建后丢失?确保使用
ViewModelProvider(this)
而非ViewModelProvider(requireActivity())
。动画在旋转后中断?将动画逻辑放在
ViewModel
或使用onRetainNonConfigurationInstance()
。
八、结语
到此这篇关于Android开发教程之屏幕变更事件的文章就介绍到这了,更多相关Android屏幕变更事件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!