Android

关注公众号 jb51net

关闭
首页 > 软件编程 > Android > Android屏幕变更事件

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

作者:咖啡の猫

Android屏幕变更事件(如旋转、尺寸调整)会触发Activity重建,导致状态丢失和性能问题,这篇文章主要介绍了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 (横屏)

问题与挑战

三、方案一:允许重建 + 正确保存状态

如果选择接受默认重建行为,必须确保关键数据不丢失。

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开发教程之屏幕变更事件的文章就介绍到这了,更多相关Android屏幕变更事件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文