Codelab นี้เป็นส่วนหนึ่งของหลักสูตร Android ขั้นสูงใน Kotlin คุณจะได้รับประโยชน์สูงสุดจากหลักสูตรนี้หากทำตาม Codelab ตามลำดับ แต่ไม่จำเป็นต้องทำ Codelab ของหลักสูตรทั้งหมดแสดงอยู่ในหน้า Landing Page ของ Codelab Android ขั้นสูงใน Kotlin
บทนำ
Android มีคลาสย่อยของ View มากมาย เช่น Button, TextView, EditText, ImageView, CheckBox หรือ RadioButton คุณสามารถใช้คลาสย่อยเหล่านี้เพื่อสร้าง UI ที่ช่วยให้ผู้ใช้โต้ตอบและแสดงข้อมูลในแอปได้ หากไม่มีViewคลาสย่อยใดที่ตรงกับความต้องการ คุณสามารถสร้างคลาสย่อยViewที่เรียกว่ามุมมอง ที่กำหนดเองได้
หากต้องการสร้างมุมมองที่กำหนดเอง คุณจะขยายViewคลาสย่อยที่มีอยู่ (เช่น Button หรือ EditText) หรือสร้างคลาสย่อยของ View เองก็ได้ การขยาย View โดยตรงช่วยให้คุณสร้างองค์ประกอบ UI แบบอินเทอร์แอกทีฟที่มีขนาดและรูปร่างใดก็ได้โดยการลบล้างเมธอด onDraw() สำหรับ View เพื่อวาด
หลังจากสร้างมุมมองที่กำหนดเองแล้ว คุณจะเพิ่มมุมมองดังกล่าวลงในเลย์เอาต์กิจกรรมได้ในลักษณะเดียวกับการเพิ่ม TextView หรือ Button
บทเรียนนี้จะแสดงวิธีสร้างมุมมองที่กำหนดเองตั้งแต่ต้นโดยการขยาย View
สิ่งที่คุณควรทราบอยู่แล้ว
- วิธีสร้างแอปที่มีกิจกรรมและเรียกใช้โดยใช้ Android Studio
สิ่งที่คุณจะได้เรียนรู้
- วิธีขยาย
Viewเพื่อสร้างมุมมองที่กำหนดเอง - วิธีการวาดมุมมองที่กำหนดเองซึ่งมีรูปร่างเป็นวงกลม
- วิธีใช้ Listener เพื่อจัดการการโต้ตอบของผู้ใช้กับมุมมองที่กำหนดเอง
- วิธีใช้มุมมองที่กำหนดเองในเลย์เอาต์
สิ่งที่คุณต้องดำเนินการ
แอป CustomFanController แสดงวิธีสร้างคลาสย่อยของมุมมองที่กำหนดเองโดยการขยายคลาส View โดยคลาสย่อยใหม่นี้เรียกว่า DialView
แอปจะแสดงองค์ประกอบ UI แบบวงกลมที่คล้ายกับการควบคุมพัดลมจริง โดยมีการตั้งค่าสำหรับปิด (0), ต่ำ (1), ปานกลาง (2) และสูง (3) เมื่อผู้ใช้แตะมุมมอง ตัวบ่งชี้การเลือกจะย้ายไปยังตำแหน่งถัดไป: 0-1-2-3 และกลับไปที่ 0 นอกจากนี้ หากเลือก 1 ขึ้นไป สีพื้นหลังของส่วนวงกลมของมุมมองจะเปลี่ยนจากสีเทาเป็นสีเขียว (แสดงว่าพัดลมเปิดอยู่)


View คือองค์ประกอบพื้นฐานของ UI ของแอป คลาส View มีคลาสย่อยจำนวนมากที่เรียกว่า UI Widget ซึ่งครอบคลุมความต้องการส่วนใหญ่ของอินเทอร์เฟซผู้ใช้ของแอป Android ทั่วไป
องค์ประกอบ UI เช่น Button และ TextView เป็นคลาสย่อยที่ขยายคลาส View คุณสามารถขยายคลาสย่อยViewใดคลาสย่อยหนึ่งต่อไปนี้เพื่อประหยัดเวลาและลดความยุ่งยากในการพัฒนาได้ มุมมองที่กำหนดเองจะรับลักษณะและลักษณะการทำงานของมุมมองหลัก และคุณสามารถลบล้างลักษณะการทำงานหรือลักษณะของลักษณะที่ต้องการเปลี่ยนได้ ตัวอย่างเช่น หากคุณขยาย EditText เพื่อสร้างมุมมองที่กำหนดเอง มุมมองจะทํางานเหมือนกับมุมมอง EditText แต่ก็ปรับแต่งให้แสดงปุ่ม X ที่ล้างข้อความจากช่องป้อนข้อความได้ด้วย
คุณสามารถขยายViewคลาสย่อยใดก็ได้ เช่น EditText เพื่อรับมุมมองที่กำหนดเอง เพียงเลือกมุมมองที่ใกล้เคียงกับสิ่งที่คุณต้องการทำให้มากที่สุด จากนั้นคุณจะใช้มุมมองที่กำหนดเองได้เหมือนกับViewคลาสย่อยอื่นๆ ในเลย์เอาต์อย่างน้อย 1 รายการเป็นองค์ประกอบ XML ที่มีแอตทริบิวต์
หากต้องการสร้างมุมมองที่กำหนดเองใหม่ตั้งแต่ต้น ให้ขยายคลาส View เอง โค้ดของคุณจะลบล้างเมธอด View เพื่อกำหนดลักษณะที่ปรากฏและฟังก์ชันการทำงานของมุมมอง สิ่งสำคัญในการสร้างมุมมองที่กำหนดเองคือคุณมีหน้าที่รับผิดชอบในการวาดองค์ประกอบ UI ทั้งหมดที่มีขนาดและรูปร่างใดก็ได้ลงบนหน้าจอ หากคุณสร้างคลาสย่อยของ View ที่มีอยู่ เช่น Button คลาสนั้นจะจัดการการวาดให้คุณ (คุณจะได้ดูข้อมูลเพิ่มเติมเกี่ยวกับการวาดใน Codelab นี้ในภายหลัง)
หากต้องการสร้างมุมมองที่กําหนดเอง ให้ทําตามขั้นตอนทั่วไปต่อไปนี้
- สร้างคลาสมุมมองที่กำหนดเองซึ่งขยาย
Viewหรือขยายคลาสย่อยView(เช่นButtonหรือEditText) - หากขยาย
Viewคลาสย่อยที่มีอยู่ ให้ลบล้างเฉพาะลักษณะการทำงานหรือลักษณะที่ปรากฏที่คุณต้องการเปลี่ยนแปลง - หากขยาย
Viewคลาส ให้วาดรูปร่างของมุมมองที่กำหนดเองและควบคุมลักษณะที่ปรากฏโดยการลบล้างเมธอดViewเช่นonDraw()และonMeasure()ในคลาสใหม่ - เพิ่มโค้ดเพื่อตอบสนองต่อการโต้ตอบของผู้ใช้ และวาดมุมมองที่กำหนดเองใหม่หากจำเป็น
- ใช้คลาสมุมมองที่กำหนดเองเป็นวิดเจ็ต UI ในเลย์เอาต์ XML ของกิจกรรม นอกจากนี้ คุณยังกำหนดแอตทริบิวต์ที่กำหนดเองสำหรับมุมมองได้ด้วย เพื่อให้ปรับแต่งมุมมองในเลย์เอาต์ต่างๆ ได้
ในงานนี้ คุณจะได้ทำสิ่งต่อไปนี้
- สร้างแอปที่มี
ImageViewเป็นตัวยึดตำแหน่งชั่วคราวสำหรับมุมมองที่กำหนดเอง - ขยาย
Viewเพื่อสร้างมุมมองที่กำหนดเอง - เริ่มต้นมุมมองที่กำหนดเองด้วยค่าการวาดและการระบายสี
ขั้นตอนที่ 1: สร้างแอปที่มีตัวยึดตำแหน่ง ImageView
- สร้างแอป Kotlin ที่มีชื่อ
CustomFanControllerโดยใช้เทมเพลตกิจกรรมเปล่า ตรวจสอบว่าชื่อแพ็กเกจเป็นcom.example.android.customfancontroller - เปิด
activity_main.xmlในแท็บข้อความเพื่อแก้ไขโค้ด XML - แทนที่
TextViewที่มีอยู่ด้วยโค้ดนี้ ข้อความนี้ทำหน้าที่เป็นป้ายกำกับในกิจกรรมสำหรับมุมมองที่กำหนดเอง
<TextView
android:id="@+id/customViewLabel"
android:textAppearance="@style/Base.TextAppearance.AppCompat.Display3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:textColor="@android:color/black"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginTop="24dp"
android:text="Fan Control"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>- เพิ่มองค์ประกอบ
ImageViewนี้ลงในเลย์เอาต์ นี่คือตัวยึดตำแหน่งสำหรับมุมมองที่กำหนดเองซึ่งคุณจะสร้างในโค้ดแล็บนี้
<ImageView
android:id="@+id/dialView"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="@android:color/darker_gray"
app:layout_constraintTop_toBottomOf="@+id/customViewLabel"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"/>- แยกทรัพยากรสตริงและมิติข้อมูลในองค์ประกอบ UI ทั้ง 2 รายการ
- คลิกแท็บออกแบบ เลย์เอาต์ควรมีลักษณะดังนี้

ขั้นตอนที่ 2 สร้างคลาสมุมมองที่กำหนดเอง
- สร้างคลาส Kotlin ใหม่ชื่อ
DialView - แก้ไขคำจำกัดความของคลาสเพื่อขยาย
Viewนำเข้าandroid.view.Viewเมื่อได้รับข้อความแจ้ง - คลิก
Viewแล้วคลิกหลอดไฟสีแดง เลือกเพิ่มตัวสร้าง Android View โดยใช้ '@JvmOverloads' Android Studio จะเพิ่มตัวสร้างจากคลาสViewคำอธิบายประกอบ@JvmOverloadsจะสั่งให้คอมไพเลอร์ Kotlin สร้างการโอเวอร์โหลดสำหรับฟังก์ชันนี้ซึ่งจะแทนที่ค่าพารามิเตอร์เริ่มต้น
class DialView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {- เหนือ
DialViewคำจำกัดความของคลาส ใต้การนำเข้า ให้เพิ่มenumระดับบนสุดเพื่อแสดงความเร็วพัดลมที่ใช้ได้ โปรดทราบว่าenumนี้เป็นประเภทIntเนื่องจากค่าเป็นทรัพยากรสตริง ไม่ใช่สตริงจริง Android Studio จะแสดงข้อผิดพลาดสำหรับทรัพยากรสตริงที่ขาดหายไปในแต่ละค่าเหล่านี้ โดยคุณจะแก้ไขได้ในขั้นตอนถัดไป
private enum class FanSpeed(val label: Int) {
OFF(R.string.fan_off),
LOW(R.string.fan_low),
MEDIUM(R.string.fan_medium),
HIGH(R.string.fan_high);
}- เพิ่มค่าคงที่ต่อไปนี้ใต้
enumคุณจะใช้ค่าเหล่านี้เป็นส่วนหนึ่งของการวาดตัวบ่งชี้และป้ายกำกับของมาตรวัด
private const val RADIUS_OFFSET_LABEL = 30
private const val RADIUS_OFFSET_INDICATOR = -35- ภายในคลาส
DialViewให้กําหนดตัวแปรหลายรายการที่คุณต้องการเพื่อวาดมุมมองที่กําหนดเอง นำเข้าandroid.graphics.PointFหากได้รับแจ้ง
private var radius = 0.0f // Radius of the circle.
private var fanSpeed = FanSpeed.OFF // The active selection.
// position variable which will be used to draw label and indicator circle position
private val pointPosition: PointF = PointF(0.0f, 0.0f)radiusคือรัศมีปัจจุบันของวงกลม ระบบจะตั้งค่านี้เมื่อวาดวิวบนหน้าจอfanSpeedคือความเร็วปัจจุบันของพัดลม ซึ่งเป็นค่าหนึ่งในการแจงนับFanSpeedโดยค่าเริ่มต้น ค่าดังกล่าวคือOFF- สุดท้าย
postPositionคือจุด X,Y ที่จะใช้ในการวาดองค์ประกอบต่างๆ ของมุมมองบนหน้าจอ
ระบบจะสร้างและเริ่มต้นค่าเหล่านี้ที่นี่แทนที่จะเป็นตอนที่วาดวิวจริงๆ เพื่อให้มั่นใจว่าขั้นตอนการวาดจริงจะทำงานได้เร็วที่สุด
- นอกจากนี้ ภายในคำจำกัดความของคลาส
DialViewให้เริ่มต้นออบเจ็กต์Paintด้วยสไตล์พื้นฐาน 2-3 แบบ นำเข้าandroid.graphics.Paintและandroid.graphics.Typefaceเมื่อมีการร้องขอ เช่นเดียวกับตัวแปรที่กล่าวถึงก่อนหน้านี้ เราจะเริ่มต้นสไตล์เหล่านี้ที่นี่เพื่อช่วยเร่งขั้นตอนการวาด
private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.FILL
textAlign = Paint.Align.CENTER
textSize = 55.0f
typeface = Typeface.create( "", Typeface.BOLD)
}- เปิด
res/values/strings.xmlแล้วเพิ่มทรัพยากรสตริงสำหรับความเร็วพัดลม
<string name="fan_off">off</string>
<string name="fan_low">1</string>
<string name="fan_medium">2</string>
<string name="fan_high">3</string>เมื่อสร้างมุมมองที่กำหนดเองแล้ว คุณต้องวาดมุมมองนั้นได้ เมื่อขยายViewคลาสย่อย เช่น EditText คลาสย่อยนั้นจะกำหนดลักษณะและแอตทริบิวต์ของมุมมอง และวาดตัวเองบนหน้าจอ ดังนั้น คุณจึงไม่ต้องเขียนโค้ดเพื่อวาดวิว คุณสามารถลบล้างเมธอดขององค์ประกอบหลักเพื่อปรับแต่งมุมมองแทนได้
หากคุณสร้างมุมมองของคุณเองตั้งแต่ต้น (โดยการขยาย View) คุณมีหน้าที่วาดมุมมองทั้งหมดทุกครั้งที่หน้าจอรีเฟรช และมีหน้าที่แทนที่เมธอด View ที่จัดการการวาด หากต้องการวาดมุมมองที่กำหนดเองซึ่งขยาย View อย่างถูกต้อง คุณต้องทำดังนี้
- คำนวณขนาดของมุมมองเมื่อปรากฏขึ้นครั้งแรก และทุกครั้งที่ขนาดของมุมมองนั้นเปลี่ยนแปลงโดยการลบล้างเมธอด
onSizeChanged() - แทนที่เมธอด
onDraw()เพื่อวาดมุมมองที่กำหนดเองโดยใช้ออบเจ็กต์Canvasที่จัดรูปแบบโดยออบเจ็กต์Paint - เรียกใช้เมธอด
invalidate()เมื่อตอบสนองต่อการคลิกของผู้ใช้ซึ่งเปลี่ยนวิธีวาดมุมมองเพื่อลบล้างมุมมองทั้งหมด ซึ่งจะเป็นการบังคับให้เรียกใช้onDraw()เพื่อวาดมุมมองใหม่
ระบบจะเรียกใช้เมธอด onDraw() ทุกครั้งที่หน้าจอฟรีเฟรช ซึ่งอาจเกิดขึ้นหลายครั้งต่อวินาที ด้วยเหตุผลด้านประสิทธิภาพและเพื่อหลีกเลี่ยงข้อบกพร่องทางภาพ คุณควรทำงานใน onDraw() ให้น้อยที่สุด โดยเฉพาะอย่างยิ่ง อย่าจัดสรรใน onDraw() เนื่องจากอาจทำให้เกิดการเก็บขยะซึ่งอาจทำให้ภาพกระตุก
คลาส Canvas และ Paint มีแป้นพิมพ์ลัดในการวาดภาพที่มีประโยชน์หลายอย่าง ดังนี้
- วาดข้อความโดยใช้
drawText()ระบุแบบอักษรโดยเรียกใช้setTypeface()และสีข้อความโดยเรียกใช้setColor() - วาดรูปร่างพื้นฐานโดยใช้
drawRect(),drawOval()และdrawArc()เปลี่ยนว่าจะให้รูปทรงมีสี เค้าโครง หรือทั้ง 2 อย่างโดยเรียกใช้setStyle() - วาดบิตแมปโดยใช้
drawBitmap()
คุณจะได้ดูข้อมูลเพิ่มเติมเกี่ยวกับ Canvas และ Paint ใน Codelab ในภายหลัง ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีที่ Android วาด View ได้ที่วิธีที่ Android วาด View
ในงานนี้ คุณจะวาดมุมมองที่กำหนดเองของตัวควบคุมพัดลมลงบนหน้าจอ ซึ่งก็คือหน้าปัด ตัวบ่งชี้ตำแหน่งปัจจุบัน และป้ายกำกับตัวบ่งชี้ โดยใช้เมธอด onSizeChanged() และ onDraw() นอกจากนี้ คุณยังจะได้สร้างเมธอดตัวช่วย computeXYForSpeed(), เพื่อคำนวณตำแหน่ง X,Y ปัจจุบันของป้ายกำกับตัวบ่งชี้บนหน้าปัดด้วย
ขั้นตอนที่ 1 คำนวณตำแหน่งและวาดมุมมอง
- ในคลาส
DialViewให้ลบล้างเมธอดonSizeChanged()จากคลาสViewเพื่อคำนวณขนาดของแป้นหมุนของมุมมองที่กำหนดเอง นำเข้าkotlinmath.minเมื่อมีการร้องขอ
ระบบจะเรียกใช้เมธอดonSizeChanged()ทุกครั้งที่ขนาดของมุมมองเปลี่ยนแปลง รวมถึงครั้งแรกที่วาดเมื่อมีการขยายเลย์เอาต์ แทนที่onSizeChanged()เพื่อคำนวณตำแหน่ง ขนาด และค่าอื่นๆ ที่เกี่ยวข้องกับขนาดของมุมมองที่กำหนดเอง แทนที่จะคำนวณใหม่ทุกครั้งที่วาด ในกรณีนี้ คุณใช้onSizeChanged()เพื่อคำนวณรัศมีปัจจุบันขององค์ประกอบวงกลมของหน้าปัด
override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
radius = (min(width, height) / 2.0 * 0.8).toFloat()
}- เพิ่มโค้ดนี้ใต้
onSizeChanged()เพื่อกำหนดฟังก์ชันส่วนขยายcomputeXYForSpeed()สำหรับคลาสPointFนำเข้าkotlin.math.cosและkotlin.math.sinเมื่อมีการร้องขอ ฟังก์ชันส่วนขยายนี้ในคลาสPointFจะคำนวณพิกัด X, Y บนหน้าจอสำหรับป้ายกำกับข้อความและตัวบ่งชี้ปัจจุบัน (0, 1, 2 หรือ 3) โดยอิงตามตำแหน่งFanSpeedปัจจุบันและรัศมีของแป้นหมุน คุณจะใช้รหัสนี้ในonDraw().
private fun PointF.computeXYForSpeed(pos: FanSpeed, radius: Float) {
// Angles are in radians.
val startAngle = Math.PI * (9 / 8.0)
val angle = startAngle + pos.ordinal * (Math.PI / 4)
x = (radius * cos(angle)).toFloat() + width / 2
y = (radius * sin(angle)).toFloat() + height / 2
}- แทนที่เมธอด
onDraw()เพื่อแสดงผลมุมมองบนหน้าจอด้วยคลาสCanvasและPaintนำเข้าandroid.graphics.Canvasเมื่อได้รับคำขอ นี่คือการลบล้างโครงกระดูก
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
}- ภายใน
onDraw()ให้เพิ่มบรรทัดนี้เพื่อตั้งค่าสีการวาดเป็นสีเทา (Color.GRAY) หรือสีเขียว (Color.GREEN) ขึ้นอยู่กับว่าความเร็วพัดลมเป็นOFFหรือค่าอื่นๆ นำเข้าandroid.graphics.Colorเมื่อได้รับคำขอ
// Set dial background color to green if selection not off.
paint.color = if (fanSpeed == FanSpeed.OFF) Color.GRAY else Color.GREEN- เพิ่มโค้ดนี้เพื่อวาดวงกลมสำหรับหน้าปัดด้วยเมธอด
drawCircle()เมธอดนี้ใช้ความกว้างและความสูงของมุมมองปัจจุบันเพื่อหารัศมีของวงกลม จุดศูนย์กลางของวงกลม และสีที่ใช้ระบายสีในปัจจุบัน พร็อพเพอร์ตี้widthและheightเป็นสมาชิกของคลาสซูเปอร์Viewและระบุขนาดปัจจุบันของมุมมอง
// Draw the dial.
canvas.drawCircle((width / 2).toFloat(), (height / 2).toFloat(), radius, paint)- เพิ่มโค้ดต่อไปนี้เพื่อวาดวงกลมขนาดเล็กสำหรับเครื่องหมายแสดงความเร็วพัดลมด้วย
drawCircle()วิธีนี้ ส่วนนี้ใช้PointFcomputeXYforSpeed()วิธีการขยายเพื่อคำนวณพิกัด X,Y สำหรับกึ่งกลางของตัวบ่งชี้ตามความเร็วพัดลมปัจจุบัน
// Draw the indicator circle.
val markerRadius = radius + RADIUS_OFFSET_INDICATOR
pointPosition.computeXYForSpeed(fanSpeed, markerRadius)
paint.color = Color.BLACK
canvas.drawCircle(pointPosition.x, pointPosition.y, radius/12, paint)- สุดท้าย ให้วาดป้ายกำกับความเร็วพัดลม (0, 1, 2, 3) ในตำแหน่งที่เหมาะสมรอบๆ หน้าปัด ส่วนนี้ของเมธอดจะเรียกใช้
PointF.computeXYForSpeed()อีกครั้งเพื่อรับตำแหน่งของป้ายกำกับแต่ละรายการ และนำออบเจ็กต์pointPositionกลับมาใช้ซ้ำทุกครั้งเพื่อหลีกเลี่ยงการจัดสรร ใช้drawText()เพื่อวาดป้ายกำกับ
// Draw the text labels.
val labelRadius = radius + RADIUS_OFFSET_LABEL
for (i in FanSpeed.values()) {
pointPosition.computeXYForSpeed(i, labelRadius)
val label = resources.getString(i.label)
canvas.drawText(label, pointPosition.x, pointPosition.y, paint)
}onDraw() ที่เสร็จสมบูรณ์จะมีลักษณะดังนี้
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Set dial background color to green if selection not off.
paint.color = if (fanSpeed == FanSpeed.OFF) Color.GRAY else Color.GREEN
// Draw the dial.
canvas.drawCircle((width / 2).toFloat(), (height / 2).toFloat(), radius, paint)
// Draw the indicator circle.
val markerRadius = radius + RADIUS_OFFSET_INDICATOR
pointPosition.computeXYForSpeed(fanSpeed, markerRadius)
paint.color = Color.BLACK
canvas.drawCircle(pointPosition.x, pointPosition.y, radius/12, paint)
// Draw the text labels.
val labelRadius = radius + RADIUS_OFFSET_LABEL
for (i in FanSpeed.values()) {
pointPosition.computeXYForSpeed(i, labelRadius)
val label = resources.getString(i.label)
canvas.drawText(label, pointPosition.x, pointPosition.y, paint)
}
}ขั้นตอนที่ 2 เพิ่มมุมมองไปยังเลย์เอาต์
หากต้องการเพิ่มมุมมองที่กำหนดเองลงใน UI ของแอป ให้ระบุเป็นองค์ประกอบในเลย์เอาต์ XML ของกิจกรรม ควบคุมลักษณะที่ปรากฏและลักษณะการทำงานของวิดเจ็ตด้วยแอตทริบิวต์ขององค์ประกอบ XML เช่นเดียวกับองค์ประกอบ UI อื่นๆ
- ใน
activity_main.xmlให้เปลี่ยนแท็กImageViewสำหรับdialViewเป็นcom.example.android.customfancontroller.DialViewแล้วลบแอตทริบิวต์android:backgroundทั้งDialViewและImageViewเดิมจะรับช่วงแอตทริบิวต์มาตรฐานจากคลาสViewจึงไม่จำเป็นต้องเปลี่ยนแอตทริบิวต์อื่นๆ องค์ประกอบDialViewใหม่จะมีลักษณะดังนี้
<com.example.android.customfancontroller.DialView
android:id="@+id/dialView"
android:layout_width="@dimen/fan_dimen"
android:layout_height="@dimen/fan_dimen"
app:layout_constraintTop_toBottomOf="@+id/customViewLabel"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginLeft="@dimen/default_margin"
android:layout_marginRight="@dimen/default_margin"
android:layout_marginTop="@dimen/default_margin" />- เรียกใช้แอป มุมมองการควบคุมพัดลมจะปรากฏในกิจกรรม

งานสุดท้ายคือการเปิดใช้มุมมองที่กำหนดเองเพื่อดำเนินการเมื่อผู้ใช้แตะมุมมอง การแตะแต่ละครั้งควรย้ายตัวบ่งชี้การเลือกไปยังตำแหน่งถัดไป ได้แก่ ปิด-1-2-3 และกลับไปที่ปิด นอกจากนี้ หากการเลือกเป็น 1 ขึ้นไป ให้เปลี่ยนพื้นหลังจากสีเทาเป็นสีเขียวเพื่อระบุว่าพัดลมเปิดอยู่
หากต้องการเปิดใช้มุมมองที่กําหนดเองเพื่อให้คลิกได้ คุณต้องทําดังนี้
- ตั้งค่าพร็อพเพอร์ตี้
isClickableของข้อมูลพร็อพเพอร์ตี้เป็นtrueซึ่งจะช่วยให้มุมมองที่กำหนดเองตอบสนองต่อการคลิกได้ - ใช้
performClick()ของคลาสViewเพื่อดำเนินการเมื่อมีการคลิกมุมมอง - เรียกใช้เมธอด
invalidate()ซึ่งจะบอกให้ระบบ Android เรียกใช้เมธอดonDraw()เพื่อวาดมุมมองใหม่
โดยปกติแล้ว เมื่อใช้มุมมอง Android มาตรฐาน คุณจะใช้ OnClickListener() เพื่อดำเนินการเมื่อผู้ใช้คลิกมุมมองนั้น สําหรับมุมมองที่กําหนดเอง ให้ใช้เมธอด performClick() ของคลาส View แทน แล้วเรียกใช้ superperformClick(). เมธอด performClick() เริ่มต้นจะเรียกใช้ onClickListener() ด้วย คุณจึงเพิ่มการดำเนินการลงใน performClick() และปล่อยให้ onClickListener() พร้อมใช้งานสำหรับการปรับแต่งเพิ่มเติมโดยคุณหรือนักพัฒนาแอปคนอื่นๆ ที่อาจใช้มุมมองที่กำหนดเองของคุณ
- ใน
DialView.ktภายในFanSpeedการแจงนับ ให้เพิ่มฟังก์ชันส่วนขยายnext()ที่เปลี่ยนความเร็วพัดลมปัจจุบันเป็นความเร็วถัดไปในรายการ (จากOFFเป็นLOW,MEDIUMและHIGHจากนั้นกลับไปที่OFF) ตอนนี้การแจงนับที่สมบูรณ์จะมีลักษณะดังนี้
private enum class FanSpeed(val label: Int) {
OFF(R.string.fan_off),
LOW(R.string.fan_low),
MEDIUM(R.string.fan_medium),
HIGH(R.string.fan_high);
fun next() = when (this) {
OFF -> LOW
LOW -> MEDIUM
MEDIUM -> HIGH
HIGH -> OFF
}
}- ใน
DialViewคลาส ให้เพิ่มบล็อกinit()ก่อนเมธอดonSizeChanged()การตั้งค่าพร็อพเพอร์ตี้isClickableของมุมมองเป็น "จริง" จะทําให้มุมมองนั้นยอมรับอินพุตของผู้ใช้ได้
init {
isClickable = true
}- ด้านล่าง
init(),ให้ลบล้างเมธอดperformClick()ด้วยโค้ดด้านล่าง
override fun performClick(): Boolean {
if (super.performClick()) return true
fanSpeed = fanSpeed.next()
contentDescription = resources.getString(fanSpeed.label)
invalidate()
return true
}โทรหา superperformClick() ต้องเกิดขึ้นก่อน ซึ่งจะเปิดใช้กิจกรรมการช่วยเหลือพิเศษรวมถึงการโทร onClickListener()
2 บรรทัดถัดไปจะเพิ่มความเร็วของพัดลมด้วยเมธอด next() และตั้งค่าคำอธิบายเนื้อหาของมุมมองเป็นทรัพยากรสตริงที่แสดงความเร็วปัจจุบัน (ปิด 1, 2 หรือ 3)
สุดท้ายนี้ วิธี invalidate() จะทำให้มุมมองทั้งหมดไม่ถูกต้อง และบังคับให้เรียกใช้ onDraw() เพื่อวาดมุมมองใหม่ หากมีการเปลี่ยนแปลงในมุมมองที่กำหนดเองไม่ว่าด้วยเหตุผลใดก็ตาม รวมถึงการโต้ตอบของผู้ใช้ และจำเป็นต้องแสดงการเปลี่ยนแปลง ให้เรียกใช้ invalidate().
- เรียกใช้แอป แตะองค์ประกอบ
DialViewเพื่อย้ายตัวบ่งชี้จากปิดเป็น 1 แป้นหมุนควรเปลี่ยนเป็นสีเขียว เมื่อแตะแต่ละครั้ง ตัวบ่งชี้ควรย้ายไปยังตำแหน่งถัดไป เมื่อสัญญาณกลับมาเป็นปิดอีกครั้ง แป้นหมุนควรเปลี่ยนเป็นสีเทาอีกครั้ง


ตัวอย่างนี้แสดงกลไกพื้นฐานของการใช้แอตทริบิวต์ที่กำหนดเองกับมุมมองที่กำหนดเอง คุณกำหนดแอตทริบิวต์ที่กำหนดเองสำหรับคลาส DialView โดยใช้สีที่แตกต่างกันสำหรับตำแหน่งการหมุนพัดลมแต่ละตำแหน่ง
- สร้างและเปิด
res/values/attrs.xml - เพิ่มองค์ประกอบทรัพยากร
<declare-styleable>ภายใน<resources> - ภายในองค์ประกอบทรัพยากร
<declare-styleable>ให้เพิ่มองค์ประกอบattr3 รายการ โดยแต่ละรายการจะมีnameและformatformatเหมือนกับประเภท และในกรณีนี้คือcolor
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="DialView">
<attr name="fanColor1" format="color" />
<attr name="fanColor2" format="color" />
<attr name="fanColor3" format="color" />
</declare-styleable>
</resources>- เปิดไฟล์เลย์เอาต์
activity_main.xml - ใน
DialViewให้เพิ่มแอตทริบิวต์สำหรับfanColor1,fanColor2และfanColor3แล้วตั้งค่าเป็นสีที่แสดงด้านล่าง ใช้app:เป็นคำนำหน้าสำหรับแอตทริบิวต์ที่กำหนดเอง (เช่นapp:fanColor1) แทนandroid:เนื่องจากแอตทริบิวต์ที่กำหนดเองอยู่ในเนมสเปซschemas.android.com/apk/res/your_app_package_nameไม่ใช่เนมสเปซandroid
app:fanColor1="#FFEB3B"
app:fanColor2="#CDDC39"
app:fanColor3="#009688"หากต้องการใช้แอตทริบิวต์ในคลาส DialView คุณต้องเรียกข้อมูลแอตทริบิวต์เหล่านั้น โดยจะจัดเก็บไว้ในAttributeSetซึ่งจะส่งมอบให้ชั้นเรียนของคุณเมื่อสร้าง หากมี คุณดึงข้อมูลแอตทริบิวต์ใน init และกำหนดค่าแอตทริบิวต์ให้กับตัวแปรภายในสำหรับการแคช
- เปิด
DialView.ktไฟล์ชั้นเรียน - ภายใน
DialViewให้ประกาศตัวแปรเพื่อแคชค่าแอตทริบิวต์
private var fanSpeedLowColor = 0
private var fanSpeedMediumColor = 0
private var fanSeedMaxColor = 0- ในบล็อก
initให้เพิ่มโค้ดต่อไปนี้โดยใช้ฟังก์ชันส่วนขยายwithStyledAttributesคุณระบุแอตทริบิวต์และมุมมอง รวมถึงตั้งค่าตัวแปรในเครื่อง การนำเข้าwithStyledAttributesจะเป็นการนำเข้าฟังก์ชันgetColor()ที่ถูกต้องด้วย
context.withStyledAttributes(attrs, R.styleable.DialView) {
fanSpeedLowColor = getColor(R.styleable.DialView_fanColor1, 0)
fanSpeedMediumColor = getColor(R.styleable.DialView_fanColor2, 0)
fanSeedMaxColor = getColor(R.styleable.DialView_fanColor3, 0)
}- ใช้ตัวแปรภายในใน
onDraw()เพื่อตั้งค่าสีแป้นหมุนตามความเร็วพัดลมปัจจุบัน แทนที่บรรทัดที่ตั้งค่าสีทา (paint.color=if(fanSpeed== FanSpeed.OFF) Color.GRAYelseColor.GREEN) ด้วยโค้ดด้านล่าง
paint.color = when (fanSpeed) {
FanSpeed.OFF -> Color.GRAY
FanSpeed.LOW -> fanSpeedLowColor
FanSpeed.MEDIUM -> fanSpeedMediumColor
FanSpeed.HIGH -> fanSeedMaxColor
} as Int- เรียกใช้แอป คลิกที่แป้นหมุน และการตั้งค่าสีควรแตกต่างกันสำหรับแต่ละตำแหน่ง ดังที่แสดงด้านล่าง
|
|
|
|
ดูข้อมูลเพิ่มเติมเกี่ยวกับแอตทริบิวต์มุมมองที่กำหนดเองได้ที่การสร้างคลาสมุมมอง
การช่วยเหลือพิเศษคือชุดเทคนิคการออกแบบ การติดตั้งใช้งาน และการทดสอบที่ช่วยให้ทุกคน รวมถึงผู้พิการสามารถใช้แอปของคุณได้
ความพิการที่พบบ่อยซึ่งอาจส่งผลต่อการใช้อุปกรณ์ Android ของบุคคล ได้แก่ ภาวะตาบอด สายตาเลือนราง ตาบอดสี ภาวะหูหนวกหรือสูญเสียการได้ยิน และทักษะการเคลื่อนไหวที่จำกัด เมื่อพัฒนาแอปโดยคำนึงถึงความสามารถในการเข้าถึง คุณจะปรับปรุงประสบการณ์ของผู้ใช้ให้ดียิ่งขึ้น ไม่เพียงแต่สำหรับผู้ใช้ที่มีความพิการเท่านั้น แต่ยังรวมถึงผู้ใช้คนอื่นๆ ทั้งหมดด้วย
Android มีฟีเจอร์การช่วยเหลือพิเศษหลายอย่างโดยค่าเริ่มต้นในมุมมอง UI มาตรฐาน เช่น TextView และ Button อย่างไรก็ตาม เมื่อสร้างมุมมองที่กำหนดเอง คุณต้องพิจารณาว่ามุมมองที่กำหนดเองนั้นจะให้ฟีเจอร์ที่เข้าถึงได้ เช่น คำอธิบายที่พูดของเนื้อหาบนหน้าจอ ได้อย่างไร
ในงานนี้ คุณจะได้เรียนรู้เกี่ยวกับ TalkBack ซึ่งเป็นโปรแกรมอ่านหน้าจอของ Android และแก้ไขแอปให้มีคำใบ้และคำอธิบายที่อ่านออกเสียงได้สำหรับDialViewมุมมองที่กำหนดเอง
ขั้นตอนที่ 1 สำรวจ TalkBack
TalkBack เป็นโปรแกรมอ่านหน้าจอในตัวของ Android เมื่อเปิดใช้ TalkBack ผู้ใช้จะโต้ตอบกับอุปกรณ์ Android ได้โดยไม่ต้องมองหน้าจอ เนื่องจาก Android จะอธิบายองค์ประกอบบนหน้าจอด้วยการอ่านออกเสียง ผู้ใช้ที่มีความบกพร่องทางสายตาอาจต้องพึ่งพา TalkBack ในการใช้แอปของคุณ
ในงานนี้ คุณจะเปิดใช้ TalkBack เพื่อทำความเข้าใจวิธีการทำงานของโปรแกรมอ่านหน้าจอและวิธีไปยังส่วนต่างๆ ของแอป
- ในอุปกรณ์หรือโปรแกรมจำลอง Android ให้ไปที่การตั้งค่า > การช่วยเหลือพิเศษ > TalkBack
- แตะปุ่มเปิด/ปิดเพื่อเปิด TalkBack
- แตะตกลงเพื่อยืนยันสิทธิ์
- ยืนยันรหัสผ่านของอุปกรณ์ หากระบบขอ หากคุณเรียกใช้ TalkBack เป็นครั้งแรก บทแนะนำจะเปิดขึ้น (บทแนะนำอาจไม่พร้อมใช้งานในอุปกรณ์รุ่นเก่า)
- การไปยังส่วนต่างๆ ของบทแนะนำโดยหลับตาอาจเป็นประโยชน์ หากต้องการเปิดบทแนะนำอีกครั้งในอนาคต ให้ไปที่การตั้งค่า > การช่วยเหลือพิเศษ > TalkBack > การตั้งค่า > เปิดบทแนะนำ TalkBack
- คอมไพล์และเรียกใช้
CustomFanControllerแอป หรือเปิดด้วยปุ่มภาพรวมหรือล่าสุดในอุปกรณ์ เมื่อเปิด TalkBack คุณจะเห็นว่าระบบจะอ่านชื่อแอป รวมถึงข้อความของป้ายกำกับTextView("การควบคุมพัดลม") อย่างไรก็ตาม หากคุณแตะที่DialViewมุมมองเอง ระบบจะไม่พูดข้อมูลเกี่ยวกับสถานะของมุมมอง (การตั้งค่าปัจจุบันสำหรับแป้นหมุน) หรือการดำเนินการที่จะเกิดขึ้นเมื่อคุณแตะมุมมองเพื่อเปิดใช้งาน
ขั้นตอนที่ 2 เพิ่มคำอธิบายเนื้อหาสำหรับป้ายกำกับแบบหมุน
คำอธิบายเนื้อหาจะอธิบายความหมายและวัตถุประสงค์ของมุมมองในแอป ป้ายกำกับเหล่านี้ช่วยให้โปรแกรมอ่านหน้าจอ เช่น ฟีเจอร์ TalkBack ของ Android อธิบายฟังก์ชันของแต่ละองค์ประกอบได้อย่างถูกต้อง สำหรับมุมมองแบบคงที่ เช่น ImageView คุณสามารถเพิ่มคำอธิบายเนื้อหาลงในมุมมองในไฟล์เลย์เอาต์ด้วยแอตทริบิวต์ contentDescription มุมมองข้อความ (TextView และ EditText) จะใช้ข้อความในมุมมองเป็นคำอธิบายเนื้อหาโดยอัตโนมัติ
สำหรับมุมมองการควบคุมพัดลมที่กำหนดเอง คุณต้องอัปเดตคำอธิบายเนื้อหาแบบไดนามิกทุกครั้งที่มีการคลิกมุมมองเพื่อระบุการตั้งค่าพัดลมปัจจุบัน
- ที่ด้านล่างของคลาส
DialViewให้ประกาศฟังก์ชันupdateContentDescription()ที่ไม่มีอาร์กิวเมนต์หรือประเภทการคืนค่า
fun updateContentDescription() {
}- ใน
updateContentDescription()ให้เปลี่ยนพร็อพเพอร์ตี้contentDescriptionสำหรับมุมมองที่กำหนดเองเป็นทรัพยากรสตริงที่เชื่อมโยงกับความเร็วพัดลมปัจจุบัน (ปิด, 1, 2 หรือ 3) ป้ายกำกับเหล่านี้เป็นป้ายกำกับเดียวกันกับที่ใช้ในonDraw()เมื่อวาดแป้นหมุนบนหน้าจอ
fun updateContentDescription() {
contentDescription = resources.getString(fanSpeed.label)
}- เลื่อนขึ้นไปที่บล็อก
init()แล้วเพิ่มการเรียกไปยังupdateContentDescription()ที่ท้ายบล็อกนั้น ซึ่งจะเริ่มต้นคำอธิบายเนื้อหาเมื่อเริ่มต้นมุมมอง
init {
isClickable = true
// ...
updateContentDescription()
}- เพิ่มการเรียกอีกครั้งในเมธอด
updateContentDescription()ในเมธอดperformClick()ก่อนinvalidate()
override fun performClick(): Boolean {
if (super.performClick()) return true
fanSpeed = fanSpeed.next()
updateContentDescription()
invalidate()
return true
}- คอมไพล์และเรียกใช้แอป และตรวจสอบว่าได้เปิด TalkBack แล้ว แตะเพื่อเปลี่ยนการตั้งค่าสำหรับมุมมองแป้นหมุน แล้วสังเกตว่าตอนนี้ TalkBack จะประกาศป้ายกำกับปัจจุบัน (ปิด, 1, 2, 3) รวมถึงวลี "แตะสองครั้งเพื่อเปิดใช้งาน"
ขั้นตอนที่ 3 เพิ่มข้อมูลเพิ่มเติมสำหรับการดำเนินการคลิก
คุณหยุดที่ตรงนี้ได้และมุมมองของคุณจะใช้ได้ใน TalkBack แต่จะดีกว่าหากมุมมองของคุณระบุได้ไม่เพียงแค่ว่าเปิดใช้งานได้ ("แตะสองครั้งเพื่อเปิดใช้งาน") แต่ยังอธิบายด้วยว่าจะเกิดอะไรขึ้นเมื่อเปิดใช้งานมุมมอง ("แตะสองครั้งเพื่อเปลี่ยน" หรือ "แตะสองครั้งเพื่อรีเซ็ต")
โดยคุณจะเพิ่มข้อมูลเกี่ยวกับการดำเนินการของมุมมอง (ในที่นี้คือการคลิกหรือแตะ) ลงในออบเจ็กต์ข้อมูลโหนดการช่วยเหลือพิเศษได้โดยใช้ตัวแทนการช่วยเหลือพิเศษ Accessibility Delegate ช่วยให้คุณปรับแต่งฟีเจอร์ที่เกี่ยวข้องกับการช่วยเหลือพิเศษของแอปผ่านการคอมโพสิชัน (แทนที่จะเป็นการรับค่า)
สำหรับงานนี้ คุณจะใช้คลาสการช่วยเหลือพิเศษในไลบรารี Android Jetpack (androidx.*) เพื่อให้มั่นใจถึงความเข้ากันได้แบบย้อนหลัง
- ใน
DialView.ktในบล็อกinitให้ตั้งค่าผู้มอบสิทธิ์การช่วยเหลือพิเศษในมุมมองเป็นออบเจ็กต์AccessibilityDelegateCompatใหม่ นำเข้าandroidx.core.view.ViewCompatและandroidx.core.view.AccessibilityDelegateCompatเมื่อมีการร้องขอ กลยุทธ์นี้ช่วยให้แอปของคุณเข้ากันได้แบบย้อนหลังมากที่สุด
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
})- ในออบเจ็กต์
AccessibilityDelegateCompatให้ลบล้างฟังก์ชันonInitializeAccessibilityNodeInfo()ด้วยออบเจ็กต์AccessibilityNodeInfoCompatแล้วเรียกใช้เมธอดของคลาสแม่ นำเข้าandroidx.core.view.accessibility.AccessibilityNodeInfoCompatเมื่อได้รับข้อความแจ้ง
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(host: View,
info: AccessibilityNodeInfoCompat) {
super.onInitializeAccessibilityNodeInfo(host, info)
}
})ทุกมุมมองจะมีโครงสร้างแบบต้นไม้ของโหนดการช่วยเหลือพิเศษ ซึ่งอาจตรงหรือไม่ตรงกับคอมโพเนนต์เลย์เอาต์จริงของมุมมองก็ได้ บริการการช่วยเหลือพิเศษของ Android จะไปยังโหนดเหล่านั้นเพื่อค้นหาข้อมูลเกี่ยวกับมุมมอง (เช่น คำอธิบายเนื้อหาที่พูดได้ หรือการดำเนินการที่เป็นไปได้ซึ่งทำได้ในมุมมองนั้น) เมื่อสร้างมุมมองที่กำหนดเอง คุณอาจต้องลบล้างข้อมูลโหนดเพื่อระบุข้อมูลที่กำหนดเองสำหรับการช่วยเหลือพิเศษด้วย ในกรณีนี้ คุณจะลบล้างข้อมูลโหนดเพื่อระบุว่ามีการดำเนินการที่กำหนดเองสำหรับมุมมอง
- ภายใน
onInitializeAccessibilityNodeInfo()ให้สร้างออบเจ็กต์AccessibilityNodeInfoCompat.AccessibilityActionCompatใหม่ แล้วกําหนดให้กับตัวแปรcustomClickส่งค่าคงที่AccessibilityNodeInfo.ACTION_CLICKและสตริงตัวยึดตำแหน่งไปยังตัวสร้าง นำเข้าAccessibilityNodeInfoเมื่อได้รับคำขอ
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(host: View,
info: AccessibilityNodeInfoCompat) {
super.onInitializeAccessibilityNodeInfo(host, info)
val customClick = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
AccessibilityNodeInfo.ACTION_CLICK,
"placeholder"
)
}
})คลาส AccessibilityActionCompat แสดงถึงการดำเนินการในมุมมองเพื่อวัตถุประสงค์ในการช่วยเหลือพิเศษ การดำเนินการทั่วไปคือการคลิกหรือแตะ เช่นที่คุณใช้ที่นี่ แต่การดำเนินการอื่นๆ อาจรวมถึงการได้รับหรือสูญเสียโฟกัส การดำเนินการในคลิปบอร์ด (ตัด/คัดลอก/วาง) หรือการเลื่อนภายในมุมมอง ตัวสร้างสำหรับคลาสนี้ต้องมีค่าคงที่ของการดำเนินการ (ในที่นี้คือ AccessibilityNodeInfo.ACTION_CLICK) และสตริงที่ TalkBack ใช้เพื่อระบุว่าการดำเนินการคืออะไร
- แทนที่สตริง
"placeholder"ด้วยการเรียกใช้context.getString()เพื่อดึงข้อมูลทรัพยากรสตริง สำหรับทรัพยากรที่เฉพาะเจาะจง ให้ทดสอบความเร็วพัดลมปัจจุบัน หากความเร็วปัจจุบันคือFanSpeed.HIGHสตริงจะเป็น"Reset"หากความเร็วพัดลมเป็นค่าอื่น สตริงจะเป็น"Change."คุณจะสร้างทรัพยากรสตริงเหล่านี้ในขั้นตอนถัดไป
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(host: View,
info: AccessibilityNodeInfoCompat) {
super.onInitializeAccessibilityNodeInfo(host, info)
val customClick = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
AccessibilityNodeInfo.ACTION_CLICK,
context.getString(if (fanSpeed != FanSpeed.HIGH) R.string.change else R.string.reset)
)
}
})- หลังจากวงเล็บปิดสำหรับ
customClickคำจำกัดความ ให้ใช้วิธีaddAction()เพื่อเพิ่มการดำเนินการเพื่อการช่วยเหลือพิเศษใหม่ไปยังออบเจ็กต์ข้อมูลโหนด
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(host: View,
info: AccessibilityNodeInfoCompat) {
super.onInitializeAccessibilityNodeInfo(host, info)
val customClick = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
AccessibilityNodeInfo.ACTION_CLICK,
context.getString(if (fanSpeed != FanSpeed.HIGH)
R.string.change else R.string.reset)
)
info.addAction(customClick)
}
})- ใน
res/values/strings.xmlให้เพิ่มทรัพยากรสตริงสำหรับ "เปลี่ยน" และ "รีเซ็ต"
<string name="change">Change</string>
<string name="reset">Reset</string>- คอมไพล์และเรียกใช้แอป แล้วตรวจสอบว่า TalkBack เปิดอยู่ ตอนนี้คุณจะเห็นว่าวลี "แตะสองครั้งเพื่อเปิดใช้งาน" เปลี่ยนเป็น "แตะสองครั้งเพื่อเปลี่ยน" (หากความเร็วพัดลมต่ำกว่าสูงหรือ 3) หรือ "แตะสองครั้งเพื่อรีเซ็ต" (หากความเร็วพัดลมสูงหรือ 3 อยู่แล้ว) โปรดทราบว่าข้อความแจ้ง "แตะสองครั้งเพื่อ..." มาจากบริการ TalkBack เอง
ดาวน์โหลดโค้ดสำหรับ Codelab ที่เสร็จสมบูรณ์
$ git clone https://github.com/googlecodelabs/android-kotlin-drawing-custom-views
หรือคุณจะดาวน์โหลดที่เก็บเป็นไฟล์ Zip, แตกไฟล์ และเปิดใน Android Studio ก็ได้
- หากต้องการสร้างมุมมองที่กำหนดเองซึ่งรับช่วงลักษณะและลักษณะการทำงานของคลาสย่อย
Viewเช่นEditTextให้เพิ่มคลาสใหม่ที่ขยายคลาสย่อยนั้น และทำการปรับเปลี่ยนโดยการลบล้างเมธอดบางอย่างของคลาสย่อย - หากต้องการสร้างมุมมองที่กำหนดเองที่มีขนาดและรูปร่างใดก็ได้ ให้เพิ่มคลาสใหม่ที่ขยาย
View - แทนที่เมธอด
Viewเช่นonDraw()เพื่อกำหนดรูปร่างและลักษณะพื้นฐานของมุมมอง - ใช้
invalidate()เพื่อบังคับให้วาดหรือวาดมุมมองใหม่ - หากต้องการเพิ่มประสิทธิภาพ ให้จัดสรรตัวแปรและกําหนดค่าที่จําเป็นสําหรับการวาดและระบายสีก่อนที่จะใช้ใน
onDraw()เช่น ในการเริ่มต้นตัวแปรสมาชิก - แทนที่จะใช้
OnClickListener() ให้ใช้การลบล้างperformClick()กับมุมมองที่กำหนดเองเพื่อให้ลักษณะการทำงานแบบอินเทอร์แอกทีฟของมุมมอง ซึ่งจะช่วยให้คุณหรือนักพัฒนาแอป Android คนอื่นๆ ที่อาจใช้คลาส View ที่กำหนดเองของคุณใช้onClickListener()เพื่อให้ลักษณะการทำงานเพิ่มเติมได้ - เพิ่มมุมมองที่กำหนดเองลงในไฟล์เลย์เอาต์ XML พร้อมแอตทริบิวต์เพื่อกำหนดลักษณะที่ปรากฏ เช่นเดียวกับองค์ประกอบ UI อื่นๆ
- สร้างไฟล์
attrs.xmlในโฟลเดอร์valuesเพื่อกำหนดแอตทริบิวต์ที่กำหนดเอง จากนั้นคุณจะใช้แอตทริบิวต์ที่กำหนดเองสำหรับมุมมองที่กำหนดเองในไฟล์เลย์เอาต์ XML ได้
หลักสูตร Udacity:
เอกสารประกอบสำหรับนักพัฒนาแอป Android
- การสร้างมุมมองที่กำหนดเอง
@JvmOverloads- คอมโพเนนต์ที่กำหนดเอง
- วิธีที่ Android วาด View
onMeasure()onSizeChanged()onDraw()CanvasPaintdrawText()setTypeface()setColor()drawRect()drawOval()drawArc()drawBitmap()setStyle()invalidate()- ดู
- เหตุการณ์อินพุต
- Paint
- ไลบรารีส่วนขยาย Kotlin android-ktx
withStyledAttributes- เอกสารประกอบของ Android KTX
- บล็อกประกาศเดิมของ Android KTX
- ทำให้มุมมองที่กำหนดเองเข้าถึงได้ง่ายขึ้น
AccessibilityDelegateCompatAccessibilityNodeInfoCompatAccessibilityNodeInfoCompat.AccessibilityActionCompat
วิดีโอ:
ส่วนนี้แสดงรายการการบ้านที่เป็นไปได้สำหรับนักเรียน/นักศึกษาที่กำลังทำ Codelab นี้เป็นส่วนหนึ่งของหลักสูตรที่สอนโดยผู้สอน ผู้สอนมีหน้าที่ดำเนินการต่อไปนี้
- มอบหมายการบ้านหากจำเป็น
- สื่อสารกับนักเรียนเกี่ยวกับวิธีส่งงานที่ได้รับมอบหมาย
- ให้คะแนนงานการบ้าน
ผู้สอนสามารถใช้คำแนะนำเหล่านี้ได้มากน้อยตามที่ต้องการ และควรมีอิสระในการมอบหมายการบ้านอื่นๆ ที่เห็นว่าเหมาะสม
หากคุณกำลังทำ Codelab นี้ด้วยตนเอง โปรดใช้แบบฝึกหัดเหล่านี้เพื่อทดสอบความรู้ของคุณ
คำถามที่ 1
หากต้องการคำนวณตำแหน่ง มิติข้อมูล และค่าอื่นๆ เมื่อมีการกำหนดขนาดให้กับมุมมองที่กำหนดเองเป็นครั้งแรก คุณจะลบล้างเมธอดใด
▢ onMeasure()
▢ onSizeChanged()
▢ invalidate()
▢ onDraw()
คำถามที่ 2
หากต้องการระบุว่าคุณต้องการให้วาดมุมมองใหม่ด้วย onDraw() คุณจะเรียกเมธอดใดจากเทรด UI หลังจากที่ค่าแอตทริบิวต์เปลี่ยนแปลง
▢ onMeasure()
▢ onSizeChanged()
▢ invalidate()
▢ getVisibility()
คำถามที่ 3
คุณควรแทนที่Viewเมธอดใดเพื่อเพิ่มการโต้ตอบในมุมมองที่กำหนดเอง
▢ setOnClickListener()
▢ onSizeChanged()
▢ isClickable()
▢ performClick()
ดูลิงก์ไปยัง Codelab อื่นๆ ในหลักสูตรนี้ได้ที่หน้า Landing Page ของ Codelab Android ขั้นสูงใน Kotlin



