การเชื่อมโยงมุมมอง   เป็นส่วนหนึ่งของ Android Jetpack

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

ในกรณีส่วนใหญ่ การเชื่อมโยงมุมมองจะแทนที่ findViewById

ตั้งค่า

การเชื่อมโยงมุมมองจะเปิดใช้ทีละโมดูล หากต้องการเปิดใช้การเชื่อมโยงมุมมองในข้อบังคับ ให้ตั้งค่าตัวเลือกการสร้าง viewBinding เป็น true ในไฟล์ build.gradle ระดับข้อบังคับ ดังที่แสดงในตัวอย่างต่อไปนี้

Groovy

android {
    ...
    buildFeatures {
        viewBinding true
    }
}

Kotlin

android {
    ...
    buildFeatures {
        viewBinding = true
    }
}

หากต้องการให้ระบบละเว้นไฟล์เลย์เอาต์ขณะสร้างคลาสการเชื่อมโยง ให้เพิ่มแอตทริบิวต์ tools:viewBindingIgnore="true" ลงในมุมมองรูทของไฟล์เลย์เอาต์นั้น

<LinearLayout
        ...
        tools:viewBindingIgnore="true" >
    ...
</LinearLayout>

การใช้งาน

หากเปิดใช้การเชื่อมโยงมุมมองสําหรับโมดูล ระบบจะสร้างคลาสการเชื่อมโยงสําหรับไฟล์เลย์เอาต์ XML แต่ละไฟล์ที่โมดูลมี คลาสการเชื่อมโยงแต่ละคลาสมีการอ้างอิงถึงมุมมองรูทและมุมมองทั้งหมดที่มีรหัส ระบบจะสร้างชื่อคลาสการเชื่อมโยงโดยแปลงชื่อไฟล์ XML เป็นรูปแบบ Pascal Case และเพิ่มคําว่า "Binding" ต่อท้าย

ตัวอย่างเช่น ลองพิจารณาไฟล์เลย์เอาต์ชื่อ result_profile.xml ที่มีข้อมูลต่อไปนี้

<LinearLayout ... >
    <TextView android:id="@+id/name" />
    <ImageView android:cropToPadding="true" />
    <Button android:id="@+id/button"
        android:background="@drawable/rounded_button" />
</LinearLayout>

คลาสการเชื่อมโยงที่สร้างขึ้นจะเรียกว่า ResultProfileBinding คลาสนี้มี 2 ช่อง ได้แก่ TextView ชื่อ name และ Button ชื่อ button ImageView ในเลย์เอาต์ไม่มีรหัส จึงไม่มีการอ้างอิงถึงในคลาสการเชื่อมโยง

คลาสการเชื่อมโยงทุกคลาสยังมีเมธอด getRoot() ด้วย ซึ่งให้การอ้างอิงโดยตรงสำหรับมุมมองรูทของไฟล์เลย์เอาต์ที่เกี่ยวข้อง ในตัวอย่างนี้ getRoot() จะแสดงผลLinearLayout มุมมองรูทในคลาส ResultProfileBinding

ส่วนต่อไปนี้จะแสดงการใช้คลาสการเชื่อมโยงที่สร้างขึ้นในแอปพลิเคชันและฟragment

ใช้การเชื่อมโยงมุมมองในกิจกรรม

หากต้องการตั้งค่าอินสแตนซ์ของคลาสการเชื่อมโยงเพื่อใช้กับกิจกรรม ให้ทําตามขั้นตอนต่อไปนี้ในเมธอด onCreate() ของกิจกรรม

  1. เรียกใช้เมธอด inflate() แบบคงที่ซึ่งรวมอยู่ในคลาสการเชื่อมโยงที่สร้างขึ้น ซึ่งจะสร้างอินสแตนซ์ของคลาสการเชื่อมโยงเพื่อให้กิจกรรมใช้
  2. รับการอ้างอิงไปยังมุมมองรูทโดยการเรียกใช้เมธอด getRoot() หรือใช้ไวยากรณ์พร็อพเพอร์ตี้ Kotlin
  3. ส่งมุมมองรูทไปที่ setContentView() เพื่อทําให้เป็นมุมมองที่ใช้งานอยู่บนหน้าจอ

ขั้นตอนเหล่านี้แสดงอยู่ในตัวอย่างต่อไปนี้

Kotlin

private lateinit var binding: ResultProfileBinding

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ResultProfileBinding.inflate(layoutInflater)
    val view = binding.root
    setContentView(view)
}

Java

private ResultProfileBinding binding;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    binding = ResultProfileBinding.inflate(getLayoutInflater());
    View view = binding.getRoot();
    setContentView(view);
}

ตอนนี้คุณใช้อินสแตนซ์ของคลาสการเชื่อมโยงเพื่ออ้างอิงมุมมองใดก็ได้ ดังนี้

Kotlin

binding.name.text = viewModel.name
binding.button.setOnClickListener { viewModel.userClicked() }

Java

binding.name.setText(viewModel.getName());
binding.button.setOnClickListener(new View.OnClickListener() {
    viewModel.userClicked()
});

ใช้การเชื่อมโยงมุมมองในส่วนย่อย

หากต้องการตั้งค่าอินสแตนซ์ของคลาสการเชื่อมโยงเพื่อใช้กับ FRG ให้ทําตามขั้นตอนต่อไปนี้ในเมธอด onCreateView() ของ FRG

  1. เรียกใช้เมธอด inflate() แบบคงที่ซึ่งรวมอยู่ในคลาสการเชื่อมโยงที่สร้างขึ้น การดำเนินการนี้จะสร้างอินสแตนซ์ของคลาสการเชื่อมโยงเพื่อให้ส่วนที่ตัดออกมาใช้
  2. รับการอ้างอิงไปยังมุมมองรูทโดยการเรียกใช้เมธอด getRoot() หรือใช้ไวยากรณ์พร็อพเพอร์ตี้ Kotlin
  3. แสดงผลมุมมองรูทจากเมธอด onCreateView() เพื่อให้เป็นมุมมองที่ใช้งานอยู่บนหน้าจอ

Kotlin

private var _binding: ResultProfileBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    _binding = ResultProfileBinding.inflate(inflater, container, false)
    val view = binding.root
    return view
}

override fun onDestroyView() {
    super.onDestroyView()
    _binding = null
}

Java

private ResultProfileBinding binding;

@Override
public View onCreateView (LayoutInflater inflater,
                          ViewGroup container,
                          Bundle savedInstanceState) {
    binding = ResultProfileBinding.inflate(inflater, container, false);
    View view = binding.getRoot();
    return view;
}

@Override
public void onDestroyView() {
    super.onDestroyView();
    binding = null;
}

ตอนนี้คุณใช้อินสแตนซ์ของคลาสการเชื่อมโยงเพื่ออ้างอิงมุมมองใดก็ได้ ดังนี้

Kotlin

binding.name.text = viewModel.name
binding.button.setOnClickListener { viewModel.userClicked() }

Java

binding.name.setText(viewModel.getName());
binding.button.setOnClickListener(new View.OnClickListener() {
    viewModel.userClicked()
});
ของแฟรกเมนต์

ระบุคำแนะนำสำหรับการกำหนดค่าต่างๆ

เมื่อคุณประกาศมุมมองในการกำหนดค่าหลายรายการ ในบางครั้งก็อาจใช้มุมมองประเภทอื่นได้ ทั้งนี้ขึ้นอยู่กับเลย์เอาต์ ข้อมูลโค้ดต่อไปนี้แสดงตัวอย่าง

# in res/layout/example.xml

<TextView android:id="@+id/user_bio" />

# in res/layout-land/example.xml

<EditText android:id="@+id/user_bio" />

ในกรณีนี้ คุณอาจคาดหวังว่าคลาสที่สร้างขึ้นจะแสดงฟิลด์ userBio ที่มีประเภท TextView เนื่องจาก TextView เป็นคลาสฐานทั่วไป เนื่องจากข้อจํากัดทางเทคนิค ตัวสร้างโค้ดการเชื่อมโยงมุมมองจึงไม่สามารถระบุข้อมูลนี้ได้และจะสร้างช่อง View แทน ซึ่งจะต้องแคสต์ฟิลด์ในภายหลังด้วย binding.userBio as TextView

การเชื่อมโยงข้อมูลพร็อพเพอร์ตี้ของมุมมองรองรับแอตทริบิวต์ tools:viewBindingType เพื่อให้คุณบอกคอมไพเลอร์ได้ว่าจะใช้ประเภทใดในโค้ดที่สร้างขึ้น ในตัวอย่างก่อนหน้านี้ คุณสามารถใช้แอตทริบิวต์นี้เพื่อให้คอมไพเลอร์สร้างช่องเป็น TextView ดังนี้

# in res/layout/example.xml (unchanged)

<TextView android:id="@+id/user_bio" />

# in res/layout-land/example.xml

<EditText android:id="@+id/user_bio" tools:viewBindingType="TextView" />

อีกตัวอย่างหนึ่งคือ สมมติว่าคุณมีเลย์เอาต์ 2 รายการ รายการหนึ่งมี BottomNavigationView และอีกรายการมี NavigationRailView ทั้ง 2 คลาสจะขยาย NavigationBarView ซึ่งมีรายละเอียดการใช้งานส่วนใหญ่ หากโค้ดไม่จำเป็นต้องทราบว่ามีคลาสย่อยใดอยู่ในเลย์เอาต์ปัจจุบันบ้าง คุณสามารถใช้ tools:viewBindingType เพื่อตั้งค่าประเภทที่สร้างขึ้นเป็น NavigationBarView ในทั้ง 2 เลย์เอาต์ ดังนี้

# in res/layout/navigation_example.xml

<BottomNavigationView android:id="@+id/navigation" tools:viewBindingType="NavigationBarView" />

# in res/layout-w720/navigation_example.xml

<NavigationRailView android:id="@+id/navigation" tools:viewBindingType="NavigationBarView" />

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

  • ค่าต้องเป็นคลาสที่รับค่ามาจาก android.view.View
  • ค่าต้องเป็นซุปเปอร์คลาสของแท็กที่วางไว้ เช่น ค่าต่อไปนี้ใช้ไม่ได้

      <TextView tools:viewBindingType="ImageView" /> <!-- ImageView is not related to TextView. -->
      <TextView tools:viewBindingType="Button" /> <!-- Button is not a superclass of TextView. -->
    
  • ประเภทสุดท้ายต้องแก้ไขให้สอดคล้องกันในการกําหนดค่าทั้งหมด

ความแตกต่างจาก findViewById

การเชื่อมโยงมุมมองมีข้อดีที่สำคัญเหนือกว่าการใช้ findViewById ดังนี้

  • ความปลอดภัยจากค่า Null: เนื่องจากการเชื่อมโยงข้อมูลวิวจะสร้างการอ้างอิงโดยตรงไปยังวิว จึงไม่มีความเสี่ยงที่จะเกิดข้อยกเว้นเกี่ยวกับ Null Pointer เนื่องจากรหัสวิวไม่ถูกต้อง นอกจากนี้ เมื่อมุมมองปรากฏในบางการกำหนดค่าของเลย์เอาต์เท่านั้น ระบบจะทําเครื่องหมายช่องที่มีข้อมูลอ้างอิงในคลาสการเชื่อมโยงด้วย @Nullable
  • ความปลอดภัยของประเภท: ฟิลด์ในคลาสการเชื่อมโยงแต่ละคลาสมีประเภทที่ตรงกับมุมมองที่อ้างอิงในไฟล์ XML ซึ่งหมายความว่าไม่มีความเสี่ยงที่จะเกิดข้อยกเว้นการแคสต์คลาส

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

การเปรียบเทียบกับการเชื่อมโยงข้อมูล

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

  • การคอมไพล์เร็วขึ้น: การเชื่อมโยงข้อมูลพร็อพเพอร์ตี้ของวิวไม่จําเป็นต้องประมวลผลการกำกับเนื้อหา ดังนั้นเวลาในการคอมไพล์จึงเร็วขึ้น
  • ใช้งานง่าย: การเชื่อมโยงข้อมูลวิวไม่จําเป็นต้องใช้ไฟล์เลย์เอาต์ XML ที่มีแท็กพิเศษ จึงนำไปใช้ในแอปได้เร็วขึ้น เมื่อเปิดใช้การเชื่อมโยงมุมมองในโมดูลแล้ว การเชื่อมโยงจะมีผลกับเลย์เอาต์ทั้งหมดของโมดูลนั้นโดยอัตโนมัติ

ในทางกลับกัน การเชื่อมโยงข้อมูลมีข้อจํากัดต่อไปนี้เมื่อเทียบกับการเชื่อมโยงข้อมูล

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

แหล่งข้อมูลเพิ่มเติม

ดูข้อมูลเพิ่มเติมเกี่ยวกับการเชื่อมโยงข้อมูลวิวได้ที่แหล่งข้อมูลต่อไปนี้

บล็อก

วิดีโอ