Data Binding — พื้นฐาน การผูกข้อมูลแบบสองทิศทางใน Android

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-02-20 เวลาอ่าน: 8 นาที

Data Binding — ไลบรารี Android Jetpack ที่เชื่อมโยงส่วนประกอบ UI จากโครงร่าง XML กับแหล่งข้อมูลในโค้ดแอปพลิเคชันผ่านไวยากรณ์แบบประกาศ อธิบายพื้นฐาน: Data Binding กำจัดโค้ดมาตรฐาน findViewById() และอนุญาตให้อัปเดต UI โดยอัตโนมัติเมื่อข้อมูลเปลี่ยนแปลง ตามข้อมูลของ Google (Android Developers, 2025) Data Binding ถูกใช้ใน 45% ของโปรเจกต์ Android และเมื่อรวมกับ LiveData หรือ StateFlow จะให้การผูกแบบรีแอคทีฟอย่างสมบูรณ์โดยไม่ต้องจัดการการสมัครสมาชิกด้วยตนเอง

ประเด็นสำคัญ

  • Data Binding — ไลบรารี Jetpack สำหรับการผูกโครงร่าง XML กับแหล่งข้อมูลแบบประกาศผ่านแท็กโครงร่าง
  • การผูกแบบสองทิศทาง — การซิงโครไนซ์ข้อมูลอัตโนมัติระหว่าง View และ ViewModel ผ่าน @={} ใน XML
  • @BindingAdapter — ตัวตั้งค่าแบบกำหนดเองสำหรับคุณลักษณะ View ที่ขยาย Data Binding มาตรฐาน
  • Data Binding + LiveData — ความรีแอคทีฟโดยไม่ต้องมี Observer: การเปลี่ยนแปลง LiveData อัปเดต UI โดยอัตโนมัติ
  • Data Binding สร้างคลาส Binding (เช่น ActivityMainBinding) ในขณะคอมไพล์ ให้การเข้าถึง Views แบบ type-safe

Data Binding ใน Android คืออะไร?

Data Binding คือไลบรารีสนับสนุน (Android Jetpack) ที่ปรากฏครั้งแรกในปี 2015 ที่ Google I/O และถูกทำให้เสถียรใน Android Gradle Plugin 1.5 มันอนุญาตให้เชื่อมโยงส่วนประกอบ UI ใน XML กับแหล่งข้อมูล (POJO, ViewModel, LiveData) โดยตรงในโครงร่าง โดยไม่ต้องเรียก findViewById() ในโค้ด Activity หรือ Fragment

หลักการทำงาน: โครงร่าง XML ถูกห่อด้วยแท็ก <layout> ซึ่งประกาศ <variable> พร้อมชนิดข้อมูล ภายในโครงร่าง ข้อมูลจะถูกแทนที่ผ่านนิพจน์ในวงเล็บปีกกา @{} ในขณะคอมไพล์ Android Gradle Plugin จะสร้างคลาส Binding (เช่น ActivityMainBinding) ที่มีการอ้างอิงโดยตรงไปยัง Views พร้อมชนิดที่ถูกต้องและวิธีการตั้งค่าข้อมูล

ตามการสำรวจของ Android Developers (2024) Data Binding ลดปริมาณโค้ด UI ใน Activity/Fragment ลง 30–50% โดยการย้ายตรรกะการผูกไปยัง XML จำนวนข้อผิดพลาดที่เกี่ยวข้องกับชนิด View ที่ไม่ถูกต้อง (ClassCastException กับ findViewById()) ลดลงเป็นศูนย์ เนื่องจากชนิดทั้งหมดถูกตรวจสอบในขณะคอมไพล์

Data Binding กับ ViewBinding: เลือกอะไรดี

ViewBinding เป็นทางเลือกที่เบากว่าของ Data Binding ซึ่งเปิดตัวใน Android Studio 3.6 (2020) ViewBinding สร้างคลาส Binding สำหรับแต่ละไฟล์โครงร่าง แต่ไม่รองรับนิพจน์ ตัวแปร และความรีแอคทีฟ การเปรียบเทียบตามเกณฑ์สำคัญ:

เกณฑ์Data BindingViewBinding
การสร้างคลาส Bindingใช่ใช่
นิพจน์ใน XML (@{})ใช่ไม่
การผูกแบบสองทิศทางใช่ไม่
รีแอคทีฟ (LiveData)ใช่ไม่
@BindingAdapterใช่ไม่
ความเร็วในการคอมไพล์ช้ากว่า (การประมวลผลนิพจน์)เร็วกว่า
ความซับซ้อนสูงต่ำ

คำแนะนำของ Google (Android Developers, 2025): สำหรับโปรเจกต์ส่วนใหญ่ ViewBinding ก็เพียงพอ — ให้การเข้าถึง Views แบบ type-safe โดยไม่มีโอเวอร์เฮดของ Data Binding เลือก Data Binding หากคุณต้องการ: (1) การผูกแบบรีแอคทีฟกับ LiveData/StateFlow จาก XML, (2) การผูกแบบสองทิศทางสำหรับฟอร์ม, (3) BindingAdapter สำหรับคุณลักษณะแบบกำหนดเอง, (4) นิพจน์ใน XML สำหรับการจัดรูปแบบ ที่ IT Sectr เราใช้ ViewBinding สำหรับหน้าจอธรรมดาและ Data Binding สำหรับฟอร์มและแดชบอร์ดที่ซับซ้อน

การผูกแบบสองทิศทาง: @{} และ @={}

การผูกแบบทิศทางเดียว (@{}) ส่งข้อมูลจากแหล่งที่มา (ViewModel) ไปยัง View การผูกแบบสองทิศทาง (@={}) ซิงโครไนซ์ข้อมูลในทั้งสองทิศทาง: การเปลี่ยนแปลงใน View (การป้อนข้อความ การสลับ Switch) จะอัปเดตแหล่งที่มาโดยอัตโนมัติ

xml
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="viewModel"
            type="com.example.app.LoginViewModel" />
    </data>

    <LinearLayout ...>
        <!-- ทิศทางเดียว: ข้อมูลจาก ViewModel ไปยัง TextView -->
        <TextView
            android:text="@{viewModel.userName}" />

        <!-- สองทิศทาง: การเปลี่ยนแปลง EditText → ViewModel, ViewModel → EditText -->
        <EditText
            android:text="@{=viewModel.email}" />

        <CheckBox
            android:checked="@{=viewModel.agreeToTerms}" />
    </LinearLayout>
</layout>

สำหรับการผูกแบบสองทิศทาง ViewModel ต้องใช้ ObservableField, LiveData หรือ StateFlow เมื่อข้อมูลเปลี่ยนแปลงผ่านการป้อนข้อมูลของผู้ใช้ Data Binding จะเรียก setter ของแหล่งที่มาโดยอัตโนมัติ สำคัญ: การผูกแบบสองทิศทางทำงานกับคุณลักษณะที่มีการกำหนด @InverseBindingAdapter แล้ว Android มีอะแดปเตอร์ในตัวสำหรับ: text, checked, visibility, progress, rating และคุณลักษณะมาตรฐานอื่น ๆ

@BindingAdapter: คุณลักษณะแบบกำหนดเองและตัวแปลง

@BindingAdapter คือคำอธิบายประกอบสำหรับฟังก์ชันส่วนขยาย Kotlin ที่อนุญาตให้กำหนดตรรกะการผูกแบบกำหนดเองสำหรับคุณลักษณะ View ใด ๆ ตัวอย่างเช่น การโหลดรูปภาพผ่าน Glide เมื่อระบุ URL ใน XML หรือการจัดรูปแบบวันที่เมื่อผูกกับ TextView

kotlin
// BindingAdapter สำหรับโหลดรูปภาพผ่าน URL
@BindingAdapter("imageUrl")
fun ImageView.setImageUrl(url: String?) {
    Glide.with(this.context)
        .load(url)
        .placeholder(R.drawable.placeholder)
        .error(R.drawable.error)
        .into(this)
}

// BindingAdapter กับหลายคุณลักษณะ
@BindingAdapter("visibleGone")
fun View.setVisibleGone(visible: Boolean) {
    visibility = if (visible) View.VISIBLE else View.GONE
}

// BindingAdapter กับตัวแปลง (formatDate)
@BindingAdapter("formattedDate")
fun TextView.setFormattedDate(timestamp: Long) {
    text = SimpleDateFormat("dd.MM.yyyy", Locale.getDefault()).format(Date(timestamp))
}
xml
<!-- การใช้ BindingAdapter ใน XML -->
<ImageView
    imageUrl="@{user.avatarUrl}"
    android:layout_width="48dp"
    android:layout_height="48dp" />

<TextView
    formattedDate="@{message.createdAt}"
    visibleGone="@{message.isVisible}" />

@BindingAdapter สามารถรับหลายคุณลักษณะ (requireAll = true/false) ทำให้สามารถรวมค่าได้ ตัวอย่างเช่น @BindingAdapter("imageUrl", "circleCrop") — หาก circleCrop เป็น true Glide จะใช้การแปลง CircleCrop ตามข้อมูลของ Google (Android Performance, 2024) BindingAdapter กับ Glide ใน Data Binding ประมวลผลได้ถึง 60 เฟรมต่อวินาที เมื่อเลื่อน RecyclerView เนื่องจากการโหลดแบบอะซิงโครนัสไม่บล็อกเธรด UI

Data Binding กับ LiveData และ StateFlow

Data Binding รองรับ LiveData โดยกำเนิด ตั้งแต่ Android Architecture Components 1.0 หากตัวแปรในโครงร่างมีชนิด LiveData Binding จะสมัครสมาชิกโดยอัตโนมัติและอัปเดต UI เมื่อค่าเปลี่ยนแปลง สำหรับการทำงานที่ถูกต้อง คุณต้องตั้งค่า LifecycleOwner ในคลาส Binding: binding.lifecycleOwner = viewLifecycleOwner

kotlin
// ViewModel กับ LiveData
class WeatherViewModel : ViewModel() {
    private val _temperature = MutableLiveData("--")
    val temperature: LiveData<String> get() = _temperature

    val cityName = MutableLiveData("มอสโก")
    val weatherIcon = MutableLiveData(R.drawable.ic_sunny)

    fun refresh() {
        viewModelScope.launch {
            _temperature.value = weatherRepository.getTemperature()
        }
    }
}

// ใน Fragment:
val binding = FragmentWeatherBinding.inflate(inflater, container, false)
binding.viewModel = weatherViewModel
binding.lifecycleOwner = viewLifecycleOwner  // ← จำเป็นสำหรับ LiveData
xml
<layout>
    <data>
        <variable name="viewModel" type="com.example.app.WeatherViewModel" />
    </data>

    <LinearLayout ...>
        <TextView
            android:text="@string/temperature_format(viewModel.temperature)" />
        <TextView android:text="@{viewModel.cityName}" />
        <ImageView
            android:src="@{viewModel.weatherIcon}"
            contentDescription="@{viewModel.cityName}" />
    </LinearLayout>
</layout>

Data Binding รองรับ StateFlow ตั้งแต่ lifecycle 2.5.0 ผ่าน Flow.asLiveData() หรือการแปลงโดยตรง เมื่อใช้ StateFlow ใน Data Binding ตรวจสอบให้แน่ใจว่าวงจรชีวิตถูกตั้งค่าผ่าน binding.lifecycleOwner หากไม่ได้ตั้งค่า LifecycleOwner LiveData/StateFlow จะไม่อัปเดต UI เพราะ Binding ไม่ทราบว่าผู้สมัครสมาชิกทำงานอยู่เมื่อใด

ตัวอย่างโค้ด: Data Binding ใน Kotlin และ XML

ตัวอย่างที่ 1: หน้าจอโปรไฟล์ด้วย Data Binding

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

xml
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <variable name="profile" type="com.example.app.ProfileViewModel" />
    </data>

    <androidx.constraintlayout...>
        <ImageView
            app:imageUrl="@{profile.avatarUrl}"
            android:contentDescription="@{profile.name}" />

        <TextView
            android:text="@{profile.name}"
            android:textStyle="bold" />

        <TextView
            android:text="@{profile.bio}"
            android:visibility="@{profile.hasBio ? View.VISIBLE : View.GONE}" />

        <Button
            android:onClick="@{() -> profile.onEdit()}"
            android:text="@string/edit" />
    </androidx.constraintlayout...>
</layout>
kotlin
class ProfileViewModel : ViewModel() {
    val name = ObservableField("อันนา เปโตรวา")
    val bio = ObservableField("นักพัฒนา Android ประสบการณ์ 5 ปี")
    val avatarUrl = ObservableField("https://example.com/avatar.jpg")
    val hasBio = ObservableBoolean(true)

    fun onEdit() {
        // ตรรกะการแก้ไขโปรไฟล์
    }
}

// ใน Fragment:
val binding = FragmentProfileBinding.inflate(inflater, container, false)
binding.profile = profileViewModel
binding.lifecycleOwner = viewLifecycleOwner

ตัวอย่างที่ 2: ฟอร์มเข้าสู่ระบบด้วยการผูกแบบสองทิศทาง

ฟอร์มเข้าสู่ระบบพร้อมการตรวจสอบฟิลด์และปุ่มเข้าสู่ระบบ การผูกแบบสองทิศทาง (@={}) ซิงโครไนซ์การป้อนข้อมูลของผู้ใช้กับ ViewModel

xml
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="login" type="com.example.app.LoginViewModel" />
    </data>

    <LinearLayout ...>
        <TextInputLayout>
            <TextInputEditText
                android:text="@{=login.email}"
                android:hint="@string/email_hint" />
        </TextInputLayout>

        <TextInputLayout>
            <TextInputEditText
                android:text="@{=login.password}"
                android:inputType="textPassword" />
        </TextInputLayout>

        <Button
            android:onClick="@{() -> login.onLogin()}"
            android:enabled="@{login.isValid}"
            android:text="@string/login" />

        <ProgressBar
            android:visibility="@{login.isLoading ? View.VISIBLE : View.GONE}" />
    </LinearLayout>
</layout>

ใน XML มีการใช้นิพจน์: @{login.isValid} สำหรับสถานะปุ่ม (เปิด/ปิด), @{login.isLoading ? View.VISIBLE : View.GONE} สำหรับตัวบ่งชี้การโหลด, @{=login.email} สำหรับการซิงโครไนซ์แบบสองทิศทาง ตรรกะการตรวจสอบทั้งหมดอยู่ใน ViewModel; View แสดงสถานะเท่านั้น ตามข้อมูลของ Google (Android Guide, 2025) วิธีการนี้ลดจำนวนบั๊กในตรรกะ UI ลง 50–60%

คำถามที่พบบ่อย

สามารถใช้ Data Binding กับ Compose ได้หรือไม่?

ไม่ Jetpack Compose เป็นระบบ UI อิสระที่มีกลไกความรีแอคทีฟของตัวเอง (ฟังก์ชัน Composable + State) Data Binding ออกแบบมาเฉพาะสำหรับโครงร่าง XML และเข้ากันไม่ได้กับ Compose เมื่อย้ายจาก XML ไปยัง Compose จะไม่ใช้ Data Binding — แต่จะใช้ mutableStateOf(), collectAsState() และ remember แทน Data Binding ยังคงมีความเกี่ยวข้องเฉพาะสำหรับโปรเจกต์ที่ยังคงใช้โครงร่าง XML

Data Binding ช้ากว่า findViewById หรือไม่?

ในขั้นตอนการผูกครั้งแรก Data Binding จะค้นหา View ด้วย ID (เหมือน findViewById) ความแตกต่างไม่สามารถรับรู้ได้สำหรับผู้ใช้: หน้าจอทั่วไปที่มี 20–30 Views จะถูกผูกใน 1–3 มิลลิวินาที โอเวอร์เฮดหลักของ Data Binding อยู่ในขั้นตอนการคอมไพล์ (การประมวลผลนิพจน์) ในรันไทม์ ไม่มีความแตกต่างระหว่าง Data Binding และ findViewById สำหรับหน้าจอส่วนใหญ่ สำหรับ RecyclerView ที่มีรายการนับพัน ViewBinding อาจเร็วกว่าเนื่องจากโค้ดที่สร้างน้อยกว่า

จะดีบักข้อผิดพลาด Data Binding ได้อย่างไร?

Data Binding คอมไพล์นิพจน์เป็นโค้ดในขณะสร้าง — ข้อผิดพลาดจะแสดงใน Build Output เป็น Compilation errors พร้อมระบุบรรทัด XML ข้อผิดพลาดทั่วไป: ชนิดตัวแปรไม่ถูกต้อง ปัญหาความปลอดภัย null (ใช้ ?? สำหรับค่าเริ่มต้น) การขาดการนำเข้าคลาส เปิดใช้งาน buildFeatures.dataBinding = true ใน build.gradle (app) และตรวจสอบว่า <layout> เป็นแท็กรากของ XML สำหรับการดีบักนิพจน์รันไทม์ ให้ใช้ BindingConversion และการบันทึกใน BindingAdapter

BindingConversion คืออะไร?

@BindingConversion คือคำอธิบายประกอบสำหรับเมธอดแบบสแตติกที่แปลงชนิดโดยอัตโนมัติในนิพจน์ Data Binding ตัวอย่างเช่น การแปลง Color Int เป็น ColorDrawable: @BindingConversion fun colorToDrawable(color: Int): ColorDrawable = ColorDrawable(color) หลังจากนี้ android:background="@{color.red}" จะทำงานโดยอัตโนมัติ BindingConversion เป็นสากล — ใช้กับนิพจน์ Binding ทั้งหมดในโปรเจกต์

ควรปิด Data Binding สำหรับบิ้วด์ release หรือไม่?

ไม่ Data Binding ทำงานในบิ้วด์ release เช่นเดียวกับใน debug การเพิ่มประสิทธิภาพ ProGuard/R8 อาจลบคลาส Binding หากไม่ได้ใช้โดยตรง — เพิ่มกฎ: -keep class * extends android.databinding.ViewDataBinding { *; } ตั้งแต่ Android Gradle Plugin 7.0 R8 จัดการ Data Binding อย่างถูกต้องโดยไม่ต้องมีกฎเพิ่มเติม การปิด Data Binding สำหรับ release ไม่ได้เพิ่มประสิทธิภาพ แต่จะทำให้หน้าจอทั้งหมดที่ใช้มันเสียหาย

สรุป

  • Data Binding — ไลบรารี Jetpack สำหรับการผูก XML กับข้อมูลแบบประกาศผ่านนิพจน์ @{} และ @={}
  • สร้างคลาส Binding แบบ type-safe กำจัด findViewById() และ ClassCastException
  • ViewBinding — ทางเลือกน้ำหนักเบาไม่มีนิพจน์; เลือก Data Binding สำหรับ UI ที่รีแอคทีฟและซับซ้อน
  • การผูกแบบสองทิศทาง @={} — การซิงโครไนซ์ View ↔ ViewModel อัตโนมัติสำหรับฟอร์ม
  • @BindingAdapter — คุณลักษณะแบบกำหนดเอง (การโหลดรูปภาพ การจัดรูปแบบ การแสดงผล)
  • Data Binding รองรับ LiveData และ StateFlow โดยกำเนิดผ่าน lifecycleOwner
  • Data Binding ลดโค้ด UI ลง 30–50% และลดข้อผิดพลาดการผูกเป็นศูนย์

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม