แอนดรอยด์มีส่วนผสมทั้งหมดสำหรับแอปหน้าจอขนาดใหญ่ 5 ดาว สูตรอาหารในตำราอาหารนี้จะเลือกและรวมส่วนผสมที่มีให้เลือกเพื่อแก้ปัญหาการพัฒนาบางอย่าง แต่ละสูตรจะมีแนวทางปฏิบัติแนะนำ ตัวอย่างโค้ดคุณภาพ และวิธีการแบบทีละขั้นตอนเพื่อช่วยให้คุณได้เป็นเชฟยอดนิยมบนหน้าจอขนาดใหญ่
การให้ดาว
สูตรอาหารดังกล่าวจะให้คะแนนดาวตามการจัดประเภทว่าเป็นไปตามหลักเกณฑ์ด้านคุณภาพสำหรับแอปที่มีหน้าจอขนาดใหญ่มากน้อยเพียงใด
![]() |
ตรงตามเกณฑ์สำหรับระดับที่ 1 ความแตกต่างของหน้าจอขนาดใหญ่ |
![]() |
ตรงตามเกณฑ์สำหรับระดับ 2 ที่เพิ่มประสิทธิภาพหน้าจอขนาดใหญ่ |
![]() |
ตรงตามเกณฑ์สำหรับระดับ 3, หน้าจอขนาดใหญ่พร้อมใช้งาน |
![]() |
มีความสามารถบนหน้าจอขนาดใหญ่บางส่วน แต่ไม่เป็นไปตามหลักเกณฑ์ด้านคุณภาพของแอปที่มีหน้าจอขนาดใหญ่ |
![]() |
ตรงตามความต้องการของ Use Case เฉพาะ แต่ไม่รองรับหน้าจอขนาดใหญ่อย่างถูกต้อง |
การรองรับกล้องของ Chromebook
ทำให้ผู้ใช้ Chromebook ค้นพบธุรกิจของคุณใน Google Play
หากแอปกล้องถ่ายรูปทำงานได้กับฟีเจอร์กล้องพื้นฐานเท่านั้น อย่าให้ร้านค้าแอปป้องกันไม่ให้ผู้ใช้ Chromebook ติดตั้งแอปเพียงเพราะคุณระบุฟีเจอร์กล้องขั้นสูงที่พบในโทรศัพท์ระดับไฮเอนด์โดยไม่ได้ตั้งใจ
Chromebook มีกล้องหน้าในตัว (สำหรับให้ผู้ใช้ใช้งาน) ซึ่งเหมาะสำหรับการประชุมทางวิดีโอ สแนปชอต และแอปพลิเคชันอื่นๆ แต่ Chromebook บางรุ่นไม่มีกล้องหลัง (หน้าจอ) และกล้องส่วนใหญ่ใน Chromebook ไม่สนับสนุนการโฟกัสอัตโนมัติหรือแฟลช
แนวทางปฏิบัติแนะนำ
แอปกล้องอเนกประสงค์รองรับอุปกรณ์ทุกชนิด ไม่ว่าจะกำหนดค่ากล้องไว้อย่างไร อุปกรณ์ที่มีกล้องหน้า กล้องหลัง กล้องภายนอกที่เชื่อมต่อด้วย USB
โปรดประกาศฟีเจอร์กล้องทั้งหมดที่แอปใช้เสมอ และระบุอย่างชัดเจนว่าต้องใช้ฟีเจอร์ดังกล่าวหรือไม่ เพื่อให้แน่ใจว่า App Store จะทำให้แอปพร้อมให้บริการสำหรับอุปกรณ์จำนวนมากที่สุด
ส่วนผสม
- สิทธิ์
CAMERA
: ให้สิทธิ์แอปของคุณเข้าถึงกล้องของอุปกรณ์ - องค์ประกอบไฟล์ Manifest
<uses-feature>
: แจ้ง App Store ของฟีเจอร์ที่แอปของคุณใช้ - แอตทริบิวต์
required
: บอกให้ App Store ทราบว่าแอปทํางานได้โดยไม่ต้องมีฟีเจอร์ที่ระบุไว้หรือไม่
จำนวนก้าว
สรุป
ประกาศสิทธิ์ CAMERA
ประกาศฟีเจอร์ของกล้องที่ให้การสนับสนุนกล้องขั้นพื้นฐาน ระบุว่าจำเป็นต้องใช้แต่ละฟีเจอร์หรือไม่
1. ประกาศสิทธิ์ CAMERA
เพิ่มสิทธิ์ต่อไปนี้ลงในไฟล์ Manifest ของแอป
<uses-permission android:name="android.permission.CAMERA" />
2. ประกาศฟีเจอร์พื้นฐานของกล้อง
เพิ่มฟีเจอร์ต่อไปนี้ลงในไฟล์ Manifest ของแอป
<uses-feature android:name="android.hardware.camera.any" android:required="false" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<uses-feature android:name="android.hardware.camera.flash" android:required="false" />
3. ระบุว่าจำเป็นต้องใช้แต่ละฟีเจอร์หรือไม่
ตั้งค่า android:required="false"
สำหรับฟีเจอร์ android.hardware.camera.any
เพื่อเปิดใช้การเข้าถึงแอปของคุณโดยอุปกรณ์ที่มีกล้องในตัวหรือกล้องภายนอกทุกประเภท หรือไม่มีกล้องเลย
สำหรับฟีเจอร์อื่นๆ ให้ตั้งค่า android:required="false"
เพื่อให้อุปกรณ์ เช่น Chromebook ที่ไม่มีกล้องหลัง ระบบโฟกัสอัตโนมัติ หรือ Flash เข้าถึงแอปของคุณใน App Store ได้
ผลลัพธ์
ผู้ใช้ Chromebook จะดาวน์โหลดและติดตั้งแอปของคุณจาก Google Play และ App Store อื่นๆ ได้ และอุปกรณ์ที่รองรับกล้องเต็มรูปแบบ เช่น โทรศัพท์ จะไม่ถูกจำกัดในฟังก์ชันการทำงานของกล้อง
การตั้งค่าฟีเจอร์ของกล้องที่แอปของคุณรองรับและระบุฟีเจอร์ที่แอปต้องการอย่างชัดแจ้งจะทำให้แอปพร้อมใช้งานในอุปกรณ์จำนวนมากที่สุดเท่าที่จะเป็นไปได้
แหล่งข้อมูลเพิ่มเติม
โปรดดูข้อมูลเพิ่มเติมที่หัวข้อฟีเจอร์ของฮาร์ดแวร์ของกล้องในเอกสารประกอบของ <uses-feature>
การวางแนวของแอปบนโทรศัพท์ถูกจำกัด แต่ไม่รวมถึงอุปกรณ์ที่มีหน้าจอขนาดใหญ่
แอปของคุณทำงานได้ดีบนโทรศัพท์ในแนวตั้ง คุณจึงจำกัดแอปให้ใช้งานได้เฉพาะแนวตั้งเท่านั้น แต่คุณมองเห็นโอกาสที่จะทำสิ่งต่างๆ ได้มากขึ้นบนหน้าจอขนาดใหญ่ในแนวนอน
คุณจะใช้ทั้ง 2 วิธีอย่างไร ซึ่งก็คือการจำกัดแอปให้อยู่ในแนวตั้งบนหน้าจอขนาดเล็ก แต่เปิดใช้แนวนอนในอุปกรณ์ขนาดใหญ่
แนวทางปฏิบัติแนะนำ
แอปที่ดีที่สุดจะเป็นไปตามค่ากำหนดของผู้ใช้ เช่น การวางแนวของอุปกรณ์
หลักเกณฑ์ด้านคุณภาพสำหรับแอปที่มีหน้าจอขนาดใหญ่แนะนำให้แอปรองรับการกำหนดค่าอุปกรณ์ทั้งหมด รวมถึงการวางแนวตั้งและแนวนอน โหมดหลายหน้าต่าง และสถานะของอุปกรณ์พับได้ แอปควรเพิ่มประสิทธิภาพเลย์เอาต์และอินเทอร์เฟซผู้ใช้เพื่อการกำหนดค่าแบบต่างๆ และแอปควรบันทึกและคืนค่าสถานะระหว่างการเปลี่ยนแปลงการกำหนดค่า
สูตรอาหารนี้เป็นมาตรการชั่วคราว การรองรับหน้าจอขนาดใหญ่เล็กน้อย ใช้สูตรจนกว่าคุณจะปรับปรุงแอปเพื่อให้รองรับการกำหนดค่าอุปกรณ์ทั้งหมดอย่างเต็มรูปแบบ
ส่วนผสม
screenOrientation
: การตั้งค่าไฟล์ Manifest ของแอปที่ช่วยให้คุณระบุวิธีที่แอปตอบสนองต่อการเปลี่ยนการวางแนวอุปกรณ์ได้- Jetpack WindowManager: ชุดไลบรารีที่ช่วยให้คุณกำหนดขนาดและสัดส่วนภาพของหน้าต่างแอปได้ เข้ากันได้กับ API ระดับ 14 แบบย้อนหลัง
Activity#setRequestedOrientation()
: วิธีการที่คุณเปลี่ยนการวางแนวของแอปขณะรันไทม์
จำนวนก้าว
สรุป
เปิดใช้แอปเพื่อจัดการกับการเปลี่ยนการวางแนวโดยค่าเริ่มต้นในไฟล์ Manifest ของแอป ระบุขนาดหน้าต่างแอปขณะรันไทม์ หากหน้าต่างแอปมีขนาดเล็ก ให้จำกัดการวางแนวของแอปโดยลบล้างการตั้งค่าการวางแนวของไฟล์ Manifest
1. ระบุการตั้งค่าการวางแนวในไฟล์ Manifest ของแอป
คุณจะหลีกเลี่ยงการประกาศองค์ประกอบ screenOrientation
ของไฟล์ Manifest ของแอป (ในกรณีที่การวางแนวมีค่าเริ่มต้นเป็น unspecified
) หรือตั้งการวางแนวหน้าจอเป็น fullUser
ก็ได้ หากผู้ใช้ไม่ได้ล็อกการหมุนตามเซ็นเซอร์ แอปจะรองรับการวางแนวอุปกรณ์ทุกแบบ
<activity
android:name=".MyActivity"
android:screenOrientation="fullUser">
ข้อแตกต่างระหว่างการใช้ unspecified
กับ fullUser
นั้นมีนัยสำคัญแต่ก็สำคัญ หากคุณไม่ได้ประกาศค่า screenOrientation
ระบบจะเลือกการวางแนวและนโยบายที่ระบบใช้เพื่อกำหนดการวางแนวอาจแตกต่างกันไปในแต่ละอุปกรณ์ ในทางกลับกัน การระบุ fullUser
จะตรงกับพฤติกรรมที่ผู้ใช้กำหนดไว้สำหรับอุปกรณ์มากยิ่งขึ้น หากผู้ใช้ล็อกการหมุนตามเซ็นเซอร์ไว้ แอปก็จะทำตามค่ากำหนดของผู้ใช้ มิฉะนั้น ระบบจะอนุญาตให้ใช้การวางแนวหน้าจอในสี่ระดับที่เป็นไปได้ (แนวตั้ง แนวนอน กลับแนวตั้ง หรือกลับด้านแนวนอน) โปรดดู android:screenOrientation
2. กำหนดขนาดหน้าจอ
เมื่อตั้งค่าไฟล์ Manifest ให้รองรับการวางแนวทุกรูปแบบที่ผู้ใช้อนุญาต คุณจะระบุการวางแนวของแอปแบบเป็นโปรแกรมตามขนาดหน้าจอได้
เพิ่มไลบรารี Jetpack WindowManager ลงในไฟล์ build.gradle
หรือ build.gradle.kts
ของโมดูลโดยทำดังนี้
Kotlin
implementation("androidx.window:window:version
") implementation("androidx.window:window-core:version
")
ดึงดูด
implementation 'androidx.window:window:version
' implementation 'androidx.window:window-core:version
'
ใช้เมธอด Jetpack WindowManager WindowMetricsCalculator#computeMaximumWindowMetrics()
เพื่อดูขนาดหน้าจอของอุปกรณ์เป็นออบเจ็กต์ WindowMetrics
เมตริกหน้าต่างสามารถนำไปเปรียบเทียบกับคลาสขนาดหน้าต่างเพื่อตัดสินใจว่าจะจำกัดการวางแนวเมื่อใด
คลาสขนาดของ Windows จะระบุเบรกพอยท์ระหว่างหน้าจอขนาดเล็กและหน้าจอขนาดใหญ่
ใช้เบรกพอยท์ WindowWidthSizeClass#COMPACT
และ WindowHeightSizeClass#COMPACT
เพื่อกำหนดขนาดหน้าจอ ดังนี้
Kotlin
/** Determines whether the device has a compact screen. **/ fun compactScreen() : Boolean { val metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this) val width = metrics.bounds.width() val height = metrics.bounds.height() val density = resources.displayMetrics.density val windowSizeClass = WindowSizeClass.compute(width/density, height/density) return windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.COMPACT || windowSizeClass.windowHeightSizeClass == WindowHeightSizeClass.COMPACT }
Java
/** Determines whether the device has a compact screen. **/ private boolean compactScreen() { WindowMetrics metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this); int width = metrics.getBounds().width(); int height = metrics.getBounds().height(); float density = getResources().getDisplayMetrics().density; WindowSizeClass windowSizeClass = WindowSizeClass.compute(width/density, height/density); return windowSizeClass.getWindowWidthSizeClass() == WindowWidthSizeClass.COMPACT || windowSizeClass.getWindowHeightSizeClass() == WindowHeightSizeClass.COMPACT; }
- หมายเหตุ:
- ตัวอย่างข้างต้นถูกนำมาใช้เป็นวิธีของกิจกรรม ดังนั้น กิจกรรมนี้จึงลดการอ้างอิงเป็น
this
ในอาร์กิวเมนต์ของcomputeMaximumWindowMetrics()
- ระบบใช้เมธอด
computeMaximumWindowMetrics()
แทนcomputeCurrentWindowMetrics()
เนื่องจากสามารถเปิดแอปได้ในโหมดหลายหน้าต่าง ซึ่งจะไม่สนใจการตั้งค่าการวางแนวหน้าจอ ไม่มีจุดใดในการกำหนดขนาดหน้าต่างแอปและลบล้างการตั้งค่าการวางแนว เว้นแต่หน้าต่างของแอปจะเป็นหน้าจออุปกรณ์ทั้งหน้าจอ
โปรดดู WindowManager สำหรับคำแนะนำเกี่ยวกับการประกาศทรัพยากร Dependency เพื่อทำให้เมธอด computeMaximumWindowMetrics()
พร้อมใช้งานในแอปของคุณ
3. ลบล้างการตั้งค่าไฟล์ Manifest ของแอป
เมื่อคุณพิจารณาแล้วว่าอุปกรณ์มีขนาดหน้าจอที่กะทัดรัด คุณสามารถเรียกใช้ Activity#setRequestedOrientation()
เพื่อลบล้างการตั้งค่า screenOrientation
ของไฟล์ Manifest ได้ ดังนี้
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) requestedOrientation = if (compactScreen()) ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else ActivityInfo.SCREEN_ORIENTATION_FULL_USER ... // Replace with a known container that you can safely add a // view to where the view won't affect the layout and the view // won't be replaced. val container: ViewGroup = binding.container // Add a utility view to the container to hook into // View.onConfigurationChanged. This is required for all // activities, even those that don't handle configuration // changes. You can't use Activity.onConfigurationChanged, // since there are situations where that won't be called when // the configuration changes. View.onConfigurationChanged is // called in those scenarios. container.addView(object : View(this) { override fun onConfigurationChanged(newConfig: Configuration?) { super.onConfigurationChanged(newConfig) requestedOrientation = if (compactScreen()) ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else ActivityInfo.SCREEN_ORIENTATION_FULL_USER } }) }
Java
@Override protected void onCreate(Bundle savedInstance) { super.onCreate(savedInstanceState); if (compactScreen()) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } else { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER); } ... // Replace with a known container that you can safely add a // view to where the view won't affect the layout and the view // won't be replaced. ViewGroup container = binding.container; // Add a utility view to the container to hook into // View.onConfigurationChanged. This is required for all // activities, even those that don't handle configuration // changes. You can't use Activity.onConfigurationChanged, // since there are situations where that won't be called when // the configuration changes. View.onConfigurationChanged is // called in those scenarios. container.addView(new View(this) { @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); if (compactScreen()) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } else { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER); } } }); }
การเพิ่มตรรกะลงในเมธอด onCreate()
และ View.onConfigurationChanged()
จะทำให้คุณสามารถรับเมตริกหน้าต่างสูงสุดและลบล้างการตั้งค่าการวางแนวได้ เมื่อใดก็ตามที่กิจกรรมดังกล่าวมีการปรับขนาดหรือย้ายไปมาระหว่างจอแสดงผล เช่น หลังการหมุนอุปกรณ์ หรือเมื่ออุปกรณ์แบบพับได้ถูกพับหรือกางออก
ดูข้อมูลเพิ่มเติมเกี่ยวกับเวลาที่เกิดการเปลี่ยนแปลงการกําหนดค่าและเวลาที่เกิดการเปลี่ยนแปลงกิจกรรมได้ที่หัวข้อจัดการการเปลี่ยนแปลงการกําหนดค่า
ผลลัพธ์
ตอนนี้แอปของคุณควรอยู่ในแนวตั้งบนหน้าจอขนาดเล็กโดยไม่คำนึงถึงการหมุนอุปกรณ์ บนหน้าจอขนาดใหญ่ แอปควรรองรับการวางทั้งแนวนอนและแนวตั้ง
แหล่งข้อมูลเพิ่มเติม
หากต้องการความช่วยเหลือในการอัปเกรดแอปให้รองรับการกำหนดค่าอุปกรณ์ทั้งหมดตลอดเวลา โปรดดูแหล่งข้อมูลต่อไปนี้
หยุดเล่นสื่อชั่วคราวและเล่นต่อด้วย Spacebar ของแป้นพิมพ์ภายนอก
การเพิ่มประสิทธิภาพหน้าจอขนาดใหญ่มีความสามารถในการจัดการ อินพุตแป้นพิมพ์ภายนอก เช่น การตอบสนองต่อ Spacebar ที่ถูกกด หยุดเล่นวิดีโอและสื่ออื่นๆ ชั่วคราวหรือเล่นต่อ วิธีนี้เหมาะอย่างยิ่งสำหรับแท็บเล็ต ซึ่งมักเชื่อมต่อกับแป้นพิมพ์ภายนอก และ Chromebook ซึ่งมักมาพร้อมกับแป้นพิมพ์ภายนอกแต่สามารถใช้ในโหมดแท็บเล็ตได้
เมื่อสื่อเป็นองค์ประกอบเดียว (เช่น การเล่นวิดีโอแบบเต็มหน้าจอ) ตอบสนองต่อเหตุการณ์การกดแป้น ที่ระดับกิจกรรมหรือใน Jetpack Compose ที่ระดับหน้าจอ
แนวทางปฏิบัติแนะนำ
ทุกครั้งที่แอปเล่นไฟล์สื่อ ผู้ใช้ควรหยุดชั่วคราวและกลับมาใช้งานต่อได้ โดยกด Spacebar บนแป้นพิมพ์จริง
ส่วนผสม
KEYCODE_SPACE
: ค่าคงที่ของโค้ดคีย์สำหรับ Spacebar
เขียน
onPreviewKeyEvent
:Modifier
ที่ช่วยให้คอมโพเนนต์สกัดกั้นเหตุการณ์สำคัญของฮาร์ดแวร์เมื่อโฟกัสอยู่ (หรือเหตุการณ์ย่อย 1 รายการ)onKeyEvent
: คล้ายกับonPreviewKeyEvent
โดยModifier
นี้จะช่วยให้คอมโพเนนต์สกัดกั้นเหตุการณ์สำคัญของฮาร์ดแวร์เมื่อโฟกัสอยู่ (หรือองค์ประกอบย่อยของฮาร์ดแวร์)
ยอดดู
onKeyUp()
: มีการเรียกใช้เมื่อมีการปล่อยคีย์และไม่ได้จัดการโดยมุมมองภายในกิจกรรม
จำนวนก้าว
สรุป
แอปที่อิงตามการดูและแอปที่อิงตาม Jetpack Compose จะตอบสนองต่อการกดแป้นบนแป้นพิมพ์ในลักษณะเดียวกัน กล่าวคือ แอปจะต้องคอยฟังเหตุการณ์การกดแป้น กรองเหตุการณ์ และตอบสนองต่อการกดแป้นที่เลือก เช่น การกดแป้น Spacebar
1. ฟังเหตุการณ์ของแป้นพิมพ์
ยอดดู
ในกิจกรรมในแอป ให้ลบล้างเมธอด onKeyUp()
ดังนี้
Kotlin
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean { ... }
Java
@Override public boolean onKeyUp(int keyCode, KeyEvent event) { ... }
จะมีการเรียกใช้เมธอดนี้ทุกครั้งที่มีการปล่อยแป้นกด จึงจะเริ่มทำงานหนึ่งครั้งต่อการกดแป้นพิมพ์ทุกครั้ง
เขียน
เมื่อใช้ Jetpack Compose คุณจะใช้ประโยชน์จากแป้นกดร่วม onPreviewKeyEvent
หรือ onKeyEvent
ภายในหน้าจอที่จัดการการกดแป้นพิมพ์ได้
Column(modifier = Modifier.onPreviewKeyEvent { event ->
if (event.type == KeyEventType.KeyUp) {
...
}
...
})
หรือ
Column(modifier = Modifier.onKeyEvent { event ->
if (event.type == KeyEventType.KeyUp) {
...
}
...
})
2. การกด Spacebar ตัวกรอง
ภายในเมธอด onKeyUp()
หรือเมธอดเขียน onPreviewKeyEvent
และ onKeyEvent
ให้กรองหา KeyEvent.KEYCODE_SPACE
เพื่อส่งเหตุการณ์ที่ถูกต้องไปยังคอมโพเนนต์สื่อ
ยอดดู
Kotlin
if (keyCode == KeyEvent.KEYCODE_SPACE) { togglePlayback() return true } return false
Java
if (keyCode == KeyEvent.KEYCODE_SPACE) { togglePlayback(); return true; } return false;
เขียน
Column(modifier = Modifier.onPreviewKeyEvent { event ->
if (event.type == KeyEventType.KeyUp && event.key == Key.Spacebar) {
...
}
...
})
หรือ
Column(modifier = Modifier.onKeyEvent { event ->
if (event.type == KeyEventType.KeyUp && event.key == Key.Spacebar) {
...
}
...
})
ผลลัพธ์
ตอนนี้แอปของคุณสามารถตอบสนองต่อการกดแป้น Spacebar เพื่อหยุดชั่วคราวและเล่นวิดีโอหรือสื่ออื่นๆ ต่อได้แล้ว
แหล่งข้อมูลเพิ่มเติม
ดูข้อมูลเพิ่มเติมเกี่ยวกับเหตุการณ์ของแป้นพิมพ์และวิธีจัดการได้ที่จัดการแป้นพิมพ์ อินพุต
การปฏิเสธฝ่ามือที่ใช้สไตลัส
สไตลัสสามารถเป็นเครื่องมือที่สร้างสรรค์และทำงานได้อย่างมีประสิทธิภาพเป็นพิเศษบนหน้าจอขนาดใหญ่ แต่เมื่อผู้ใช้วาด เขียน หรือโต้ตอบกับแอปโดยใช้สไตลัส บางครั้งผู้ใช้ก็แตะหน้าจอด้วยฝ่ามือ คุณสามารถรายงานเหตุการณ์การสัมผัสไปยังแอปของคุณก่อนที่ระบบจะจดจําและปิดเหตุการณ์นั้นเป็นการแตะฝ่ามือโดยไม่ตั้งใจ
แนวทางปฏิบัติแนะนำ
แอปของคุณต้องระบุกิจกรรมการสัมผัสที่เกินมาและละเว้นกิจกรรมดังกล่าว Android ยกเลิกการสัมผัสฝ่ามือด้วยการส่งวัตถุ MotionEvent
ตรวจสอบวัตถุเพื่อหา ACTION_CANCEL
หรือ ACTION_POINTER_UP
และ FLAG_CANCELED
เพื่อดูว่าจะปฏิเสธท่าทางสัมผัสที่เกิดจากการแตะฝ่ามือหรือไม่
ส่วนผสม
MotionEvent
: หมายถึงเหตุการณ์การแตะและการเคลื่อนไหว มีข้อมูลที่จำเป็นต่อการพิจารณาว่าควรละเว้นเหตุการณ์หรือไม่OnTouchListener#onTouch()
: รับออบเจ็กต์MotionEvent
รายการMotionEvent#getActionMasked()
: แสดงผลการทำงานที่เชื่อมโยงกับเหตุการณ์การเคลื่อนไหวACTION_CANCEL
: ค่าคงที่MotionEvent
ที่บ่งชี้ว่าท่าทางสัมผัสควรเลิกทำACTION_POINTER_UP
: ค่าคงที่MotionEvent
ที่บ่งชี้ว่ามีตัวชี้ที่ไม่ใช่ตัวชี้แรกขึ้น (กล่าวคือ ยกเลิกการสัมผัสหน้าจอของอุปกรณ์แล้ว)FLAG_CANCELED
: ค่าคงที่MotionEvent
ที่บ่งชี้ว่าตัวชี้เลื่อนขึ้นทำให้เกิดการแตะโดยไม่ได้ตั้งใจ เพิ่มลงในACTION_POINTER_UP
และACTION_CANCEL
เหตุการณ์ใน Android 13 (API ระดับ 33) ขึ้นไปแล้ว
จำนวนก้าว
สรุป
ตรวจสอบออบเจ็กต์ MotionEvent
รายการที่ส่งไปยังแอป ใช้ MotionEvent
API เพื่อกำหนดลักษณะของเหตุการณ์ ดังนี้
- เหตุการณ์ที่มีจุดเดียว — ตรวจสอบเพื่อหา
ACTION_CANCEL
ใน Android 13 ขึ้นไป ให้ตรวจสอบFLAG_CANCELED
ด้วย - เหตุการณ์ที่มีหลายจุด — ใน Android 13 ขึ้นไป ให้ตรวจสอบ
ACTION_POINTER_UP
และFLAG_CANCELED
ตอบกลับกิจกรรม ACTION_CANCEL
และ ACTION_POINTER_UP
/FLAG_CANCELED
1. รับออบเจ็กต์เหตุการณ์การเคลื่อนไหว
เพิ่ม OnTouchListener
ลงในแอป
Kotlin
val myView = findViewById<View>(R.id.myView).apply { setOnTouchListener { view, event -> // Process motion event. } }
Java
View myView = findViewById(R.id.myView); myView.setOnTouchListener( (view, event) -> { // Process motion event. });
2. ระบุการดำเนินการและการแจ้งเหตุการณ์
ตรวจหา ACTION_CANCEL
ซึ่งระบุเหตุการณ์ตัวชี้เดียวใน API ทุกระดับ ใน Android 13 ขึ้นไป ให้ตรวจสอบ ACTION_POINTER_UP
เพื่อหา FLAG_CANCELED.
Kotlin
val myView = findViewById<View>(R.id.myView).apply { setOnTouchListener { view, event -> when (event.actionMasked) { MotionEvent.ACTION_CANCEL -> { //Process canceled single-pointer motion event for all SDK versions. } MotionEvent.ACTION_POINTER_UP -> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && (event.flags and MotionEvent.FLAG_CANCELED) == MotionEvent.FLAG_CANCELED) { //Process canceled multi-pointer motion event for Android 13 and higher. } } } true } }
Java
View myView = findViewById(R.id.myView); myView.setOnTouchListener( (view, event) -> { switch (event.getActionMasked()) { case MotionEvent.ACTION_CANCEL: // Process canceled single-pointer motion event for all SDK versions. case MotionEvent.ACTION_UP: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && (event.getFlags() & MotionEvent.FLAG_CANCELED) == MotionEvent.FLAG_CANCELED) { //Process canceled multi-pointer motion event for Android 13 and higher. } } return true; });
3. เลิกทำท่าทางสัมผัส
เมื่อระบุการสัมผัสด้วยฝ่ามือแล้ว คุณสามารถยกเลิกเอฟเฟกต์บนหน้าจอของท่าทางสัมผัสได้
แอปของคุณต้องเก็บประวัติการดำเนินการของผู้ใช้ไว้ เพื่อให้เลิกทำการป้อนข้อมูลที่ไม่ได้ตั้งใจ เช่น การสัมผัสฝ่ามือได้ โปรดดูตัวอย่างใช้แอปวาดภาพพื้นฐานใน Codelab ปรับปรุงการรองรับสไตลัสในแอป Android
ผลลัพธ์
ตอนนี้แอปสามารถระบุและปฏิเสธการสัมผัสแบบใช้ฝ่ามือสำหรับกิจกรรมที่มีหลายตัวชี้ใน Android 13 ขึ้นไปในระดับ API และสำหรับกิจกรรมแบบใช้จุดเดียวในทุกระดับ API
แหล่งข้อมูลเพิ่มเติม
โปรดดูข้อมูลต่อไปนี้สำหรับข้อมูลเพิ่มเติม
- ฟีเจอร์และ API ของ Android 13 - ปรับปรุงการปฏิเสธต้นปาล์ม
- คู่มือนักพัฒนาซอฟต์แวร์
- Codelab — ปรับปรุงการรองรับสไตลัสในแอป Android
การจัดการสถานะ WebView
WebView
เป็นคอมโพเนนต์ที่ใช้กันโดยทั่วไปที่มีระบบขั้นสูงสำหรับการจัดการรัฐ WebView
ต้องคงสถานะไว้และตำแหน่งการเลื่อนในการเปลี่ยนแปลงการกำหนดค่า WebView
อาจสูญเสียตำแหน่งการเลื่อนเมื่อผู้ใช้หมุนอุปกรณ์หรือกางโทรศัพท์แบบพับได้ ซึ่งบังคับให้ผู้ใช้ต้องเลื่อนอีกครั้งจากด้านบนของ WebView
ไปยังตำแหน่งการเลื่อนก่อนหน้า
แนวทางปฏิบัติแนะนำ
ลดจำนวนครั้งที่มีการสร้าง WebView
อีกครั้ง WebView
มีความสามารถในการจัดการสถานะดี และคุณสามารถใช้ประโยชน์จากคุณภาพนี้ได้ด้วยการจัดการการเปลี่ยนแปลงการกำหนดค่าให้ได้มากที่สุด แอปของคุณต้องจัดการการเปลี่ยนแปลงการกำหนดค่าเนื่องจากการทำซ้ำ Activity
(วิธีจัดการการเปลี่ยนแปลงการกำหนดค่าของระบบ) จะสร้าง WebView
ขึ้นใหม่ด้วย ซึ่งทำให้ WebView
สูญเสียสถานะดังกล่าว
ส่วนผสม
android:configChanges
: แอตทริบิวต์ขององค์ประกอบ<activity>
ของไฟล์ Manifest แสดงรายการการเปลี่ยนแปลงการกำหนดค่าที่จัดการโดยกิจกรรมView#invalidate()
: วิธีที่ทําให้เกิดมุมมองใหม่ รับค่าโดยWebView
จำนวนก้าว
สรุป
หากต้องการบันทึกสถานะ WebView
ให้หลีกเลี่ยงการสร้าง Activity
ใหม่ให้ได้มากที่สุด แล้วปล่อยให้ WebView
เป็นโมฆะเพื่อให้ปรับขนาดในขณะที่รักษาสถานะไว้ได้
1. เพิ่มการเปลี่ยนแปลงการกำหนดค่าลงในไฟล์ AndroidManifest.xml
ของแอป
หลีกเลี่ยงการสร้างกิจกรรมใหม่โดยการระบุการเปลี่ยนแปลงการกำหนดค่าที่แอปของคุณจัดการ (ไม่ใช่โดยระบบ) ดังนี้
<activity
android:name=".MyActivity"
android:configChanges="screenLayout|orientation|screenSize
|keyboard|keyboardHidden|smallestScreenSize" />
2. ทำให้ WebView
เป็นโมฆะทุกครั้งที่แอปได้รับการเปลี่ยนแปลงการกำหนดค่า
Kotlin
override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) webView.invalidate() }
Java
@Override public void onConfigurationChanged(@NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); webview.invalidate(); }
ขั้นตอนนี้ใช้ได้กับระบบมุมมองเท่านั้น เนื่องจาก Jetpack Compose ไม่จำเป็นต้องทำให้สิ่งใดใช้งานไม่ได้เพื่อปรับขนาดองค์ประกอบ Composable
อย่างถูกต้อง อย่างไรก็ตาม Compose จะสร้าง WebView
ใหม่บ่อยครั้งหากจัดการไม่ถูกต้อง ใช้ Wrapper ของ Accompanist WebView เพื่อบันทึกและคืนค่าสถานะ WebView
ในแอปเขียน
ผลลัพธ์
ตอนนี้คอมโพเนนต์ WebView
ของแอปจะยังคงมีสถานะและตำแหน่งการเลื่อนเมื่อมีการเปลี่ยนแปลงการกำหนดค่าหลายรายการ ตั้งแต่การปรับขนาด การวางแนว ไปจนถึงการพับและการกางออก
แหล่งข้อมูลเพิ่มเติม
ดูข้อมูลเพิ่มเติมเกี่ยวกับการเปลี่ยนแปลงการกําหนดค่าและวิธีจัดการได้ที่จัดการการเปลี่ยนแปลงการกําหนดค่า
การจัดการสถานะ RecyclerView
RecyclerView
สามารถแสดงข้อมูลจำนวนมากโดยใช้ทรัพยากรด้านกราฟิกน้อยที่สุด ในฐานะ RecyclerView
เลื่อนดูรายการต่างๆ RecyclerView
จะใช้อินสแตนซ์ของ View
ที่เลื่อนออกไปจากหน้าจอเพื่อสร้างรายการใหม่ในขณะที่เลื่อนดูบนหน้าจอ แต่การเปลี่ยนแปลงการกำหนดค่า เช่น การหมุนอุปกรณ์ อาจรีเซ็ตสถานะของ RecyclerView
ซึ่งบังคับให้ผู้ใช้เลื่อนไปยังตำแหน่งก่อนหน้าในรายการของ RecyclerView
แนวทางปฏิบัติแนะนำ
RecyclerView
ควรคงสถานะไว้โดยเฉพาะ โดยเฉพาะตำแหน่งการเลื่อน และสถานะขององค์ประกอบรายการระหว่างการเปลี่ยนแปลงการกำหนดค่าทั้งหมด
ส่วนผสม
RecyclerView.Adapter#setStateRestorationPolicy()
: ระบุวิธีที่RecyclerView.Adapter
จะกู้คืนสถานะหลังจากการเปลี่ยนแปลงการกําหนดค่าViewModel
: คงสถานะสำหรับกิจกรรมหรือส่วนย่อย
จำนวนก้าว
สรุป
ตั้งค่านโยบายการกู้คืนสถานะของ RecyclerView.Adapter
เพื่อบันทึกตำแหน่งการเลื่อน RecyclerView
บันทึกสถานะของรายการ RecyclerView
รายการ เพิ่มสถานะของรายการสินค้าลงในอะแดปเตอร์ RecyclerView
และคืนค่าสถานะของรายการเมื่อผูกกับ ViewHolder
1. เปิดใช้นโยบายการกู้คืนสถานะ Adapter
เปิดใช้นโยบายการกู้คืนสถานะของอะแดปเตอร์ RecyclerView
เพื่อรักษาตำแหน่งการเลื่อนของ RecyclerView
ในการเปลี่ยนแปลงการกำหนดค่า เพิ่มข้อกำหนดของนโยบายลงในตัวสร้างอะแดปเตอร์ดังนี้
Kotlin
class MyAdapter() : RecyclerView.Adapter<RecyclerView.ViewHolder>() { init { stateRestorationPolicy = StateRestorationPolicy.PREVENT_WHEN_EMPTY } ... }
Java
class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { public Adapter() { setStateRestorationPolicy(StateRestorationPolicy.PREVENT_WHEN_EMPTY); } ... }
2. บันทึกสถานะของรายการการเก็บสถานะ
บันทึกสถานะของรายการ RecyclerView
ที่ซับซ้อน เช่น รายการที่มีองค์ประกอบ EditText
เช่น หากต้องการบันทึกสถานะของ EditText
ให้เพิ่ม Callback ที่คล้ายกับเครื่องจัดการ onClick
เพื่อบันทึกการเปลี่ยนแปลงข้อความ กำหนดข้อมูลที่จะบันทึกใน Callback ดังนี้
Kotlin
input.addTextChangedListener( afterTextChanged = { text -> text?.let { // Save state here. } } )
Java
input.addTextChangedListener(new TextWatcher() { ... @Override public void afterTextChanged(Editable s) { // Save state here. } });
ประกาศการติดต่อกลับใน Activity
หรือ Fragment
ใช้ ViewModel
เพื่อจัดเก็บรัฐ
3. เพิ่มสถานะรายการใน Adapter
เพิ่มสถานะของรายการต่างๆ ในRecyclerView.Adapter
ส่งสถานะรายการไปยังตัวสร้างอะแดปเตอร์เมื่อสร้างโฮสต์ Activity
หรือ Fragment
:
Kotlin
val adapter = MyAdapter(items, viewModel.retrieveState())
Java
MyAdapter adapter = new MyAdapter(items, viewModel.retrieveState());
4. กู้คืนสถานะรายการในViewHolder
ของอะแดปเตอร์
ใน RecyclerView.Adapter
เมื่อคุณเชื่อมโยง ViewHolder
กับรายการ ให้คืนค่าสถานะของรายการดังกล่าวดังนี้
Kotlin
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { ... val item = items[position] val state = states.firstOrNull { it.item == item } if (state != null) { holder.restore(state) } }
Java
@Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { ... Item item = items[position]; Arrays.stream(states).filter(state -> state.item == item) .findFirst() .ifPresent(state -> holder.restore(state)); }
ผลลัพธ์
ตอนนี้ RecyclerView
คืนค่าตำแหน่งการเลื่อนและสถานะของแต่ละรายการใน RecyclerView
ได้แล้ว
แหล่งข้อมูลเพิ่มเติม
การจัดการแป้นพิมพ์ที่ถอดออกได้
การรองรับแป้นพิมพ์แบบถอดได้ช่วยเพิ่มประสิทธิภาพการทำงานของผู้ใช้ในอุปกรณ์ขนาดใหญ่
อุปกรณ์หน้าจออื่นๆ Android จะทริกเกอร์การเปลี่ยนแปลงการกำหนดค่าทุกครั้งที่แป้นพิมพ์
แนบหรือถอดออกจากอุปกรณ์ ซึ่งอาจทำให้สูญเสียสถานะ UI บัญชี
สามารถบันทึกและคืนค่าสถานะแอปเพื่อให้ระบบจัดการ
การสร้างกิจกรรมใหม่ หรือจำกัดการสร้างกิจกรรมใหม่สำหรับการเปลี่ยนแปลงการกำหนดค่าแป้นพิมพ์
ในทุกกรณี ข้อมูลทั้งหมดที่เกี่ยวข้องกับแป้นพิมพ์จะถูกเก็บไว้ใน
Configuration
keyboard
และ
ช่อง keyboardHidden
ของออบเจ็กต์การกำหนดค่ามีข้อมูลเกี่ยวกับประเภท
แป้นพิมพ์และความพร้อมใช้งาน
แนวทางปฏิบัติแนะนำ
แอปที่ปรับให้เหมาะกับหน้าจอขนาดใหญ่รองรับอุปกรณ์อินพุตทุกประเภทตั้งแต่ แป้นพิมพ์ซอฟต์แวร์และฮาร์ดแวร์กับสไตลัส เมาส์ แทร็กแพด และอุปกรณ์ต่อพ่วงอื่นๆ อุปกรณ์
การรองรับแป้นพิมพ์ภายนอกจะมีการเปลี่ยนแปลงการกำหนดค่า ซึ่งคุณสามารถ จัดการได้ 2 วิธี ได้แก่
- ปล่อยให้ระบบสร้างกิจกรรมที่กำลังทำงานอยู่ในขณะนี้อีกครั้ง โดยคุณมีหน้าที่จัดการสถานะของแอป
- จัดการการเปลี่ยนแปลงการกำหนดค่าด้วยตนเอง (ระบบจะไม่สร้างกิจกรรมใหม่) โดยทำดังนี้
- ประกาศค่าของการกำหนดค่าทั้งหมดที่เกี่ยวข้องกับแป้นพิมพ์
- สร้างเครื่องจัดการการเปลี่ยนแปลงการกำหนดค่า
แอปเพื่อการทำงาน ซึ่งมักต้องมีการควบคุม UI อย่างละเอียดสำหรับการป้อนข้อความ ข้อมูลอื่น ๆ ที่มีประโยชน์ที่คุณจะได้รับจากแนวทางการจัดการงานด้วยตัวเอง การเปลี่ยนแปลงการกำหนดค่า
ในกรณีพิเศษ คุณอาจต้องเปลี่ยน การจัดวางแอปเมื่อฮาร์ดแวร์ แป้นพิมพ์แนบหรือถอดออก เช่น เพื่อเพิ่มพื้นที่ว่างสำหรับเครื่องมือหรือ แก้ไขหน้าต่าง
เนื่องจากวิธีเดียวที่เชื่อถือได้ในการรับการเปลี่ยนแปลงการกำหนดค่าคือการลบล้าง
วิธี onConfigurationChanged()
ของข้อมูลพร็อพเพอร์ตี้ คุณสามารถเพิ่มข้อมูลพร็อพเพอร์ตี้ใหม่ได้
ของกิจกรรมบนแอป และตอบกลับใน onConfigurationChanged()
ของข้อมูลพร็อพเพอร์ตี้
ตัวแฮนเดิลกับการเปลี่ยนแปลงการกำหนดค่าที่เกิดจากแป้นพิมพ์ที่แนบหรือ
ถอดออกแล้ว
ส่วนผสม
android:configChanges
: แอตทริบิวต์ขององค์ประกอบ<activity>
ของไฟล์ Manifest ของแอป แจ้งระบบเกี่ยวกับการเปลี่ยนแปลงการกำหนดค่าที่แอปจัดการView#onConfigurationChanged()
: วิธีที่ตอบสนองต่อการแพร่พันธุ์ การกำหนดค่าแอป
จำนวนก้าว
สรุป
ประกาศแอตทริบิวต์ configChanges
และเพิ่มค่าที่เกี่ยวข้องกับแป้นพิมพ์ เพิ่ม
View
ไปยังลำดับชั้นการแสดงผลของกิจกรรมและรอรับการเปลี่ยนแปลงการกำหนดค่า
1. ประกาศแอตทริบิวต์ configChanges
อัปเดตองค์ประกอบ <activity>
ในไฟล์ Manifest ของแอปด้วยการเพิ่มค่า keyboard|keyboardHidden
ลงในรายการการเปลี่ยนแปลงการกำหนดค่าที่มีการจัดการอยู่แล้ว ดังนี้
<activity
…
android:configChanges="...|keyboard|keyboardHidden">
2. เพิ่มมุมมองว่างในลำดับชั้นการแสดงผล
ประกาศข้อมูลพร็อพเพอร์ตี้ใหม่และเพิ่มโค้ดเครื่องจัดการของคุณภายในเมธอด onConfigurationChanged()
ของมุมมอง ดังนี้
Kotlin
val v = object : View(this) { override fun onConfigurationChanged(newConfig: Configuration?) { super.onConfigurationChanged(newConfig) // Handler code here. } }
Java
View v = new View(this) { @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // Handler code here. } };
ผลลัพธ์
ตอนนี้แอปจะตอบสนองต่อแป้นพิมพ์ภายนอกที่แนบหรือถอดออกโดยไม่สร้างกิจกรรมที่ทำงานอยู่ในปัจจุบันใหม่
แหล่งข้อมูลเพิ่มเติม
หากต้องการดูวิธีบันทึกสถานะ UI ของแอประหว่างการเปลี่ยนแปลงการกำหนดค่า เช่น การแนบแป้นพิมพ์หรือการถอดออก โปรดดูบันทึกสถานะ UI
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- จัดการการเปลี่ยนแปลงการกำหนดค่า