Giới thiệu
Hướng dẫn này trình bày cách phát hiện sự thay đổi hướng màn hình (portrait ↔ landscape) trên Android bằng cách override phương thức onConfigurationChanged() trong Activity, sử dụng Kotlin.
Các bước thực hiện
Bước 1: Tạo dự án mới
Tạo project: File → New Project.
Bước 2: Thiết kế giao diện (res/layout/activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="50dp"
android:text="Tutorials Point"
android:textAlignment="center"
android:textColor="@android:color/holo_green_dark"
android:textSize="32sp"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Hello World!"
android:textSize="24sp"
android:textStyle="bold" />
</RelativeLayout>
Bước 3: Xử lý logic (src/MainActivity.kt)
import android.content.res.Configuration
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
title = "KotlinApp"
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
when (newConfig.orientation) {
Configuration.ORIENTATION_LANDSCAPE -> {
Toast.makeText(baseContext, "Landscape Mode", Toast.LENGTH_SHORT).show()
// Xử lý layout landscape
}
Configuration.ORIENTATION_PORTRAIT -> {
Toast.makeText(baseContext, "Portrait Mode", Toast.LENGTH_SHORT).show()
// Xử lý layout portrait
}
Configuration.ORIENTATION_UNDEFINED -> {
Toast.makeText(baseContext, "Undefined Orientation", Toast.LENGTH_SHORT).show()
}
}
}
}
Bước 4: Cấu hình Manifest (androidManifest.xml) - QUAN TRỌNG
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="app.com.q11">
<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|screenLayout|keyboardHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Chạy ứng dụng
Kết nối thiết bị, nhấn Run. Xoay thiết bị ngang/dọc. Toast sẽ hiển thị "Landscape Mode" hoặc "Portrait Mode" tương ứng. Activity không bị destroy/recreate nhờ android:configChanges.
Giải thích android:configChanges
Mặc định, khi xoay màn hình, Android destroy và recreate Activity (gọi onDestroy → onCreate). Để tự xử lý thay đổi cấu hình, khai báo android:configChanges:
orientation: Thay đổi hướng màn hìnhscreenSize: Thay đổi kích thước màn hình (bắt buộc từ API 13+)screenLayout: Thay đổi bố cục màn hìnhkeyboardHidden: Bàn phím ẩn/hiệnfontScale: Thay đổi cỡ font hệ thốnguiMode: Chế độ Night/Day, Car, Desk, VR...density: Mật độ pixel (multi-window)
Khi nào nên dùng onConfigurationChanged?
- Video player: Giữ trạng thái phát khi xoay.
- Game: Không muốn reset state.
- WebView: Giữ trang web đang load.
- Camera preview: Không ngắt luồng camera.
Khi nào KHÔNG nên dùng?
- UI đơn giản: Delegate cho hệ thống recreate an toàn hơn.
- Cần layout khác nhau cho portrait/landscape (dùng layout-port/layout-land).
- Sử dụng ViewBinding/DataBinding phức tạp (recreate dễ quản lý).
Cách hiện đại: ViewModel + LiveData
Thay vì override configChanges, hãy dùng ViewModel để giữ data qua recreation:
class MyViewModel : ViewModel() {
val data = MutableLiveData()
}
// Activity
val model = ViewModelProvider(this).get(MyViewModel::class.java)
model.data.observe(this) { updateUI(it) }
// Layout khác nhau:
res/layout/activity_main.xml (portrait)
res/layout-land/activity_main.xml (landscape)
Lưu ý
- Nếu dùng
android:configChanges, bạn phải tự cập nhật UI trongonConfigurationChanged(inflate layout mới, rebind view, v.v.). - Trên Android 12+, có thêm
screenLayoutvàdensitychanges khi multi-window.