หัวข้อนี้มุ่งเน้นที่บางแง่มุมที่มีประโยชน์ที่สุดของภาษา Kotlin เมื่อพัฒนาสำหรับ Android
ทำงานกับ Fragment
ส่วนต่อไปนี้ใช้ตัวอย่าง Fragment
เพื่อไฮไลต์
ที่ดีที่สุด
การรับค่า
คุณประกาศคลาสใน Kotlin ได้ด้วยคีย์เวิร์ด class
ในรายการต่อไปนี้
เช่น LoginFragment
เป็นคลาสย่อยของ Fragment
คุณสามารถระบุ
การรับช่วงค่าโดยใช้โอเปอเรเตอร์ :
ระหว่างคลาสย่อยและคลาสย่อย:
class LoginFragment : Fragment()
ในการประกาศของชั้นเรียนนี้ LoginFragment
จะเป็นผู้รับผิดชอบการเรียกใช้การเรียก
ตัวสร้างของซูเปอร์คลาส Fragment
ภายใน LoginFragment
คุณสามารถลบล้างจํานวน Callback ของวงจรเป็น
ตอบสนองต่อการเปลี่ยนแปลงสถานะใน Fragment
ของคุณ หากต้องการลบล้างฟังก์ชัน ให้ใช้พารามิเตอร์
คีย์เวิร์ด override
คำดังที่ปรากฏในตัวอย่างต่อไปนี้
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.login_fragment, container, false)
}
หากต้องการอ้างอิงฟังก์ชันในชั้นเรียนหลัก ให้ใช้คีย์เวิร์ด super
ดังที่แสดง
ในตัวอย่างต่อไปนี้
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
ความสามารถในการเว้นว่างและการเริ่มต้น
ในตัวอย่างก่อนหน้านี้ พารามิเตอร์บางตัวในเมธอดที่ถูกลบล้างได้
ประเภทที่ต่อท้ายด้วยเครื่องหมายคำถาม ?
ซึ่งเป็นการระบุว่าอาร์กิวเมนต์
ส่งผ่านสำหรับพารามิเตอร์เหล่านี้อาจเป็นค่าว่าง อย่าลืม
จัดการกับการเว้นว่างของผู้ใช้ได้อย่างปลอดภัย
ใน Kotlin คุณต้องเริ่มต้นคุณสมบัติของออบเจ็กต์เมื่อประกาศออบเจ็กต์
หมายความว่า เมื่อคุณได้รับอินสแตนซ์
ของคลาส คุณสามารถ
อ้างอิงพร็อพเพอร์ตี้ที่เข้าถึงได้ ออบเจ็กต์ View
ใน Fragment
แต่ยังไม่พร้อมปรับค่าโฆษณา จนกว่าจะโทรติดต่อ Fragment#onCreateView
คุณต้องมีวิธีเลื่อนการเริ่มต้นพร็อพเพอร์ตี้สำหรับ View
lateinit
ช่วยให้คุณเลื่อนการเริ่มต้นพร็อพเพอร์ตี้ได้ เมื่อใช้ lateinit
คุณควรเริ่มต้นพร็อพเพอร์ตี้โดยเร็วที่สุด
ตัวอย่างต่อไปนี้สาธิตการใช้ lateinit
เพื่อกำหนดออบเจ็กต์ View
ใน
onViewCreated
:
class LoginFragment : Fragment() {
private lateinit var usernameEditText: EditText
private lateinit var passwordEditText: EditText
private lateinit var loginButton: Button
private lateinit var statusTextView: TextView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
usernameEditText = view.findViewById(R.id.username_edit_text)
passwordEditText = view.findViewById(R.id.password_edit_text)
loginButton = view.findViewById(R.id.login_button)
statusTextView = view.findViewById(R.id.status_text_view)
}
...
}
Conversion ของ SAM
คุณสามารถเฝ้าติดตามกิจกรรมการคลิกใน Android ได้โดยการใช้
อินเทอร์เฟซของ OnClickListener
ออบเจ็กต์ Button
รายการมี setOnClickListener()
ที่จะติดตั้งใช้งาน OnClickListener
OnClickListener
มีวิธีการแบบนามธรรมวิธีเดียว คือ onClick()
ซึ่งคุณต้อง
สำหรับการใช้งานจริง เนื่องจาก setOnClickListener()
จะนำ OnClickListener
เป็น
อาร์กิวเมนต์ และเนื่องจาก OnClickListener
มีนามธรรมเดี่ยวๆ เสมอ
เมธอด การติดตั้งใช้งานนี้สามารถแสดงโดยใช้ฟังก์ชันที่ไม่ระบุชื่อใน
Kotlin กระบวนการนี้เรียกว่า
การแปลงวิธีนามธรรมเดี่ยว
หรือ SAM Conversion
Conversion ของ SAM ทำให้โค้ดของคุณดูสะอาดตาขึ้นมาก ตัวอย่างต่อไปนี้
แสดงวิธีใช้ Conversion ของ SAM เพื่อใช้ OnClickListener
สำหรับ
Button
:
loginButton.setOnClickListener {
val authSuccessful: Boolean = viewModel.authenticate(
usernameEditText.text.toString(),
passwordEditText.text.toString()
)
if (authSuccessful) {
// Navigate to next screen
} else {
statusTextView.text = requireContext().getString(R.string.auth_failed)
}
}
รหัสภายในฟังก์ชันที่ไม่ระบุตัวตนที่ส่งไปยัง setOnClickListener()
ทำงานเมื่อผู้ใช้คลิก loginButton
ออบเจ็กต์ร่วม
ออบเจ็กต์ที่แสดงร่วม
มีกลไกสำหรับการกำหนดตัวแปรหรือฟังก์ชันที่จะลิงก์
อยู่ในแนวคิดประเภทหนึ่งๆ แต่ไม่ได้เชื่อมโยงกับวัตถุใดวัตถุหนึ่ง แยกหน้าจอประชุม
จะคล้ายกับการใช้คีย์เวิร์ด static
ของ Java เพื่อหาตัวแปรและเมธอด
ในตัวอย่างต่อไปนี้ TAG
เป็นค่าคงที่ String
คุณไม่จำเป็นต้องมี
ของ String
สำหรับแต่ละอินสแตนซ์ของ LoginFragment
ดังนั้นคุณควร
ให้คำจำกัดความในออบเจ็กต์การแสดงร่วม
class LoginFragment : Fragment() {
...
companion object {
private const val TAG = "LoginFragment"
}
}
คุณอาจกำหนด TAG
ที่ระดับบนสุดของไฟล์ แต่
อาจมีตัวแปร ฟังก์ชัน และคลาสจำนวนมากด้วย
ที่กำหนดไว้ที่ระดับบนสุดด้วย ออบเจ็กต์ที่แสดงร่วมช่วยในการเชื่อมต่อ
ตัวแปร ฟังก์ชัน และคำจำกัดความของคลาสโดยไม่อ้างอิง
อินสแตนซ์หนึ่งๆ ของคลาสนั้นๆ
การมอบสิทธิ์พร็อพเพอร์ตี้
ขณะเริ่มต้นพร็อพเพอร์ตี้ คุณอาจต้องทำซ้ำรายการตามปกติของ Android
เช่น การเข้าถึง ViewModel
ภายใน Fragment
เพื่อหลีกเลี่ยงส่วนเกิน
โค้ดซ้ำกัน ให้ใช้ไวยากรณ์การมอบสิทธิ์พร็อพเพอร์ตี้ของ Kotlin ได้
private val viewModel: LoginViewModel by viewModels()
การมอบสิทธิ์พร็อพเพอร์ตี้มีการติดตั้งใช้งานทั่วไปที่คุณสามารถนํามาใช้ใหม่ได้
ในแอป Android KTX มอบสิทธิ์พร็อพเพอร์ตี้ให้คุณบางส่วน
ตัวอย่างเช่น viewModels
จะเรียกข้อมูล ViewModel
ที่กำหนดขอบเขตไว้เป็น
Fragment
ปัจจุบัน
การมอบสิทธิ์พร็อพเพอร์ตี้ใช้การสะท้อน ซึ่งเพิ่มโอเวอร์เฮดประสิทธิภาพบางส่วน โดยมีข้อเสียคือไวยากรณ์ที่สั้นกระชับซึ่งช่วยประหยัดเวลาในการพัฒนา
ความสามารถในการเว้นว่าง
Kotlin มีกฎความสามารถในการเว้นว่างที่เข้มงวดซึ่งจะรักษาระดับความปลอดภัยตามประเภททั้งหมด
แอปของคุณ ใน Kotlin การอ้างอิงออบเจ็กต์ต้องไม่มีค่า Null
"ค่าเริ่มต้น" หากต้องการกำหนดค่า Null ให้กับตัวแปร คุณต้องประกาศ nullable
ประเภทตัวแปรได้โดยการเพิ่ม ?
ต่อท้ายประเภทฐาน
ตัวอย่างเช่น นิพจน์ต่อไปนี้ใน Kotlin ผิดกฎหมาย name
เป็นประเภท
String
และไม่สามารถเป็นค่าว่าง
val name: String = null
หากต้องการอนุญาตค่า Null คุณต้องใช้ประเภท String
ที่ไม่มีข้อมูล ซึ่งก็คือ String?
เป็น
ที่แสดงในตัวอย่างต่อไปนี้
val name: String? = null
ความสามารถในการทำงานร่วมกัน
กฎที่เข้มงวดของ Kotlin ทำให้โค้ดของคุณปลอดภัยและกระชับยิ่งขึ้น กฎเหล่านี้ต่ำกว่า
โอกาสในการมี NullPointerException
ที่จะทำให้แอป
ขัดข้อง นอกจากนี้ ยังช่วยลดจำนวนของการตรวจสอบ Null ที่คุณต้องดำเนินการในบัญชี
โค้ด
บ่อยครั้งคุณต้องเรียกใช้โค้ดที่ไม่ใช่ Kotlin เมื่อเขียนแอป Android ด้วย เช่น API ของ Android ส่วนใหญ่เขียนด้วยภาษาโปรแกรม Java
ความสามารถในการเว้นว่างคือส่วนสำคัญที่ Java และ Kotlin มีลักษณะการทำงานที่แตกต่างกัน Java มีราคาน้อยกว่า ที่เข้มงวดด้วยไวยากรณ์ที่ไม่มีข้อมูล
ตัวอย่างเช่น คลาส Account
มีพร็อพเพอร์ตี้หลายรายการ รวมถึง String
ชื่อ name
Java ไม่มีกฎของ Kotlin เกี่ยวกับความสามารถในการเว้นว่าง
การพึ่งพาคำอธิบายประกอบที่เป็นค่าว่างซึ่งไม่บังคับ เพื่อประกาศ
คุณสามารถกำหนดค่า Null ได้หรือไม่
เนื่องจากเฟรมเวิร์ก Android นั้นเขียนโดยใช้ Java เป็นหลัก คุณจึงอาจพบกับ ในสถานการณ์นี้เมื่อเรียกใช้ API โดยไม่มีคำอธิบายประกอบความสามารถในการเว้นว่าง
ประเภทแพลตฟอร์ม
หากคุณใช้ Kotlin เพื่ออ้างอิงสมาชิก name
ที่ไม่มีคำอธิบายประกอบซึ่งกำหนดไว้ใน
คลาส Java Account
คอมไพเลอร์จะไม่ทราบว่า String
จับคู่กับ
String
หรือ String?
ใน Kotlin ความคลุมเครือนี้จะแสดงผ่าน
ประเภทแพลตฟอร์ม, String!
String!
ไม่ได้มีความหมายพิเศษต่อคอมไพเลอร์ Kotlin String!
สามารถนำเสนอ
String
หรือ String?
และคอมไพเลอร์จะให้คุณกำหนดค่า
ประเภทใดก็ได้ โปรดทราบว่าคุณอาจเสี่ยงต่อการส่ง NullPointerException
หาก
แสดงประเภทเป็น String
และกําหนดค่า Null
เพื่อแก้ไขปัญหานี้ คุณควรใช้คำอธิบายประกอบที่ไม่มีข้อมูลเมื่อคุณเขียน ใน Java หมายเหตุเหล่านี้ช่วยนักพัฒนา Java และ Kotlin ได้
ลองดูตัวอย่างคลาส Account
ตามที่กำหนดไว้ใน Java ดังนี้
public class Account implements Parcelable {
public final String name;
public final String type;
private final @Nullable String accessId;
...
}
ตัวแปรสมาชิกตัวหนึ่ง accessId
มีคำอธิบายประกอบเป็น @Nullable
ซึ่งระบุว่าสามารถเก็บค่า Null ได้ Kotlin จะปฏิบัติต่อ accessId
ในฐานะ String?
หากต้องการระบุว่าตัวแปรต้องไม่เป็นค่าว่าง ให้ใช้คำอธิบายประกอบ @NonNull
ดังนี้
public class Account implements Parcelable {
public final @NonNull String name;
...
}
ในสถานการณ์นี้ name
จะถือเป็น String
ที่เป็นค่าว่างไม่ได้ใน Kotlin
คำอธิบายประกอบ Nullability จะรวมอยู่ใน Android API ใหม่ทั้งหมดและหลาย API ที่มีอยู่แล้ว Android API ไลบรารี Java จำนวนมากได้เพิ่มคำอธิบายประกอบ Nullability เพื่อให้ รองรับทั้งนักพัฒนาซอฟต์แวร์ Kotlin และ Java
การจัดการความสามารถในการเว้นว่าง
หากคุณไม่แน่ใจเกี่ยวกับประเภท Java คุณควรพิจารณาว่าประเภทนั้นเป็นโมฆะ
ยกตัวอย่างเช่น สมาชิก name
ของชั้นเรียน Account
ไม่มีการใส่คำอธิบายประกอบ คุณจึง
ควรถือว่าเป็น String
ที่ไม่มีข้อมูล
หากต้องการตัด name
เพื่อให้ค่าไม่มีเครื่องหมายนำหน้าหรือ
ต่อท้ายด้วยช่องว่าง คุณสามารถใช้ฟังก์ชัน trim
ของ Kotlin ได้ คุณสามารถตัด
String?
หลากหลายวิธี หนึ่งในวิธีเหล่านี้คือการใช้แอตทริบิวต์ not-null
โอเปอเรเตอร์การยืนยัน !!
ดังที่แสดงในตัวอย่างต่อไปนี้
val account = Account("name", "type")
val accountName = account.name!!.trim()
ตัวดำเนินการ !!
จะถือว่าทุกอย่างที่อยู่ทางซ้ายมือเป็นแบบไม่ใช่ค่าว่าง ดังนั้นใน
ในกรณีนี้ คุณกำลังถือว่า name
เป็น String
ที่ไม่เป็นค่าว่าง หากผลลัพธ์ของ
นิพจน์ไปทางซ้ายมีค่าเป็น null แสดงว่าแอปของคุณแสดง NullPointerException
โอเปอเรเตอร์นี้รวดเร็วและง่ายดาย แต่ควรใช้งานอย่างจำกัดเนื่องจาก
แนะนำอินสแตนซ์ของ NullPointerException
อีกครั้งในโค้ดของคุณ
ตัวเลือกที่ปลอดภัยกว่าคือการใช้โอเปอเรเตอร์ Safe-Call, ?.
ดังที่แสดงใน
ตัวอย่างต่อไปนี้
val account = Account("name", "type")
val accountName = account.name?.trim()
การใช้โอเปอเรเตอร์ Safe-call หาก name
เป็นค่าว่าง ผลลัพธ์จะเป็น
name?.trim()
คือค่าชื่อที่ไม่มีช่องว่างนำหน้าหรือต่อท้าย ถ้า
name
เป็นค่าว่าง จากนั้นผลลัพธ์ของ name?.trim()
จะเป็น null
ซึ่งหมายความว่า
แอปของคุณต้องไม่ส่ง NullPointerException
เมื่อเรียกใช้คำสั่งนี้
แม้ว่าโอเปอเรเตอร์การโทรช่วยให้คุณไม่ต้องเสียเวลาจากNullPointerException
ที่อาจเกิดขึ้น
จะส่งค่า Null ไปยังคำสั่งถัดไป คุณจัดการค่า Null แทนได้
กรณีทันทีโดยใช้โอเปอเรเตอร์ Elvis (?:
) ดังที่แสดงใน
ตัวอย่าง:
val account = Account("name", "type")
val accountName = account.name?.trim() ?: "Default name"
ถ้าผลลัพธ์ของนิพจน์ทางด้านซ้ายมือของโอเปอเรเตอร์ของเอลวิสคือ
null ค่าทางด้านขวามือจะกำหนดให้กับ accountName
ช่วงเวลานี้
มีประโยชน์สำหรับการระบุค่าเริ่มต้นที่อาจเป็นค่าว่าง
หรือจะใช้โอเปอเรเตอร์ Elvis เพื่อออกจากฟังก์ชันก่อนก็ได้ ตามที่แสดง ในตัวอย่างต่อไปนี้
fun validateAccount(account: Account?) {
val accountName = account?.name?.trim() ?: "Default name"
// account cannot be null beyond this point
account ?: return
...
}
การเปลี่ยนแปลง API ของ Android
API ของ Android มีความเป็นมิตรกับ Kotlin มากขึ้น Android หลายรุ่น
API ที่พบบ่อยที่สุด รวมถึง AppCompatActivity
และ Fragment
มี
คำอธิบายประกอบที่ไม่มีข้อมูล และการเรียกบางรายการ เช่น Fragment#getContext
ทางเลือกอื่นๆ ที่ใช้กับ Kotlin ได้
ตัวอย่างเช่น การเข้าถึง Context
ของ Fragment
มักจะไม่เป็นค่าว่าง
เนื่องจากการโทรส่วนใหญ่ที่คุณทำใน Fragment
เกิดขึ้นขณะที่ Fragment
แนบอยู่กับ Activity
(คลาสย่อยของ Context
) อย่างไรก็ตาม
Fragment#getContext
ไม่ได้แสดงผลเป็นค่าที่ไม่ใช่ค่าว่างเสมอไป เนื่องจากมี
สถานการณ์ที่ Fragment
ไม่ได้แนบกับ Activity
ดังนั้น ผลลัพธ์ที่ได้
ประเภทของ Fragment#getContext
จะเว้นว่างไม่ได้
เนื่องจาก Context
ที่แสดงผลจาก Fragment#getContext
เป็นค่าว่าง (และ
มีคำอธิบายประกอบเป็น @Nullable) คุณจะต้องตั้งค่าเป็น Context?
ในโค้ด Kotlin
ซึ่งหมายถึงการใช้โอเปอเรเตอร์รายการใดรายการหนึ่งที่กล่าวถึงก่อนหน้านี้เพื่อจัดการกับ
ความสามารถในการเว้นว่างก่อนที่จะเข้าถึงคุณสมบัติและฟังก์ชัน สำหรับเพลงเหล่านี้บางรายการ
อย่างเช่น Android มี API อื่นๆ ที่ให้ความสะดวกสบายนี้
เช่น Fragment#requireContext
แสดงผล Context
ที่ไม่เป็นค่าว่าง และส่ง
IllegalStateException
หากเรียกใช้เมื่อ Context
จะเป็นค่าว่าง ด้วยวิธีนี้
คุณสามารถถือว่า Context
ที่เป็นผลลัพธ์ไม่เป็นค่าว่างได้โดยไม่ต้อง
ผู้ให้บริการที่ปลอดภัยหรือวิธีแก้ปัญหาเบื้องต้น
การเริ่มต้นพร็อพเพอร์ตี้
พร็อพเพอร์ตี้ใน Kotlin ไม่ได้เริ่มต้นโดยค่าเริ่มต้น ต้องเริ่มต้น เมื่อเริ่มชั้นเรียนที่ล้อมรอบอยู่
คุณเริ่มต้นพร็อพเพอร์ตี้ได้หลายวิธี ตัวอย่างต่อไปนี้
แสดงวิธีเริ่มต้นตัวแปร index
โดยการกำหนดค่าให้กับตัวแปรใน
การประกาศคลาส:
class LoginFragment : Fragment() {
val index: Int = 12
}
คุณกำหนดการเริ่มต้นนี้ไว้ในบล็อกการเริ่มต้นได้ด้วย โดยทำดังนี้
class LoginFragment : Fragment() {
val index: Int
init {
index = 12
}
}
ในตัวอย่างด้านบน index
จะเริ่มทํางานเมื่อ LoginFragment
คือ
ขึ้น
อย่างไรก็ตาม คุณอาจมีพร็อพเพอร์ตี้บางรายการที่ไม่สามารถเริ่มต้นระหว่างออบเจ็กต์
การก่อสร้าง ตัวอย่างเช่น คุณอาจต้องการอ้างอิง View
จากภายใน
Fragment
ซึ่งหมายความว่าเลย์เอาต์จะต้องพองขึ้นก่อน ภาวะเงินเฟ้อ
จะไม่เกิดขึ้นเมื่อสร้าง Fragment
แต่จะสูงเกินจริงเมื่อโทร
Fragment#onCreateView
วิธีหนึ่งในการจัดการสถานการณ์นี้คือการประกาศมุมมองเป็นค่า Null และ ควรเริ่มต้นโดยเร็วที่สุด ดังที่แสดงในตัวอย่างต่อไปนี้
class LoginFragment : Fragment() {
private var statusTextView: TextView? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
statusTextView = view.findViewById(R.id.status_text_view)
statusTextView?.setText(R.string.auth_failed)
}
}
แม้ว่าการดำเนินการนี้จะทำงานได้ตามที่คาดไว้ แต่ในขณะนี้คุณต้องจัดการความสามารถในการเว้นว่างของ View
ทุกครั้งที่อ้างอิง วิธีแก้ไขที่ดีกว่าคือใช้ lateinit
สำหรับ View
การเริ่มต้น ดังที่แสดงในตัวอย่างต่อไปนี้
class LoginFragment : Fragment() {
private lateinit var statusTextView: TextView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
statusTextView = view.findViewById(R.id.status_text_view)
statusTextView.setText(R.string.auth_failed)
}
}
คีย์เวิร์ด lateinit
ช่วยหลีกเลี่ยงการเริ่มต้นพร็อพเพอร์ตี้เมื่อ
ขึ้น หากมีการอ้างอิงพร็อพเพอร์ตี้ก่อนเริ่มต้น
Kotlin โยน UninitializedPropertyAccessException
ดังนั้นอย่าลืม
เริ่มต้นพร็อพเพอร์ตี้ของคุณโดยเร็วที่สุด