Handle Orientation Change - Xử lý xoay màn hình
Mặc định Android hủy và tạo lại Activity khi xoay màn hình (configuration change). Có 2 cách chính để xử lý:
- Cho phép recreate - Lưu/restore state qua
onSaveInstanceState/onRestoreInstanceState - Tự xử lý - Khai báo
android:configChangestrong Manifest, overrideonConfigurationChanged()
Cách 1: Tự xử lý (Ví dụ này)
Bước 1: Tạo dự án mới
Khởi tạo project Android Studio.
Bước 2: Layout (res/layout/activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
tools:context=".MainActivity">
<TextView
android:id="@+id/actionEvent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Xoay màn hình để test"
android:textSize="24sp"
android:padding="24dp" />
</LinearLayout>
Bước 3: MainActivity.java
package com.example.myapplication;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private TextView actionEvent;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
actionEvent = findViewById(R.id.actionEvent);
updateOrientationText();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
updateOrientationText();
}
private void updateOrientationText() {
int orientation = getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
actionEvent.setText("HƯỚNG: NGANG (LANDSCAPE)");
} else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
actionEvent.setText("HƯỚNG: DỌC (PORTRAIT)");
} else {
actionEvent.setText("HƯỚNG: KHÔNG XÁC ĐỊNH");
}
}
}
Bước 4: AndroidManifest.xml - Quan trọng!
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="com.example.myapplication">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize|keyboardHidden|smallestScreenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Giải thích android:configChanges
| Giá trị | Ý nghĩa | |
|---|---|---|
orientation |
Xoay màn hình (portrait/landscape) | |
screenSize |
Thay đổi kích thước màn hình (API 13+) - BẮT BUỘC nếu dùng orientation | |
keyboardHidden |
Bàn phím ảo hiện/ẩn | |
smallestScreenSize |
Thay đổi smallest width (API 13+) | |
screenLayout |
Thay đổi layout direction (RTL/LTR) | |
uiMode |
Chế độ UI (night mode, car mode, watch) | |
locale |
Thay đổi ngôn ngữ | |
fontScale |
Thay đổi cỡ font hệ thống | |
density |
Thay đổi mật độ pixel |
| Tiêu chí | configChanges (Tự xử lý) | Recreate + ViewModel/onSaveInstanceState |
|---|---|---|
| Độ phức tạp | Thấp | Trung bình |
| Hiệu năng | Tốt (không recreate) | Recreate cost (nhỏ) |
| Layout landscape/portrait riêng | Khó (phải tự inflate/swap) | Dễ (res/layout-land/) |
| System handling | Bỏ qua system logic | Tận dụng system |
| Memory leak risk | Cao hơn (view giữ reference cũ) | Thấp (ViewModel lifecycle-aware) |
| Khuyến nghị Google | Chỉ khi cần thiết đặc biệt | Khuyến nghị chuẩn |
Khi nào NÊN dùng configChanges?
- Video player, game - không muốn interrupt playback
- Camera preview - tái tạo camera tốn tài nguyên
- WebView - giữ trạng thái trang web
- Map/Navigation - giữ vị trí, zoom
Khi nào KHÔNG nên dùng?
- UI thông thường - dùng ViewModel + layout-land
- Form nhập liệu - ViewModel tự restore
- Cần layout khác nhau portrait/landscape
Best Practices hiện đại
- Mặc định: Không dùng configChanges, dùng ViewModel + LiveData/Flow
- Layout riêng: Tạo
res/layout-land/activity_main.xmlcho landscape - State preservation: ViewModel tự survive, hoặc
onSaveInstanceStatecho data nhỏ - Chỉ dùng configChanges khi thực sự cần thiết (media, camera, webview)