ดัก Screen Orientation Event ใน Activity อย่างไรให้ถูกต้อง
สำหรับแอนดรอยด์แล้ว การทำให้ 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 เปลี่ยนหรือไม่
class MainActivity : AppCompatActivity() {
private var screenOrientation = 0
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
if (screenOrientation != newConfig.orientation) {
screenOrientation = newConfig.orientation
// Do something here when screen orientation has changed
}
}
}
ซึ่งบอกเลยว่าวิธีนี้อาจจะดูเหมือนทำงานได้ แต่เอาเข้าจริงแล้วเจ้าของบล็อกไม่แนะนำให้ทำแบบนี้ด้วยซ้ำ
ทำไมวิธีนี้ถึงไม่ถูกต้อง?
ถ้าอยากจะรู้เรื่องนี้แบบละเอียดๆ ให้ไปอ่านที่ Configuration Changes อีกหนึ่งอย่างที่นักพัฒนาแอนดรอยด์ควรรู้จัก
แต่ถ้าจะให้อธิบายสั้นๆก็ต้องบอกว่า android:configChange
ไม่ได้ทำมาให้ใช้งานแบบนี้ ดังนั้นถ้าใช้วิธีแบบนี้ นั่นหมายความว่า Configuration Change ของ Activity ตัวนั้นๆจะทำงานไม่ถูกต้อง เช่น แบ่ง Layout ไว้เป็น Portrait หรือ Landscape และเมื่อผู้ใช้หมุนหน้าจอจะไม่เปลี่ยนไปตาม Behavior ที่ถูกต้อง
วิธีที่ดีกว่าการไปยุ่งกับ Configuration Change
เนื่องจากการไปยุ่งกับ Configuration Change ไม่ใช่วิธีที่ถูกต้อง ดังนั้นเจ้าของบล็อกจึงต้องมองหาวิธีอื่นๆที่สามารถรับรู้ได้ว่า “เฮ้ย หน้าจอมีการหมุนนะ”
และวิธีที่เลือกใช้ก็คือเช็ค Screen Orientation ตอน onStart แล้วเก็บค่าไว้ และเมื่อมีการหมุนหน้าจอ onStart
ก็จะถูกเรียกอีกครั้ง ก็จะดึงค่า Screen Orientation มาเทียบกับของเก่าว่ามีการเปลี่ยนแปลงหรือไม่ (เนื่องจาก onStart สามารถถูกเรียกได้ตลอดเวลา)
class MainActivity : AppCompatActivity() {
private var lastOrientation = 0
override fun onCreate(savedInstanceState: Bundle?) {
...
if (savedInstanceState == null) {
lastOrientation = resources.configuration.orientation
}
}
override fun onStart() {
...
checkOrientationChanged()
}
private fun checkOrientationChanged() {
val currentOrientation = resources.configuration.orientation
if (currentOrientation != lastOrientation) {
onScreenOrientationChanged(currentOrientation)
lastOrientation = currentOrientation
}
}
private fun onScreenOrientationChanged(currentOrientation: Int) {
// Do something here when screen orientation changed
}
}
และสำหรับค่า lastOrientation
ถ้าจะให้ดีที่สุดก็ควร Handle ใน Save/Restore State ด้วย ก็เลยเขียนเพิ่มเข้ามาอีกนิดหน่อย กลายเป็นแบบนี้
class MainActivity : AppCompatActivity() {
companion object {
private const val KEY_LAST_ORIENTATION = "last_orientation"
}
private var lastOrientation = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (savedInstanceState == null) {
lastOrientation = resources.configuration.orientation
}
}
override fun onStart() {
super.onStart()
checkOrientationChanged()
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
lastOrientation = savedInstanceState.getInt(KEY_LAST_ORIENTATION)
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt(KEY_LAST_ORIENTATION, lastOrientation)
}
private fun checkOrientationChanged() {
val currentOrientation = resources.configuration.orientation
if (currentOrientation != lastOrientation) {
onScreenOrientationChanged(currentOrientation)
lastOrientation = currentOrientation
}
}
private fun onScreenOrientationChanged(currentOrientation: Int) {
// Do something here when screen orientation changed
}
}
เท่านี้ก็เรียบร้อย~ สามารถรับรู้ได้ว่ามีการหมุนหน้าจอได้จาก Method ที่ชื่อว่า onScreenOrientationChanged
ไหนๆก็มาถึงจุดนี้แล้ว ทำเป็น Library ไปเลยละกัน
จากโค้ดดังกล่าวจึงทำให้เจ้าของบล็อกรวบคำสั่งแล้วทำเป็น Library ไว้ใช้งานไปเลย จะได้เรียกใช้งานสะดวกๆหน่อย ก็เลยทำเป็น Library ที่ชื่อว่า ScreenOrientationHelper ซะเลย
วิธีใช้งาน Screen Orientation Helper
เพิ่ม Dependency ของ Library ตัวนี้ไว้ใน build.gradle
ดังนี้
implementation 'com.akexorcist:screenorientationhelper:1.0.0'
เวลาจะเรียกใช้งานให้สร้าง Base Activity ขึ้นมาครับ เพราะผมเขียนโค้ดไว้ให้มันสามารถนำไป Imprement ใส่ Activity ใดๆก็ได้ตามที่ต้องการ
ยกตัวอย่างเช่น Activity ที่ผู้ที่หลงเข้ามาอ่านใช้งานอยู่คือ AppCompatActivity ดังนั้นก็ให้สร้าง Base Activity ขึ้นมาดังนี้
abstract class BaseActivity: AppCompatActivity() { ... }
แล้วเพิ่มคำสั่งของ ScreenOrientationHelper เข้าไปดังนี้
abstract class BaseActivity : AppCompatActivity() {
private val helper = ScreenOrientationHelper(this)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
helper.onCreate(savedInstanceState)
helper.setScreenOrientationChangeListener(this)
}
override fun onStart() {
super.onStart()
helper.onStart()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
helper.onSaveInstanceState(outState)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
helper.onRestoreInstanceState(savedInstanceState)
}
open fun onScreenOrientationChanged(orientation: Int) { }
}
โค้ดส่วนนี้เป็น Boilerpate Code ครับ ส่วนหนึ่งเพราะต้องการให้เอาไปใช้กับ Activity ตัวไหนก็ได้นั่นเอง ก็ให้สร้างเป็น Base Activity แบบนี้แล้วเรียกใช้ใน Activity ตัวอื่นๆตามต้องการละกันเนอะ
เวลาเอาไปใช้งานจริงๆก็จะเป็นแบบนี้
class MyActivity: BaseActivity() {
override fun onScreenOrientationChanged(orientation: Int) {
super.onScreenOrientationChanged(orientation)
}
}
แค่ Override Method ที่ชื่อว่า onScreenOrientationChanged
ก็จะสามารถดัก Event เมื่อผู้ใช้มีการหมุนหน้าจอได้แล้ววววววววววววว
สรุป
เนื่องจากเจ้าของบล็อกเห็นว่ายังมีนักพัฒนาหลายๆคนที่ใช้วิธีที่ไม่เหมาะสมและอาจจะส่งผลกระทบต่อการทำงานบางอย่างของแอป จึงมองหาวิธีอื่นที่ดีกว่ามาทดแทน แต่พอๆเขียนไปซักพักก็รู้สึกว่าน่าจะจับมาทำเป็น Library เลยดีกว่า ก็เลยออกมาเป็นแบบนี้นี่แหละ
สำหรับ Library ของ ScreenOrientationHelper สามารถเข้าไปดูโค้ดที่เจ้าของบล็อกเขียนไว้ได้ที่ Screen Orientation Helper [GitHub]