Android Kotlin Fundamentals 01.3: ทรัพยากรและความเข้ากันได้ของรูปภาพ

Codelab นี้เป็นส่วนหนึ่งของหลักสูตรพื้นฐานเกี่ยวกับ Kotlin ใน Android คุณจะได้รับประโยชน์สูงสุดจากหลักสูตรนี้ หากทํางานผ่าน Codelab ตามลําดับ Codelab ของหลักสูตรทั้งหมดจะแสดงอยู่ในหน้า Landing Page ของ Codelab ของ Android Kotlin Fundamentals

บทนำ

ใน Codelab นี้ปรับปรุงแอป DiceRoller จาก Codelab ล่าสุดและเรียนรู้วิธีเพิ่มและใช้ทรัพยากรรูปภาพในแอป นอกจากนี้ คุณยังเรียนรู้เกี่ยวกับความเข้ากันได้ของแอปกับ Android เวอร์ชันต่างๆ และวิธีที่ Android Jetpack ช่วยคุณได้อีกด้วย

สิ่งที่ควรทราบอยู่แล้ว

  • วิธีสร้างโปรเจ็กต์แอปใหม่และเรียกใช้แอปในโปรแกรมจําลองหรืออุปกรณ์จริง
  • คอมโพเนนต์พื้นฐานของโปรเจ็กต์แอป รวมถึงไดเรกทอรีทรัพยากร (res) และไฟล์บิลด์ของ Gradle
  • วิธีแก้ไขไฟล์เลย์เอาต์ของแอป
  • วิธีค้นหาและแก้ไขออบเจ็กต์ข้อมูลพร็อพเพอร์ตี้ในโค้ดของแอป

สิ่งที่จะได้เรียนรู้

  • วิธีเพิ่มไฟล์ไปยังทรัพยากรของแอป
  • วิธีใช้รูปภาพในเลย์เอาต์ของแอป
  • วิธีค้นหายอดดูอย่างมีประสิทธิภาพยิ่งขึ้นในโค้ดของแอป
  • วิธีใช้รูปภาพตัวยึดตําแหน่งในการออกแบบของแอปด้วยเนมสเปซ XML
  • เกี่ยวกับระดับ API ของ Android สําหรับแอปของคุณ และวิธีทําความเข้าใจระดับ API ขั้นต่ํา ที่กําหนดเป้าหมาย และที่คอมไพล์แล้ว
  • วิธีใช้ไลบรารี Jetpack ในแอปเพื่อรองรับ Android เวอร์ชันเก่า

สิ่งที่จะทําได้

  • แก้ไขแอป DiceRoller จาก Codelab ครั้งล่าสุดเพื่อรวมรูปภาพสําหรับค่าลูกเต๋า แทนที่จะใส่ตัวเลข
  • เพิ่มไฟล์ภาพลงในทรัพยากรของแอป
  • อัปเดตเลย์เอาต์และโค้ดของแอปเพื่อใช้รูปภาพสําหรับค่าไดร์ฟแทนตัวเลข
  • อัปเดตโค้ดเพื่อให้ดูได้อย่างมีประสิทธิภาพยิ่งขึ้น
  • อัปเดตโค้ดเพื่อใช้รูปภาพเปล่าเมื่อแอปเริ่มทํางาน
  • อัปเดตแอปเพื่อใช้ไลบรารี Android Jetpack ที่เข้ากันได้กับ Android เวอร์ชันเก่า

ใน Codelab นี้ คุณสร้างในแอป DiceRoller ที่เริ่มต้นใน Codelab ก่อนหน้า และจะเพิ่มรูปภาพลูกเต๋าที่เปลี่ยนแปลงไปเมื่อมีการทอยลูกเต๋า แอป DiceRoller สุดท้ายจะมีลักษณะดังนี้

หากคุณไม่ได้ใช้ Codelab ล่าสุด คุณจะดาวน์โหลดแอปเริ่มต้นได้ที่นี่ DiceRoller

ในตอนท้ายของ Codelab ที่ผ่านมา คุณมีแอปที่อัปเดตมุมมองข้อความซึ่งมีตัวเลข 1 ถึง 6 ทุกครั้งที่ผู้ใช้แตะปุ่ม แต่แอปนี้เรียกว่า DiceRoller ไม่ใช่โปรแกรมสร้างตัวเลข 1-6 ดังนั้นลูกเต๋าจะดูเหมือนลูกเต๋าจริงๆ ในงานนี้ คุณจะต้องเพิ่มรูปภาพลูกเต๋าลงในแอป จากนั้นแทนที่จะอัปเดตข้อความเมื่อกดปุ่ม คุณจะสลับรูปภาพอื่นสําหรับผลการค้นหาม้วนแต่ละรายการได้

ขั้นตอนที่ 1: เพิ่มรูปภาพ

  1. เปิดโปรเจ็กต์แอป DiceRoller ใน Android Studio หากยังไม่ได้เปิด หากคุณไม่ได้ใช้ Codelab ล่าสุด คุณจะดาวน์โหลดแอปได้ที่ DiceRoller
  2. ในมุมมองโปรเจ็กต์ &gt ให้ขยายโฟลเดอร์ res และขยายวาดเขียน



    แอปของคุณใช้ทรัพยากรต่างๆ มากมาย รวมถึงรูปภาพและไอคอน สี สตริง และเลย์เอาต์ XML ทรัพยากรทั้งหมดเหล่านั้นจะอยู่ในโฟลเดอร์ res โฟลเดอร์ drawable คือที่ที่คุณควรวางทรัพยากรรูปภาพทั้งหมดสําหรับแอปไว้ ในโฟลเดอร์ drawable คุณจะพบทรัพยากรสําหรับไอคอน Launcher ของแอปอยู่แล้ว
  3. ดับเบิลคลิก ic_ Launcher_background.xml โปรดทราบว่านี่เป็นไฟล์ XML ที่อธิบายไอคอนเป็นรูปภาพเวกเตอร์ เวกเตอร์ช่วยให้สามารถวาดรูปภาพของคุณในขนาดและความละเอียดต่างๆ มากมาย อาจต้องปรับขนาดรูปภาพบิตแมป เช่น PNG หรือ GIF สําหรับอุปกรณ์ที่ต่างกัน ซึ่งอาจส่งผลให้คุณภาพลดลง
  4. คลิกแสดงตัวอย่างในคอลัมน์ด้านขวาของตัวแก้ไข XML เพื่อดูเวกเตอร์ที่วาดได้ในรูปแบบภาพ


  5. ดาวน์โหลดรูปภาพลูกเต๋าสําหรับแอปจาก DiceImages.zip แตกไฟล์ ZIP คุณควรจะมีโฟลเดอร์ของไฟล์ XML ที่มีลักษณะดังนี้

  1. ใน Android Studio ให้คลิกเมนูแบบเลื่อนลงที่ด้านบนของมุมมองโปรเจ็กต์ที่มีข้อความว่า Android แล้วเลือกโปรเจ็กต์ ภาพหน้าจอด้านล่างแสดงโครงสร้างของแอปในระบบไฟล์


  2. ขยาย DiceRoller > app > src > main > res >Drawable
  3. ลากไฟล์ XML ทั้งหมดจากโฟลเดอร์ DiceImages ไปไว้ใน Android Studio และไปยังโฟลเดอร์ Drawable คลิก OK
  1. ให้เปลี่ยนโปรเจ็กต์กลับไปเป็นมุมมอง Android และสังเกตเห็นว่าไฟล์ XML ลูกเต๋าของคุณอยู่ในโฟลเดอร์ที่วาดได้
  2. ดับเบิลคลิกที่ dice_1.xml และสังเกตโค้ด XML ของรูปภาพนี้ คลิกปุ่มแสดงตัวอย่างเพื่อดูตัวอย่างของเวกเตอร์ที่วาดได้นี้

ขั้นตอนที่ 2: อัปเดตเลย์เอาต์เพื่อใช้รูปภาพ

เมื่อมีไฟล์ภาพลูกเต๋าในโฟลเดอร์ res/drawables แล้ว คุณจะเข้าถึงไฟล์เหล่านั้นจากเลย์เอาต์และโค้ดของแอปได้ ในขั้นตอนนี้ คุณจะแทนที่ TextView ที่แสดงตัวเลขด้วย ImageView เพื่อแสดงรูปภาพ

  1. เปิดไฟล์รูปแบบ activity_main.xml หากยังไม่ได้เปิด คลิกแท็บข้อความเพื่อดูโค้ด XML ของเลย์เอาต์
  2. ลบองค์ประกอบ <TextView>
  3. เพิ่มเอลิเมนต์ <ImageView> ที่มีแอตทริบิวต์เหล่านี้
<ImageView
   android:id="@+id/dice_image"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:layout_gravity="center_horizontal"
   android:src="@drawable/dice_1" />

ใช้ ImageView เพื่อแสดงรูปภาพในเลย์เอาต์ แอตทริบิวต์ใหม่เพียงรายการเดียวขององค์ประกอบนี้คือ android:src เพื่อระบุทรัพยากรแหล่งที่มาของรูปภาพ ในกรณีนี้ แหล่งที่มาของรูปภาพ @drawable/dice_1 หมายความว่า Android ควรดูทรัพยากรที่ดึงมาได้ (res/drawable) ของรูปภาพที่มีชื่อว่า dice_1

  1. คลิกปุ่มแสดงตัวอย่างเพื่อดูตัวอย่างเลย์เอาต์ ซึ่งควรมีลักษณะดังนี้

ขั้นตอนที่ 3: อัปเดตโค้ด

  1. เปิด MainActivity ฟังก์ชัน rollDice() มีลักษณะดังนี้
private fun rollDice() {
   val randomInt = Random().nextInt(6) + 1

   val resultText: TextView = findViewById(R.id.result_text)
   resultText.text = randomInt.toString()
}

โปรดสังเกตว่าการอ้างอิงไปยัง R.id.result_text อาจมีการไฮไลต์เป็นสีแดง นั่นคือเพราะคุณลบ TextView จากเลย์เอาต์ และรหัสนั้นไม่มีอยู่แล้ว

  1. ลบเส้น 2 เส้นที่ส่วนท้ายของฟังก์ชันที่กําหนดตัวแปร resultText และตั้งค่าคุณสมบัติของข้อความ คุณไม่ได้ใช้ TextView ในเลย์เอาต์แล้ว ดังนั้นจึงไม่จําเป็นต้องต่ออีก 2-3 บรรทัด
  2. ใช้ findViewByID() เพื่อรับการอ้างอิง ImageView ใหม่ในเลย์เอาต์ตามรหัส (R.id.dice_image) และกําหนดมุมมองดังกล่าวให้กับตัวแปร diceImage ใหม่
val diceImage: ImageView = findViewById(R.id.dice_image)
  1. เพิ่มบล็อก when เพื่อเลือกรูปภาพลูกเต๋าที่เจาะจงตามค่าของ randomInteger:
val drawableResource = when (randomInt) {
   1 -> R.drawable.dice_1
   2 -> R.drawable.dice_2
   3 -> R.drawable.dice_3
   4 -> R.drawable.dice_4
   5 -> R.drawable.dice_5
   else -> R.drawable.dice_6
}

เช่นเดียวกับรหัส คุณสามารถอ้างอิงรูปภาพลูกเต๋าในโฟลเดอร์ที่วาดได้ด้วยค่าในชั้นเรียน R ในที่นี้ R.drawable จะหมายถึงโฟลเดอร์ที่วาดได้ของแอป และ dice_1 เป็นทรัพยากรรูปภาพไดฟ์ที่เฉพาะเจาะจงภายในโฟลเดอร์นั้น

  1. อัปเดตแหล่งที่มาของ ImageView ด้วยเมธอด setImageResource() และการอ้างอิงไปยังรูปภาพไดร์ฟที่คุณเพิ่งพบ
diceImage.setImageResource(drawableResource)
  1. คอมไพล์และเรียกใช้แอป เมื่อคลิกปุ่มม้วนรูปภาพ รูปภาพควรอัปเดตเป็นรูปภาพที่เหมาะสม

ทุกอย่างในแอปของคุณทํางานได้ แต่ยังมีมากกว่าแค่การพัฒนาแอปไม่ใช่แค่การมีโค้ดที่ใช้งานได้ รวมถึงเข้าใจวิธีเขียนแอปที่มีประสิทธิภาพและมีประสิทธิภาพสูงด้วย ซึ่งหมายความว่าแอปควรทํางานได้ดี แม้ว่าผู้ใช้จะไม่มีอุปกรณ์ Android ที่แพงที่สุดหรือการเชื่อมต่อเครือข่ายที่ดีที่สุด นอกจากนี้แอปควรทํางานได้อย่างราบรื่นในขณะที่เพิ่มฟีเจอร์เพิ่มเติม และโค้ดควรอ่านได้ง่ายและเป็นระเบียบ

ในงานนี้ คุณจะได้เรียนรู้เกี่ยวกับวิธีหนึ่งในการทําให้แอปมีประสิทธิภาพมากขึ้น

  1. เปิด MainActivity หากยังไม่ได้เปิด ในเมธอด rollDice() โปรดสังเกตการประกาศสําหรับตัวแปร diceImage ดังนี้
val diceImage : ImageView = findViewById(R.id.dice_image)

เนื่องจาก rollDice() เป็นเครื่องจัดการคลิกสําหรับปุ่มม้วน ทุกครั้งที่ผู้ใช้แตะปุ่มดังกล่าว แอปของคุณจะเรียกใช้ findViewById() และจะได้รับการอ้างอิงอีกครั้งไปยัง ImageView นี้ โดยหลักการแล้ว คุณควรลดจํานวนการเรียกไปยัง findViewById() เนื่องจากระบบ Android ค้นหาลําดับชั้นของการดูทั้งหมดในแต่ละครั้ง ซึ่งนับเป็นการดําเนินการที่แพง

ในแอปขนาดเล็กอย่างข้อความนี้ นี่ไม่ใช่ปัญหาใหญ่ หากคุณใช้แอปที่ซับซ้อนมากขึ้นในโทรศัพท์ที่ช้า การเรียกใช้ findViewById() อย่างต่อเนื่องอาจทําให้แอปล่าช้า แนวทางปฏิบัติแนะนําคือการเรียก findViewById() เพียงครั้งเดียวและจัดเก็บออบเจ็กต์ View ในช่องแทน การเก็บการอ้างอิง ImageView ไว้ในช่องจะช่วยให้ระบบเข้าถึง View ได้โดยตรงได้ทุกเมื่อ ซึ่งช่วยปรับปรุงประสิทธิภาพการทํางาน

  1. ที่ด้านบนสุดของช่อง ก่อนonCreate() ให้สร้างช่องเพื่อใช้เก็บ ImageView
var diceImage : ImageView? = null

โดยหลักการแล้ว ควรเริ่มต้นตัวแปรนี้ที่นี่เมื่อประกาศหรือในตัวสร้าง แต่กิจกรรม Android จะไม่ใช้เครื่องมือสร้าง ความจริงแล้ว มุมมองในเลย์เอาต์นั้นไม่สามารถเข้าถึงวัตถุในหน่วยความจําได้เลยจนกว่าจะเป่าเมธอด onCreate() ตามการเรียก setContentView() คุณจะเริ่มตัวแปร diceImage ไม่ได้จนกว่าเหตุการณ์จะเสร็จสิ้น

อีกทางเลือกหนึ่งคือการกําหนดให้ตัวแปร diceImage เป็น Null ในตัวอย่างนี้ ตั้งค่าเป็น null เมื่อประกาศแล้ว จากนั้นจึงมอบหมายให้ ImageView จริงใน onCreate() ด้วย findViewById() อย่างไรก็ตาม โค้ดนี้อาจทําให้โค้ดของคุณซับซ้อน เนื่องจากคุณต้องตรวจสอบค่า null ทุกครั้งที่คุณต้องการใช้ diceImage เราเชื่อว่าต้องมีหนทางที่ดีกว่า

  1. เปลี่ยนการประกาศ diceImage เพื่อใช้คีย์เวิร์ด lateinit และนําการมอบหมาย null ออก
lateinit var diceImage : ImageView

คีย์เวิร์ด lateinit สัญญาให้คอมไพเลอร์ Kotlin ทราบว่าจะเริ่มต้นตัวแปรก่อนโค้ดจะเรียกดําเนินการใดๆ กับตัวแปร เราจึงไม่จําเป็นต้องเริ่มต้นตัวแปรเป็น null ที่นี่ และเราจะถือว่าตัวแปรเป็นตัวแปรที่ไม่สามารถยกเลิกได้เมื่อเราใช้ แนวทางปฏิบัติแนะนําคือใช้ lateinit กับช่องที่มีมุมมองในลักษณะนี้

  1. ใน onCreate() หลังจากวิธีการ setContentView() ให้ใช้ findViewById() เพื่อรับ ImageView
diceImage = findViewById(R.id.dice_image)
  1. ลบบรรทัดเก่าใน rollDice() ที่ประกาศและรับ ImageView คุณได้แทนที่บรรทัดนี้ด้วยการประกาศของช่องก่อนหน้านี้
val diceImage : ImageView = findViewById(R.id.dice_image)
  1. เรียกใช้แอปอีกครั้งเพื่อดูว่าแอปยังคงทํางานตามที่คาดไว้

ขณะนี้คุณกําลังใช้ dice_1 เป็นรูปภาพเริ่มต้นสําหรับลูกเต๋า สมมติว่าคุณไม่ต้องการแสดงรูปภาพใดๆ จนกว่าจะทอยลูกเต๋าเป็นครั้งแรก การดําเนินการดังกล่าวทําได้หลายวิธี

  1. เปิด activity_layout.xml ในแท็บข้อความ
  2. ในองค์ประกอบ <ImageView> ให้ตั้งค่าแอตทริบิวต์ android:src เป็น "@drawable/empty_dice":
android:src="@drawable/empty_dice" 

รูปภาพ empty_dice เป็นหนึ่งในรูปภาพที่คุณดาวน์โหลดและเพิ่มลงในโฟลเดอร์ drawable ขนาดดังกล่าวมีขนาดเท่ากับรูปภาพลูกเต๋าอื่นๆ แต่ขนาดจะว่างเปล่า รูปภาพนี้เป็นรูปภาพที่จะแสดงขึ้นเมื่อแอปเริ่มทํางานเป็นครั้งแรก

  1. คลิกแท็บการออกแบบ รูปภาพหุ่นตายว่างเปล่าแล้ว แต่รูปภาพนี้จะไม่ปรากฏในตัวอย่างด้วย



    ค่อนข้างเป็นเรื่องปกติที่เนื้อหาการออกแบบอาจได้รับการกําหนดแบบไดนามิกในช่วงรันไทม์ เช่น แอปที่ดึงข้อมูลจากอินเทอร์เน็ตควรเริ่มต้นด้วยหน้าจอว่างหรือว่างเปล่า แต่จะมีประโยชน์เมื่อคุณออกแบบแอปให้แสดงข้อมูลตัวยึดตําแหน่งบางส่วนในเลย์เอาต์เพื่อให้คุณทราบข้อมูลที่กําลังวางอยู่
  2. ใน activity_layout.xml ให้คัดลอกบรรทัด android:src และวางสําเนาที่ 2 เปลี่ยนคํา "android" เป็น "tools" เพื่อให้แอตทริบิวต์ทั้ง 2 ของคุณมีลักษณะดังนี้
android:src="@drawable/empty_dice" 
tools:src="@drawable/empty_dice" />

นี่ คุณได้เปลี่ยนแปลงเนมสเปซ XML ของแอตทริบิวต์นี้จากเนมสเปซ android ที่เป็นค่าเริ่มต้นเป็นเนมสเปซ tools เนมสเปซ tools จะใช้ได้เมื่อคุณต้องการกําหนดเนื้อหาตัวยึดตําแหน่งที่ใช้เฉพาะในการแสดงตัวอย่างหรือตัวแก้ไขดีไซน์ใน Android Studio ระบบจะนําแอตทริบิวต์ที่ใช้เนมสเปซ tools ออกเมื่อคุณคอมไพล์แอป

เนมสเปซใช้ในการช่วยแก้ปัญหาความกํากวมเมื่อพูดถึงแอตทริบิวต์ที่มีชื่อเดียวกัน ตัวอย่างเช่น แอตทริบิวต์ทั้งสองในแท็ก <ImageView> มีชื่อเดียวกัน (src) แต่เนมสเปซต่างกัน

  1. ตรวจสอบองค์ประกอบ <LinearLayout> ที่รากของไฟล์เลย์เอาต์และสังเกตเนมสเปซทั้ง 2 รายการที่กําหนดไว้ที่นี่
<LinearLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   ...
  1. เปลี่ยนแอตทริบิวต์ tools:src ในแท็ก ImageView เป็น dice_1 แทน empty_dice ดังนี้
android:src="@drawable/empty_dice" 
tools:src="@drawable/dice_1" />

โปรดสังเกตว่ามีรูปภาพ dice_1 วางเป็นรูปภาพตัวยึดตําแหน่งในหน้าตัวอย่างแล้ว

  1. คอมไพล์และเรียกใช้แอป โปรดสังเกตว่ารูปภาพไดร์ฟว่างเปล่าในแอปจริงจนกว่าคุณจะคลิกหรือแตะกลิ้ง

ข้อดีอย่างหนึ่งของการพัฒนาแอป Android คือจํานวนโค้ดที่โค้ดใช้ได้

เมื่อเขียนสําหรับ Android คุณจะเขียนแอปแยกต่างหากสําหรับอุปกรณ์แต่ละเครื่องแยกกันไม่ได้ แม้กระทั่งแอปที่ทํางานโดยใช้รูปแบบที่แตกต่างกันอย่างสิ้นเชิง เช่น นาฬิกาและทีวี ก็จะแชร์รหัสได้ แต่ก็ยังมีข้อจํากัดและกลยุทธ์ความเข้ากันได้ที่คุณควรทราบเพื่อสนับสนุนการดําเนินการทั้งหมด

ในงานนี้ คุณจะได้ดูวิธีกําหนดเป้าหมายแอปสําหรับ Android API เวอร์ชันต่างๆ (เวอร์ชัน) และวิธีใช้ไลบรารี Android Jetpack เพื่อรองรับอุปกรณ์รุ่นเก่า

ขั้นตอนที่ 1: สํารวจระดับ API

ใน Codelab ก่อนหน้า เมื่อสร้างโปรเจ็กต์ คุณได้ระบุระดับ Android API ที่เจาะจงซึ่งแอปควรรองรับ ระบบปฏิบัติการ Android มีหมายเลขเวอร์ชันต่างกันซึ่งตั้งชื่อตามความสวยงามซึ่งเรียงตามลําดับตัวอักษร ระบบปฏิบัติการแต่ละเวอร์ชันจะจัดส่งด้วยฟังก์ชันและฟังก์ชันใหม่ๆ ตัวอย่างเช่น Android Oreo ส่งการรองรับแอปการแสดงภาพซ้อนภาพ ส่วน Android Pie ขอแนะนํา Slices ระดับ API จะสอดคล้องกับเวอร์ชัน Android เช่น API 19 สอดคล้องกับ Android 4.4 (KitKat)

เนื่องด้วยปัจจัยหลายประการ รวมถึงสิ่งที่ฮาร์ดแวร์รองรับได้ ผู้ใช้เลือกที่จะอัปเดตอุปกรณ์ของตนหรือไม่ และผู้ผลิตรองรับระบบปฏิบัติการระดับต่างๆ หรือไม่ ท้ายที่สุดแล้วผู้ใช้ก็ใช้งานอุปกรณ์เวอร์ชันระบบปฏิบัติการที่แตกต่างกัน

เมื่อสร้างโปรเจ็กต์แอป คุณต้องระบุระดับ API ขั้นต่ําที่แอปรองรับ กล่าวคือคุณระบุ Android เวอร์ชันเก่าที่สุดที่แอปรองรับ แอปยังมีระดับที่จะคอมไพล์ และระดับที่จะกําหนดเป้าหมายด้วย ในแต่ละระดับจะเป็นพารามิเตอร์การกําหนดค่าในไฟล์บิลด์ Gradle

  1. ขยายโฟลเดอร์ Gradle Scripts และเปิดไฟล์ build.gradle (โมดูล: แอป)

    ไฟล์นี้จะกําหนดพารามิเตอร์บิวด์และทรัพยากร Dependency สําหรับโมดูลแอปโดยเฉพาะ ไฟล์ build.gradle (โครงการ: DiceRoller) จะกําหนดพารามิเตอร์บิวด์สําหรับโครงการโดยรวม ในหลายๆ กรณี โมดูลแอปจะเป็นโมดูลเดียวในโปรเจ็กต์ของคุณ ดังนั้นการแบ่งแผนกนี้อาจมีลักษณะที่กําหนดเอง แต่หากแอปของคุณซับซ้อนขึ้นและแบ่งออกเป็นหลายส่วน หรือหากแอปรองรับแพลตฟอร์มอย่างเช่น นาฬิกา Android คุณอาจเห็นโมดูลต่างๆ ในโปรเจ็กต์เดียวกัน
  2. ตรวจสอบส่วน android ที่ด้านบนสุดของไฟล์ build.gradle (ตัวอย่างด้านล่างไม่ใช่ทั้งส่วน แต่ประกอบด้วยสิ่งที่คุณสนใจมากที่สุดสําหรับ Codelab นี้)
android {
   compileSdkVersion 28
   defaultConfig {
       applicationId "com.example.android.diceroller"
       minSdkVersion 19
       targetSdkVersion 28
       versionCode 1
       versionName "1.0"
   }
  1. ตรวจสอบพารามิเตอร์ compileSdkVersion
compileSdkVersion 28

พารามิเตอร์นี้ระบุระดับ API ของ Android ที่ Gradle ควรใช้เพื่อรวบรวมแอปของคุณ นี่เป็น Android เวอร์ชันล่าสุดที่แอปของคุณรองรับได้ ซึ่งหมายความว่าแอปของคุณอาจใช้ฟีเจอร์ API ที่รวมอยู่ในระดับ API นี้และต่ํากว่าได้ ในกรณีนี้ แอปของคุณรองรับ API 28 ซึ่งสอดคล้องกับ Android 9 (Pie)

  1. ตรวจสอบพารามิเตอร์ targetSdkVersion ซึ่งอยู่ในส่วน defaultConfig
targetSdkVersion 28

ค่านี้จะเป็น API ล่าสุดที่คุณใช้ทดสอบแอป ซึ่งในหลายๆ กรณี ค่านี้จะเป็นค่าเดียวกับ compileSdkVersion

  1. ตรวจสอบพารามิเตอร์ minSdkVersion
minSdkVersion 19

พารามิเตอร์นี้มีความสําคัญมากที่สุดจากทั้ง 3 แบบ เนื่องจากจะกําหนด Android เวอร์ชันเก่าที่สุดที่แอปของคุณจะทํางาน อุปกรณ์ที่ใช้ระบบปฏิบัติการ Android เก่ากว่าระดับ API นี้จะเรียกใช้แอปไม่ได้เลย

การเลือกระดับ API ขั้นต่ําสําหรับแอปอาจทําได้ยาก ตั้งระดับ API ต่ําเกินไปแล้วคุณจะพลาดฟีเจอร์ใหม่ๆ ของระบบปฏิบัติการ Android ตั้งให้สูงเกินไปและแอปของคุณอาจทํางานบนอุปกรณ์รุ่นใหม่ๆ เท่านั้น

เมื่อคุณตั้งค่าโปรเจ็กต์และขึ้นมาไปยังตําแหน่งที่คุณกําหนดระดับ API ขั้นต่ําสําหรับแอป ให้คลิกช่วยฉันเลือกเพื่อดูกล่องโต้ตอบการเผยแพร่เวอร์ชัน API กล่องโต้ตอบจะให้ข้อมูลเกี่ยวกับจํานวนอุปกรณ์ที่ใช้ระดับระบบปฏิบัติการที่แตกต่างกันและฟีเจอร์ที่มีการเปลี่ยนแปลงหรือเปลี่ยนแปลงในระดับระบบปฏิบัติการ นอกจากนี้ คุณยังดูบันทึกประจํารุ่นของเอกสารประกอบและหน้าแดชบอร์ดของ Android ซึ่งมีข้อมูลเพิ่มเติมเกี่ยวกับผลกระทบของการรองรับระดับ API ต่างๆ ได้อีกด้วย

ขั้นตอนที่ 2: สํารวจความเข้ากันได้

การเขียนแอปสําหรับ Android API ระดับต่างๆ เป็นความท้าทายที่นักพัฒนาแอปพบบ่อย ทีมเฟรมเวิร์กของ Android จึงได้ทํางานอย่างหนักเพื่อช่วยเหลือคุณ

ในปี 2011 ทีมได้ปล่อยไลบรารีการสนับสนุนแห่งแรก ซึ่งเป็นไลบรารีที่พัฒนาโดย Google ซึ่งนําเสนอชั้นเรียนที่ใช้งานร่วมกันได้แบบย้อนหลังและฟังก์ชันที่มีประโยชน์ ในปี 2018 Google ได้เปิดตัว Android Jetpack ซึ่งเป็นคอลเล็กชันที่ประกอบด้วยไลบรารีและฟังก์ชันต่างๆ ก่อนหน้าของไลบรารีการสนับสนุน ขณะเดียวกันก็ขยายในคลังการสนับสนุนด้วย

  1. เปิด MainActivity
  2. โปรดทราบว่าชั้นเรียน MainActivity ไม่ได้ขยายจาก Activity เอง แต่มาจาก AppCompatActivity
class MainActivity : AppCompatActivity() { 
...

AppCompatActivity เป็นคลาสความเข้ากันได้เพื่อให้กิจกรรมมีลักษณะเหมือนกันทั่วทั้งแพลตฟอร์มระบบปฏิบัติการต่างๆ

  1. คลิกสัญลักษณ์ + ข้างบรรทัดที่ขึ้นต้นด้วย import เพื่อขยายการนําเข้าสําหรับชั้นเรียนของคุณ โปรดทราบว่าระบบจะนําเข้าคลาส AppCompatActivity จากแพ็กเกจ androidx.appcompat.app เนมสเปซสําหรับไลบรารี Android Jetpack คือ androidx
  2. เปิด build.gradle (โมดูล: แอป) แล้วเลื่อนลงไปยังส่วนการขึ้นต่อกัน
dependencies {
   implementation fileTree(dir: 'libs', include: ['*.jar'])
   implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
   implementation 'androidx.appcompat:appcompat:1.0.0-beta01'
   implementation 'androidx.core:core-ktx:1.0.1'
   implementation 'androidx.constraintlayout:constraintlayout:1.1.2'
   testImplementation 'junit:junit:4.12'
   androidTestImplementation 'androidx.test:runner:1.1.0-alpha4'
   androidTestImplementation 
        'androidx.test.espresso:espresso-core:3.1.0-alpha4'
}

แสดงทรัพยากร Dependency ในไลบรารี appcompat ซึ่งเป็นส่วนหนึ่งของ androidx และมีคลาส AppCompatActivity

ขั้นตอนที่ 3: เพิ่มความเข้ากันได้สําหรับภาพวาดที่เวกเตอร์ได้

คุณกําลังจะใช้ความรู้ใหม่เกี่ยวกับเนมสเปซ Gradle และความเข้ากันได้เพื่อทําการปรับเปลี่ยนสุดท้ายในแอป ซึ่งจะช่วยเพิ่มประสิทธิภาพขนาดแอปในแพลตฟอร์มเก่า

  1. ขยายโฟลเดอร์ res แล้วขยาย Drawable ดับเบิลคลิกรูปภาพลูกเต๋า 1 ภาพ

    จากตัวอย่างก่อนหน้านี้ รูปภาพลูกเต๋าทั้งหมดเป็นไฟล์ XML ที่ระบุสีและรูปร่างของลูกเต๋า ไฟล์ประเภทนี้เรียกว่า Drawable Drawable ข้อดีของรูปแบบเวกเตอร์ที่ถอนออกได้กับรูปแบบบิตแมป เช่น PNG คือค่าที่วาดได้เวกเตอร์สามารถปรับขนาดได้โดยไม่สูญเสียคุณภาพ นอกจากนี้ เวกเตอร์ที่วาดได้ก็มักจะเป็นไฟล์ที่เล็กกว่ารูปภาพเดียวกันในรูปแบบบิตแมปมาก

    สิ่งสําคัญที่ควรทราบเกี่ยวกับวาดเขียนที่เวกเตอร์ได้คือรองรับใน API 21 แต่ SDK ขั้นต่ําของแอปได้รับการตั้งค่าเป็น API 19 หากคุณเคยลองใช้แอปในอุปกรณ์หรือโปรแกรมจําลอง API 19 แล้ว คุณก็พบว่าแอปสร้างและเริ่มทํางานได้เลย วิธีนี้ทํางานอย่างไร

    เมื่อคุณสร้างแอป กระบวนการสร้าง Gradle จะสร้างไฟล์ PNG จากไฟล์เวกเตอร์แต่ละไฟล์ และมีการใช้งานไฟล์ PNG เหล่านั้นในอุปกรณ์ Android รุ่นที่ต่ํากว่า 21 ไฟล์ PNG เพิ่มเติมเหล่านี้จะเพิ่มขนาดของแอป แอปขนาดใหญ่โดยไม่จําเป็นนั้นไม่ดีนัก เนื่องจากทําให้การดาวน์โหลดช้าลงสําหรับผู้ใช้และกินพื้นที่ของอุปกรณ์มากขึ้น' พื้นที่จํากัด แอปขนาดใหญ่ยังมีโอกาสที่จะถอนการติดตั้งสูงกว่า และผู้ใช้ดาวน์โหลดหรือดาวน์โหลดแอปเหล่านั้นไม่สําเร็จ

    ข่าวดีคือมีไลบรารีความเข้ากันได้ของ Android X สําหรับภาพวาดที่เวกเตอร์ได้จนถึง API ระดับ 7
  2. เปิด build.gradle (โมดูล: แอป) เพิ่มบรรทัดนี้ลงในส่วน defaultConfig:
vectorDrawables.useSupportLibrary = true
  1. คลิกปุ่มซิงค์เลย ทุกครั้งที่มีการแก้ไขไฟล์ build.gradle คุณจะต้องซิงค์ไฟล์บิลด์กับโปรเจ็กต์
  2. เปิดไฟล์รูปแบบ main_activity.xml เพิ่มเนมสเปซนี้ในแท็ก <LinearLayout> ราก ใต้เนมสเปซ tools:
xmlns:app="http://schemas.android.com/apk/res-auto"

เนมสเปซ app มีไว้สําหรับแอตทริบิวต์ที่มาจากโค้ดที่กําหนดเองหรือจากไลบรารี ไม่ใช่เฟรมเวิร์กหลักของ Android

  1. เปลี่ยนแอตทริบิวต์ android:src ในองค์ประกอบ <ImageView> เป็น app:srcCompat
app:srcCompat="@drawable/empty_dice"


แอตทริบิวต์ app:srcCompat ใช้ไลบรารี Android X เพื่อรองรับเวกเตอร์ที่ถอนออกได้ใน Android เวอร์ชันเก่า กลับไปที่ API ระดับ 7

  1. สร้างและเรียกใช้แอป คุณจะไม่เห็นสิ่งที่ต่างไปจากเดิมบนหน้าจอ แต่ตอนนี้แอปไม่ต้องใช้ #PNG ที่สร้างขึ้นสําหรับลูกเต๋าไม่ว่าจะวิ่งไปที่ใดก็ตาม ซึ่งหมายความว่าไฟล์แอปขนาดเล็กกว่า

โปรเจ็กต์ Android Studio: DiceRollerFinal

ความท้าทาย: แก้ไขแอป DiceRoller ให้มีลูกเต๋า 2 ลูก เมื่อผู้ใช้แตะปุ่มม้วน แต่ละไดร์ฟควรมีค่าเป็นอิสระต่อกัน

เคล็ดลับ: สร้างฟังก์ชันส่วนตัวใหม่เพื่อรับรูปภาพที่สุ่มได้และแสดงผลจํานวนเต็มสําหรับทรัพยากรที่ดึงได้ ใช้ฟังก์ชันนั้นสําหรับรูปภาพแต่ละรูป

private fun getRandomDiceImage() : Int { ... }

โค้ดโซลูชันสําหรับการท้าโค้ด

โปรเจ็กต์ Android Studio: DiceRollerFinal-challenge

แหล่งข้อมูลของแอป:

  • ทรัพยากรของแอปอาจรวมถึงรูปภาพและไอคอน สีมาตรฐานที่ใช้ในแอป สตริง และเลย์เอาต์ XML ระบบจะเก็บทรัพยากรทั้งหมดไว้ในโฟลเดอร์ res
  • โฟลเดอร์ทรัพยากร drawable คือที่ให้คุณวางทรัพยากรรูปภาพทั้งหมดสําหรับแอป

การใช้เวกเตอร์ที่ถอนออกได้ในมุมมองรูปภาพ

  • เวกเตอร์ที่ถอนออกได้คือรูปภาพที่อธิบายในรูปแบบ XML เวกเตอร์ที่ถอนออกได้มีความยืดหยุ่นกว่าภาพบิตแมป (เช่น ไฟล์ PNG) เนื่องจากสามารถปรับขนาดเป็นขนาดหรือความละเอียดใดก็ได้
  • หากต้องการเพิ่มภาพวาดที่วาดได้ลงในเลย์เอาต์ของแอป ให้ใช้องค์ประกอบ <ImageView> แหล่งที่มาของรูปภาพอยู่ในแอตทริบิวต์ android:src หากต้องการอ้างอิงโฟลเดอร์ทรัพยากรที่ถอนออกได้ ให้ใช้ @drawable เช่น "@drawable/image_name"
  • ใช้มุมมอง ImageView ในโค้ด MainActivity สําหรับรูปภาพ คุณใช้ setImageResource() เพื่อเปลี่ยนรูปภาพมุมมองเป็นทรัพยากรอื่นได้ ใช้ R.drawable เพื่ออ้างถึงทรัพยากรที่ถอนออกได้ เช่น setImageResource(R.drawable.image_name)

คีย์เวิร์ด lateinit คํานี้

  • ย่อการเรียก findViewById() ในโค้ดโดยประกาศช่องเพื่อคงการดูเหล่านั้นไว้และเริ่มต้นช่องใน onCreate() ใช้คีย์เวิร์ด lateinit กับช่องนี้เพื่อหลีกเลี่ยงไม่ให้ต้องประกาศเป็นค่าว่าง

เนมสเปซ tools สําหรับแอตทริบิวต์เวลาออกแบบ:

  • ใช้แอตทริบิวต์ tools:src ในองค์ประกอบ <ImageView> ในเลย์เอาต์เพื่อแสดงรูปภาพเฉพาะในตัวอย่างหรือตัวแก้ไขดีไซน์ของ Android Studio&#39 จากนั้นคุณจะใช้รูปภาพที่ว่างเปล่าของ android:src สําหรับแอปสุดท้ายได้
  • ใช้เนมสเปซ tools ในไฟล์เลย์เอาต์ของ Android เพื่อสร้างเนื้อหาตัวยึดตําแหน่งหรือคําแนะนําสําหรับเลย์เอาต์ใน Android Studio ระบบจะไม่ใช้ข้อมูลที่ประกาศโดยแอตทริบิวต์ tools ในแอปสุดท้าย

ระดับ API

  • ระบบปฏิบัติการ Android แต่ละระบบจะมีหมายเลขเวอร์ชันและชื่ออย่างเป็นทางการ (เช่น Android 9.0, "Pie") และระดับ API (API 28) ใช้ระดับ API ในไฟล์ Gradle ของแอปเพื่อระบุเวอร์ชันของ Android ที่แอปของคุณรองรับ
  • พารามิเตอร์ compileSdkVersion ในไฟล์ build.gradle จะระบุระดับ Android API ที่ Gradle ควรใช้เพื่อรวบรวมแอปของคุณ
  • พารามิเตอร์ targetSdkVersion จะระบุระดับ API ล่าสุดที่คุณทําการทดสอบกับแอป ในหลายกรณี พารามิเตอร์นี้จะมีค่าเหมือนกับ compileSdkVersion
  • พารามิเตอร์ minSdkVersion จะระบุระดับ API ที่เก่าที่สุดที่แอปสามารถทําได้

Android Jetpack:

  • Android Jetpack คือคอลเล็กชันของไลบรารีที่พัฒนาโดย Google ซึ่งนําเสนอคลาสที่เข้ากันได้กับ Google และฟังก์ชันที่มีประโยชน์สําหรับการรองรับ Android เวอร์ชันเก่า Jetpack จะมาแทนที่และขยายชุดไลบรารีที่เคยใช้ชื่อว่า Android Support Library
  • คลาสที่นําเข้าจากแพ็กเกจ androidx หมายถึงไลบรารี Jetpack การขึ้นต่อกันของ Jetpack ในไฟล์ build.gradle จะเริ่มต้นด้วย androidx

ความเข้ากันได้แบบย้อนหลังสําหรับภาพวาดที่เวกเตอร์ได้

  • เวอร์ชันวาดเขียนที่วาดได้จะใช้ได้เฉพาะใน Android เวอร์ชันสูงกว่า API 21 เท่านั้น ในเวอร์ชันเก่า Gradle จะสร้างรูปภาพ PNG สําหรับสิ่งที่วาดได้เหล่านั้นเมื่อสร้างแอป
  • คุณระบุได้ว่าควรใช้ไลบรารีการสนับสนุนของ Android สําหรับทรัพยากรที่ถอนออกได้ใน API เวอร์ชันเก่าด้วยพารามิเตอร์การกําหนดค่า vectorDrawables.useSupportLibrary = true ในไฟล์ build.gradle
  • เมื่อคุณเปิดใช้ไลบรารีการสนับสนุนสําหรับภาพวาดที่เวกเตอร์ได้ ให้ใช้แอตทริบิวต์ app:srcCompat ในองค์ประกอบ <ImageView> (แทน android:src) เพื่อระบุแหล่งข้อมูลที่วาดเวกเตอร์สําหรับรูปภาพนั้นได้

เนมสเปซ app:

  • เนมสเปซ app ในไฟล์เลย์เอาต์ XML มีไว้สําหรับแอตทริบิวต์ที่มาจากโค้ดที่กําหนดเองหรือจากไลบรารี ไม่ใช่จากเฟรมเวิร์กของ Android หลัก

หลักสูตร Udacity:

เอกสารประกอบสําหรับนักพัฒนาซอฟต์แวร์ Android

อื่นๆ:

ส่วนนี้จะอธิบายการบ้านและรายงานสําหรับนักเรียนที่ทํางานผ่าน Codelab นี้ซึ่งเป็นส่วนหนึ่งของหลักสูตรที่นําโดยผู้สอน สิ่งที่ผู้สอนต้องทํามีดังนี้

  • มอบหมายการบ้านหากจําเป็น
  • สื่อสารกับนักเรียนเกี่ยวกับวิธีส่งงานทําการบ้าน
  • ตัดเกรดการบ้าน

ผู้สอนจะใช้คําแนะนําเหล่านี้เท่าใดก็ได้หรือตามที่ต้องการก็ได้ และสามารถกําหนดให้การบ้านอื่นๆ ที่ตนคิดว่าเหมาะสมได้

หากคุณใช้ Codelab ด้วยตัวเอง ก็ให้ใช้การบ้านเพื่อทดสอบความรู้ของคุณได้

เปลี่ยนแอป

เพิ่มปุ่มล้างในแอป DiceRoller ซึ่งตั้งค่ารูปภาพเทคนิคกลับไปเป็นรูปภาพว่างเปล่า

ตอบคําถามเหล่านี้

คำถามที่ 1

แอตทริบิวต์ <ImageView> ใดระบุรูปภาพต้นฉบับที่ควรใช้เฉพาะใน Android Studio

  • android:srcCompat
  • app:src
  • tools:src
  • tools:sourceImage

คำถามที่ 2

วิธีใดจะเปลี่ยนทรัพยากรรูปภาพของ ImageView ในโค้ด Kotlin xmx

  • setImageResource()
  • setImageURI()
  • setImage()
  • setImageRes()

คำถามที่ 3

คีย์เวิร์ด lateinit ในการประกาศตัวแปรระบุอะไรในโค้ดของ Kotlin

  • ห้ามเริ่มต้นตัวแปร
  • ตัวแปรเริ่มต้นเมื่อรันไทม์แอปเท่านั้น
  • ตัวแปรเริ่มต้นเป็น null โดยอัตโนมัติ
  • ระบบจะเริ่มต้นตัวแปรในภายหลัง สาบาน

คำถามที่ 4

การกําหนดค่า Gradle ใดบ่งบอกถึงระดับ API ล่าสุดที่แอปของคุณได้รับการทดสอบ

  • minSdkVersion
  • compileSdkVersion
  • targetSdkVersion
  • testSdkVersion

คำถามที่ 5

คุณจะเห็นบรรทัดการนําเข้าในโค้ดที่ขึ้นต้นด้วย androidx นี่หมายความว่าอย่างไร

  • ชั้นเรียนนี้เป็นส่วนหนึ่งของไลบรารี Android Jetpack
  • ชั้นเรียนจะอยู่ในไลบรารีภายนอกที่จะโหลดแบบไดนามิกเมื่อแอปทํางาน
  • &classt;extra" เป็นตัวเลือกที่ไม่บังคับสําหรับชั้นเรียนของคุณ
  • ชั้นเรียนนี้เป็นส่วนหนึ่งของการรองรับ XML ของ Android

ส่งแอปเพื่อตัดเกรด

ตรวจสอบว่าแอปมีคุณสมบัติต่อไปนี้

  • เลย์เอาต์ของแอปควรมีมุมมองรูปภาพ 1 ปุ่มและปุ่ม 2 ปุ่ม
  • โค้ดของแอปควรตั้งค่าเครื่องจัดการการคลิก 2 รายการ โดย 1 รายการสําหรับปุ่มแต่ละปุ่ม
  • เครื่องจัดการคลิกสําหรับปุ่มล้างควรตั้งค่ารูปภาพเล็กเป็น R.drawable.empty_dice

เริ่มบทเรียนถัดไป: 1.4: เรียนรู้เพื่อช่วยเหลือตัวเอง

สําหรับลิงก์ไปยัง Codelab อื่นๆ ในหลักสูตรนี้ โปรดดูหน้า Landing Page ของ Codelab ของ Android Kotlin Fundamentals