สำหรับแอนดรอยด์แล้ว การทำให้ Layout สามารถแสดงผลแยกกันระหว่างหน้าจอแนวตั้งกับแนวนอนนั้นไม่ใช่เรื่องอยากซักเท่าไร เพราะแอนดรอยด์ได้สร้างสิ่งที่เรียกว่า Configuration Qualifier เพื่อช่วยจัดการเรื่องนี้แล้ว แต่ถ้าอยากจะดัก Event เมื่อผู้ใช้มีการหมุนหน้าจออุปกรณ์แอนดรอยด์ล่ะ ต้องทำยังไง?
วิธีที่นักพัฒนาส่วนใหญ่ใช้กัน
เนื่องจากแอนดรอยด์ไม่มีคำสั่งเช็ค Event ดังกล่าวให้ ดังนั้นนักพัฒนาส่วนใหญ่จึงใช้วิธีแบบนี้แทนเริ่มจากกำหนด configChange ใน Android Manifest ให้กับ Activity ที่ต้องการดังนี้
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest>
...
<application>
<activity
android:name=".MainActivity"
android:configChanges="screenSize|orientation"/>
...
</application>
</manifest>
แล้วใน Activity ก็ดัก Event จาก onConfigurationChanged เพื่อเทียบดูว่าค่า Orientation เปลี่ยนหรือไม่
MainActivity.java
import android.content.res.Configuration;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
private int screenOrientation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (screenOrientation != newConfig.orientation) {
onScreenOrientationChanged();
screenOrientation = newConfig.orientation;
}
}
public void onScreenOrientationChanged() {
// Do something here when screen orientation has changed
}
}
ซึ่งบอกเลยว่าวิธีนี้อาจจะดูเหมือนทำงานได้ แต่เอาเข้าจริงแล้วเจ้าของบล็อกไม่แนะนำให้ทำแบบนี้ด้วยซ้ำ
ทำไมวิธีนี้ถึงไม่ถูกต้อง?
ถ้าอยากจะรู้เรื่องนี้แบบละเอียดๆ ให้ไปอ่านที่ [Android Code] Configuration Changes อีกหนึ่งอย่างที่นักพัฒนาแอนดรอยด์ควรรู้จักแต่ถ้าจะให้อธิบายสั้นๆก็ต้องบอกว่า android:configChange ไม่ได้ทำมาให้ใช้งานแบบนี้ ดังนั้นถ้าใช้วิธีแบบนี้ นั่นหมายความว่า Configuration Change ของ Activity ตัวนั้นๆจะทำงานไม่ถูกต้อง เช่น แบ่ง Layout ไว้เป็น Portrait หรือ Landscape และเมื่อผู้ใช้หมุนหน้าจอจะไม่เปลี่ยนไปตาม Behavior ที่ถูกต้อง
วิธีที่ดีกว่าการไปยุ่งกับ Configuration Change
เนื่องจากการไปยุ่งกับ Configuration Change ไม่ใช่วิธีที่ถูกต้อง ดังนั้นเจ้าของบล็อกจึงต้องมองหาวิธีอื่นๆที่สามารถรับรู้ได้ว่า "เฮ้ย หน้าจอมีการหมุนนะ"และวิธีที่เลือกใช้ก็คือเช็ค Screen Orientation ตอน onStart แล้วเก็บค่าไว้ และเมื่อมีการหมุนหน้าจอ onStart ก็จะถูกเรียกอีกครั้ง ก็จะดึงค่า Screen Orientation มาเทียบกับของเก่าว่ามีการเปลี่ยนแปลงหรือไม่ (เนื่องจาก onStart สามารถถูกเรียกได้ตลอดเวลา)
SampleActivity.java
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class SampleActivity extends AppCompatActivity {
private int lastOrientation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
if (savedInstanceState == null) {
lastOrientation = getResources().getConfiguration().orientation;
}
}
@Override
protected void onStart() {
super.onStart();
checkOrientationChanged();
}
private void checkOrientationChanged() {
int currentOrientation = getResources().getConfiguration().orientation;
if (currentOrientation != lastOrientation) {
onScreenOrientationChanged(currentOrientation);
lastOrientation = currentOrientation;
}
}
public void onScreenOrientationChanged(int currentOrientation) {
// Do something here when screen orientation changed
}
}
และสำหรับค่า lastOrientation ถ้าจะให้ดีที่สุดก็ควร Handle ใน Save/Restore Instant State ด้วย ก็เลยเขียนเพิ่มเข้ามาอีกนิดหน่อย กลายเป็นแบบนี้
SampleActivity.java
package com.akexorcist.myapplication;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class SampleActivity extends AppCompatActivity {
public static final String KEY_LAST_ORIENTATION = "last_orientation";
private int lastOrientation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
if (savedInstanceState == null) {
lastOrientation = getResources().getConfiguration().orientation;
}
}
@Override
protected void onStart() {
super.onStart();
checkOrientationChanged();
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
lastOrientation = savedInstanceState.getInt(KEY_LAST_ORIENTATION);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(KEY_LAST_ORIENTATION, lastOrientation);
}
private void checkOrientationChanged() {
int currentOrientation = getResources().getConfiguration().orientation;
if (currentOrientation != lastOrientation) {
onScreenOrientationChanged(currentOrientation);
lastOrientation = currentOrientation;
}
}
public void onScreenOrientationChanged(int currentOrientation) {
// Do something here when screen orientation changed
}
}
เท่านี้ก็เรียบร้อย~ สามารถรับรู้ได้ว่ามีการหมุนหน้าจอได้จาก Method ที่ชื่อว่า onScreenOrientationChanged
ไหนๆก็มาถึงจุดนี้แล้ว ทำเป็น Library ไปเลยละกัน
จากโค้ดดังกล่าวจึงทำให้เจ้าของบล็อกรวบคำสั่งแล้วทำเป็น Library ไว้ใช้งานไปเลย จะได้เรียกใช้งานสะดวกๆหน่อย ก็เลยทำเป็น Library ที่ชื่อว่า ScreenOrientationHelper ซะเลยวิธีใช้งาน Screen Orientation Helper
เพิ่ม Dependency ของ Library ตัวนี้ไว้ใน build.gradle ดังนี้compile 'com.akexorcist:screenorientationhelper:1.0.0'
เวลาจะเรียกใช้งานให้สร้าง Base Activity Class ขึ้นมาครับ เพราะผมเขียนโค้ดไว้ให้มันสามารถนำไป Imprement ใส่ Activity ใดๆก็ได้ตามที่ต้องการ
ยกตัวอย่างเช่น Activity ที่ผู้ที่หลงเข้ามาอ่านใช้งานอยู่คือ AppCompatActivity ดังนั้นก็ให้สร้าง Base Activity Class ขึ้นมาดังนี้
BaseActivity.java
import android.support.v7.app.AppCompatActivity;
public class BaseActivity extends AppCompatActivity {
}
แล้วเพิ่มคำสั่งของ ScreenOrientationHelper เข้าไปดังนี้
BaseActivity.java
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import com.akexorcist.screenorientationhelper.ScreenOrientationHelper;
public class BaseActivity extends AppCompatActivity implements ScreenOrientationHelper.ScreenOrientationChangeListener {
private ScreenOrientationHelper helper = new ScreenOrientationHelper(this);
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
helper.onCreate(savedInstanceState);
helper.setScreenOrientationChangeListener(this);
}
@Override
protected void onStart() {
super.onStart();
helper.onStart();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
helper.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
helper.onRestoreInstanceState(savedInstanceState);
}
@Override
public void onScreenOrientationChanged(int orientation) {
}
}
โค้ดส่วนนี้เป็น Boilerpate Code ครับ ส่วนหนึ่งเพราะต้องการให้เอาไปใช้กับ Activity ตัวไหนก็ได้นั่นเอง ก็ให้สร้างเป็น Base Activity Class แบบนี้แล้วเรียกใช้ใน Activity ตัวอื่นๆตามต้องการละกันเนอะ
เวลาเอาไปใช้งานจริงๆก็จะเป็นแบบนี้
import android.os.Bundle;
public class MainActivity extends BaseActivity {
...
@Override
public void onScreenOrientationChanged(int orientation) {
// Do something when screen orientation changed
}
}
แค่ Override Method ที่ชื่อว่า onScreenOrientationChanged ก็จะสามารถดัก Event เมื่อผู้ใช้มีการหมุนหน้าจอได้แล้ววววววววววววว
สรุป
เนื่องจากเจ้าของบล็อกเห็นว่ายังมีนักพัฒนาหลายๆคนที่ใช้วิธีแบบผิดๆกันอยู่ แม้กระทั่งใน StackOverflow ก็ตาม ก็เลยมองหาวิธีอื่นที่ดีกว่ามาทดแทน แต่พอๆเขียนไปซักพักก็รู้สึกว่าน่าจะจับมาทำเป็น Library เลยดีกว่า ก็เลยออกมาเป็นแบบนี้นี่แหละครับ ฮาๆดังนั้นเลิกเถอะครับ เลิกไปยุ่งกับ Configuration Change โดยไม่จำเป็น เพียงแค่เพราะอยากจะดัก Event ตอนที่ผู้ใช้หมุนหน้าจอ
สำหรับ Library ของ ScreenOrientationHelper สามารถเข้าไปดูโค้ดที่เจ้าของบล็อกเขียนไว้ได้ที่ Screen Orientation Helper [GitHub]