开发者

Android开发教程之屏幕变更事件

目录
  • 一、什么是屏幕变更事件?
    • 常见的配置变更类型
  • 二、默认行为:Activity 重建
    • 生命周期流程
    • 问题与挑战
  • 三、方案一:允许重建 + 正确保存状态
    • 1. 使用 onSaveInstanceState() 保存临时状态
    • 2. 使用 ViewModel 保留复杂数据
  • 四、方案二:阻止重建 + 手动处理变更
    • 1. 声明要自行处理的配置变更
    • 2. 重写 onConfigurationChanged() 方法
    • 3. 动态调整 UI 示例
  • 五、为不同屏幕提供专属布局
    • 1. 创建横屏专用布局
    • 2. 横屏布局示例(layout-land/activity_main.XML)
  • 六、高级技巧与最佳实践
    • 1. 监听软键盘弹出/隐藏
    • 2. 折叠屏与多窗口支持
    • 3. 使用 Jetpack Compose 实现响应式 UI
    • 七、常见问题与避坑指南
  • 八、结语

    一、什么是屏幕变更事件?

    当设备的配置(Configuration) 发生变化时,android 系统会触发 Configuration Change 事件。

    常见的配置变更类型

    javascript更类型示例
    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);
    
      http://www.devze.com  // 判断当前屏幕方向
        if (newConfiphpg.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屏幕变更事件内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!

    0

    上一篇:

    下一篇:

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    最新开发

    开发排行榜