ภาพรวม
บทความนี้สาธิตวิธีตรวจจับการเปลี่ยนแปลงทิศทางหน้าจอ (Portrait ↔ Landscape) ใน Android มี 2 วิธีหลัก: 1) Override onConfigurationChanged() พร้อม android:configChanges ใน Manifest (แอปจัดการเอง) 2) ให้ระบบ Recreate Activity (Default behavior) และตรวจสอบใน onCreate()
ขั้นตอนที่ 1: สร้างโปรเจกต์ใหม่
สร้างโปรเจกต์ Android ใหม่ใน Android Studio
ขั้นตอนที่ 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:gravity="center"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:textSize="24sp"
android:textStyle="bold" />
</LinearLayout>
วิธีที่ 1: จัดการเองด้วย configChanges (ไม่ Recreate Activity)
AndroidManifest.xml - เพิ่ม configChanges
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
MainActivity.java
import androidx.appcompat.app.AppCompatActivity;
import android.content.res.Configuration;
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// ตรวจสอบ Orientation
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Toast.makeText(this, "📱 Landscape Mode (แนวนอน)", Toast.LENGTH_SHORT).show();
// ปรับ Layout, Visibility, หรือ Logic สำหรับ Landscape
updateUIForLandscape();
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
Toast.makeText(this, "📱 Portrait Mode (แนวตั้ง)", Toast.LENGTH_SHORT).show();
// ปรับ Layout, Visibility, หรือ Logic สำหรับ Portrait
updateUIForPortrait();
}
// ตรวจสอบการเปลี่ยนแปลงอื่นๆ
if ((newConfig.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK)
== Configuration.SCREENLAYOUT_SIZE_LARGE) {
// หน้าจอขนาดใหญ่ (Tablet, Foldable)
}
}
private void updateUIForLandscape() {
// ตัวอย่าง: ซ่อน Toolbar, แสดง 2 Column, ปรับขนาด Font
}
private void updateUIForPortrait() {
// ตัวอย่าง: แสดง Toolbar, 1 Column, ปรับขนาด Font
}
}
วิธีที่ 2: ให้ระบบ Recreate Activity (Default - แนะนำสำหรับมือใหม่)
AndroidManifest.xml - ไม่ต้องเพิ่ม configChanges
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
MainActivity.java
import androidx.appcompat.app.AppCompatActivity;
import android.content.res.Configuration;
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// ตรวจสอบ Orientation ปัจจุบัน
int orientation = getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
Toast.makeText(this, "Landscape (onCreate)", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Portrait (onCreate)", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// ไม่จำเป็นต้องทำอะไร เพราะ Activity จะถูก Destroy/Recreate
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// บันทึกข้อมูลที่ต้องการเก็บไว้ (Scroll Position, Form Data, etc.)
outState.putString("key", "value");
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// เรียกคืนข้อมูล
String value = savedInstanceState.getString("key");
}
}
Layout แยกตาม Orientation (Best Practice)
สร้างไฟล์ Layout แยกในโฟลเดอร์ res/layout-land/:
res/layout/activity_main.xml- Portrait Layoutres/layout-land/activity_main.xml- Landscape Layout
ระบบจะเลือก Layout อัตโนมัติเมื่อหมุนหน้าจอ (ถ้าไม่ใช้ configChanges)
การจัดการกับ Foldable / Multi-window (Modern Android)
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// ตรวจสอบ Foldable State
if (newConfig.screenLayout != 0) {
int screenLayout = newConfig.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
// SMALL, NORMAL, LARGE, XLARGE
}
// ตรวจสอบ Window Metrics (API 30+)
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
android.view.WindowMetrics metrics = getWindowManager().getCurrentWindowMetrics();
android.graphics.Insets insets = metrics.getWindowInsets()
.getInsetsIgnoringVisibility(android.view.WindowInsets.Type.systemBars());
// คำนวณพื้นที่ใช้งานจริง
}
}
ตารางเปรียบเทียบ 2 วิธี
| ด้าน | configChanges (Manual) | Default Recreate |
|---|---|---|
| Performance | ดีกว่า (ไม่ Recreate) | ช้ากว่า (Destroy/Create) |
| Complexity | สูง (จัดการ View/State เอง) | ต่ำ (ระบบจัดการ) |
| State Management | Manual (Retain Instance) | Auto (onSaveInstanceState) |
| Layout Switching | Manual (setContentView) | Auto (layout-land folder) |
| สถานการณ์เหมาะสม | Video Player, Camera, Game, Map | Form, List, General App |
Best Practices
- Default: ให้ระบบ Recreate (วิธีที่ 2) - เรียบง่าย ปลอดภัย
- ใช้ configChanges เมื่อ: Video Player, Camera Preview, OpenGL Games, WebView ที่ซับซ้อน
- ViewModel + LiveData: ใช้ Architecture Components เพื่อเก็บ State ระหว่าง Rotation ได้อัตโนมัติ
- Testing: ทดสอบหมุนหน้าจอซ้ำๆ (Don't keep activities ใน Developer Options)