การผูกข้อมูลเป็นเทคนิคที่ใช้เมื่อคุณต้องการติดข้อมูลบางส่วน (ข้อมูลของคุณ) กับองค์ประกอบการป้อนข้อมูลของผู้ใช้ที่มองเห็นได้ ในกระบวนการนี้ เมื่อใดก็ตามที่อินพุตได้รับการอัปเดต ข้อมูลเบื้องหลังจะได้รับการอัปเดตด้วยเช่นกัน
นี่ยังห่างไกลจากแนวคิดใหม่ และมีเฟรมเวิร์กมากมายที่รวมสิ่งนี้ไว้ในการออกแบบ (เช่น AngularJS/React/Vue)
ความสนใจของเราในบทความนี้ไม่ได้อยู่ที่เฟรมเวิร์กส่วนหน้า แต่อยู่ที่การพัฒนาอุปกรณ์พกพา Google เปิดตัว Data Binding Library ใน Android ซึ่งเป็นส่วนหนึ่งของ Android Jetpack
หากคุณไม่คุ้นเคยกับชุดไลบรารี Jetpack อาจเป็นเพราะ Google ได้ประกาศว่าจะยกเลิกการพัฒนาสำหรับไลบรารีที่รองรับ แต่จะย้ายไปอยู่ในไลบรารีที่รองรับ AndroidX แทน (ซึ่งเป็นเวอร์ชันใหม่ของไลบรารีสนับสนุน)
ฉันทราบดีว่ามีบทความมากมายที่อธิบายวิธีใช้การผูกข้อมูลโดยใช้อะแดปเตอร์ แต่งานชิ้นนี้ไม่ได้เน้นที่เรื่องนั้น แต่ฉันจะแสดงวิธีง่ายๆ ในการผูกข้อมูลซึ่งช่วยประหยัดเวลาโดยการลดจำนวนโค้ดที่คุณต้องเขียน
เหตุใดจึงต้องใช้การเชื่อมโยงข้อมูล
หากคุณยังไม่ได้ขาย ให้ใช้เวลาสักครู่เพื่ออธิบายประโยชน์ของการใช้การเชื่อมโยงข้อมูลโดยแสดงตัวอย่าง สมมติว่าคุณมีเมนูที่มีปุ่มกำหนดเองสามปุ่ม โดยที่แต่ละปุ่มจะเป็นเลย์เอาต์ในตัวมันเอง
วิธีหนึ่งในการสร้างสิ่งเหล่านี้คือการใช้เลย์เอาต์ XML สี่แบบ:แบบหนึ่งสำหรับเลย์เอาต์หลักและอีกอันสำหรับสามปุ่มแต่ละปุ่ม
คุณต้องทำเช่นนี้เนื่องจากแต่ละปุ่มจะนำผู้ใช้ไปยังส่วนต่างๆ ของแอปพลิเคชันของคุณ ดังนั้นจึงต้องใช้ข้อความและรูปภาพที่แตกต่างกัน
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_gravity="center_horizontal"
android:layout_height="wrap_content"
android:layout_width="wrap_content">
<ImageView
android:id="@+id/imageView"
android:layout_height="100dp"
android:layout_width="100dp"
android:src="@drawable/image_name"
android:adjustViewBounds="true"
android:scaleType="centerInside"
/>
<TextView
android:id="@+id/textView"
android:gravity="center_horizontal"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:text="Image Text"
android:textSize="16sp" />
</LinearLayout>
จริงอยู่ที่ โค้ดนี้ไม่ได้ซ้ำกันมากนัก เนื่องจากเราจัดการแค่สามเลย์เอาต์เท่านั้น แต่ถ้าคุณลองคิดดู มันค่อนข้างเสียเวลาในการจัดการกับโค้ดทั้งหมดนั้น หากคุณพิจารณาแอปพลิเคชันที่มีเลย์เอาต์ที่ซับซ้อนกว่า ซึ่งอาจแสดงผลิตภัณฑ์และรูปภาพ การทำเช่นนี้อาจนำไปสู่การจำลองโค้ดที่น่าเบื่อมากมาย
ด้วยการผูกข้อมูล เราจะสร้างเค้าโครง XML เพียงรูปแบบเดียวที่จะใช้โดยปุ่มทั้งหมดของเรา
เราจะเริ่มต้นที่ไหน
เราจำเป็นต้องให้โครงการของเราเปิดใช้งานการผูกข้อมูล เพื่อที่เราต้องเพิ่ม dataBinding
องค์ประกอบใน build.gradle
. ของแอปพลิเคชันของเรา ไฟล์:
android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
applicationId "com.tomerpacific.example"
minSdkVersion 15
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
dataBinding { //<-------
enabled = true
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
หลังจากซิงโครไนซ์โปรเจ็กต์แล้ว เราจะสร้างคลาสข้อมูล ซึ่งเราจะใช้เพื่อผูกเลย์เอาต์ด้วย
package com.tomerpacific.example
import android.graphics.drawable.Drawable
data class ButtonData(val buttonText: String, val buttonImageSrc : Drawable)
ให้ความสนใจกับความจริงที่ว่าเรามีสองฟิลด์ใน ButtonData
. ของเรา คลาส:
buttonText
— นี่คือข้อความที่จะปรากฏใต้ภาพของเราbuttonImageSrc
— รับผิดชอบรูปภาพของปุ่ม
หากเราต้องการข้อมูลมากกว่านี้ เราก็จะเพิ่มฟิลด์ในคลาสข้อมูลของเรามากขึ้น
การผูกที่แท้จริง
ต่อไป เราต้องประกาศคุณสมบัติของตัวแปรในรูปแบบของเราเพื่อให้สามารถใช้งานได้ ตัวแปรนี้จะถูกผูกไว้กับคลาสข้อมูลที่เราสร้างขึ้น ในการทำเช่นนี้ มีสองสิ่งที่เราต้องทำ:
- ห่อองค์ประกอบเค้าโครงรูทของเราในแท็กเลย์เอาต์
- เพิ่มแท็กข้อมูลที่จะมีการประกาศตัวแปรของเรา (
buttonData
)
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="https://schemas.android.com/apk/res/android"> // <---- 1
<data>
<variable name="buttonData" type="com.tomerpacific.example.ButtonData"/> // <---- 2
</data>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:app="https://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Example"
android:textSize="30dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/linearLayout3"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView2">
<ImageButton
android:layout_width="100dp"
android:layout_height="100dp"
android:adjustViewBounds="true"
android:scaleType="centerInside"
android:src="@drawable/android">
</ImageButton>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="Image Text"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/linearLayout3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView2"
app:layout_constraintVertical_bias="0.504">
<ImageButton
android:layout_width="100dp"
android:layout_height="100dp"
android:adjustViewBounds="true"
android:scaleType="centerInside"
android:src="@drawable/android_p_logo">
</ImageButton>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="Image Text"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/linearLayout2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.200"
app:layout_constraintStart_toEndOf="@+id/linearLayout3"
app:layout_constraintTop_toBottomOf="@+id/textView2"
app:layout_constraintVertical_bias="0.504">
<ImageButton
android:layout_width="100dp"
android:layout_height="100dp"
android:adjustViewBounds="true"
android:scaleType="centerInside"
android:src="@drawable/android_studio_icon">
</ImageButton>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="Image Text"
android:textSize="16sp" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
โปรดสังเกตว่าเราสามารถลบสคีมาออกจากเลย์เอาต์หลักก่อนหน้าของเราเมื่อถูกย้ายไปยังแท็กเลย์เอาต์รูท นอกจากนี้ ตัวแปรที่เราเพิ่มยังเชื่อมโยงกับคลาสข้อมูลของเราโดยตรงอีกด้วย
ใน MainActivity
. ของเรา เราต้องเพิ่มโค้ดเพื่อจัดการกับการโยง:
package com.tomerpacific.example
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.databinding.DataBindingUtil
import com.tomerpacific.example.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val binding: ActivityMainBinding = DataBindingUtil.setContentView(
this, R.layout.activity_main)
binding.buttonData = ButtonData("First", resources.getDrawable(R.drawable.android))
}
}
ทุกครั้งที่คุณสร้างตัวแปรภายในโครงร่าง คลาสการโยงจะถูกสร้างขึ้นสำหรับเลย์เอาต์นั้นโดยอัตโนมัติ ในกรณีของเรา เลย์เอาต์ของเราเรียกว่า activity_main
ดังนั้นคลาสการผูกจะถูกตั้งชื่อว่า ActivityMainBinding
.
แบบแผนคือชื่อของเลย์เอาต์ที่มี ผูก . เสมอ เพิ่มในตอนท้าย
เนื่องจากเราประกาศ buttonData
ในฐานะตัวแปรของเราในเลย์เอาต์ มันถูกผนวกเข้ากับอ็อบเจกต์การโยง และเราสามารถกำหนดอินสแตนซ์ใหม่ของ ButtonData
ของเราให้ ชั้นเรียน
หลังจากทำทุกอย่างแล้ว เราก็สามารถใช้ข้อมูลที่เราเพิ่งผูกไว้ในเลย์เอาต์ได้
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/linearLayout3"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView2">
<ImageButton
android:layout_width="100dp"
android:layout_height="100dp"
android:adjustViewBounds="true"
android:scaleType="centerInside"
android:src="@{buttonData.buttonImageSrc}"> // <----
</ImageButton>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="@{buttonData.buttonText}" // <----
android:textSize="16sp" />
และผลลัพธ์ก็คือ:
รอสักครู่…
เรามีปุ่มสามปุ่มและคลาสข้อมูลของเราสามารถใช้ได้เพียงปุ่มเดียว แล้วเราจะข้ามสิ่งนี้ไปได้อย่างไร
package com.tomerpacific.example
data class ButtonsData(val buttonsData : List<ButtonData>) {
fun get(index: Int) : ButtonData {
return buttonsData.get(index)
}
}
เราต้องแทนที่ get
ที่เราต้องจดจำเมื่อเรานำไปใช้ในรูปแบบของเรา
จากนั้น เราต้องเปลี่ยนข้อมูลอ้างอิงใน activity_main.xml
. ของเรา :
<data>
<variable name="buttonsData" type="com.tomerpacific.example.ButtonsData"/>
</data>
และเราต้องสร้างการเชื่อมโยงใหม่กับคลาสข้อมูลใหม่:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val binding: ActivityMainBinding = DataBindingUtil.setContentView(
this, R.layout.activity_main)
val firstButton : ButtonData = ButtonData("First", resources.getDrawable(R.drawable.android))
val secondButton : ButtonData = ButtonData("Second", resources.getDrawable(R.drawable.android_p_logo))
val thirdButton : ButtonData = ButtonData("Third", resources.getDrawable(R.drawable.android_studio_icon))
val buttonsData : ButtonsData = ButtonsData(listOf(firstButton, secondButton, thirdButton))
binding.buttonsData = buttonsData
}
เราสร้าง ButtonData
. สามอินสแตนซ์ ระดับ. จากนั้นเราก็สร้าง ButtonsData
วัตถุและแนบไปกับวัตถุที่มีผลผูกพันของเรา
สุดท้ายนี้ เราสามารถใช้คลาสข้อมูลของเราในรูปแบบของเราได้อย่างเหมาะสม:
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/linearLayout3"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView2">
<ImageButton
android:layout_width="100dp"
android:layout_height="100dp"
android:adjustViewBounds="true"
android:scaleType="centerInside"
android:src="@{buttonsData.get(0).buttonImageSrc}"> // <-------
</ImageButton>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="@{buttonsData.get(0).buttonText}" // <--------
android:textSize="16sp" />
</LinearLayout>
บทความนี้เขียนขึ้นเนื่องจากประสบการณ์ของฉันในการพัฒนาแอปพลิเคชันต่อไปนี้:
(คุณไม่มีทางรู้หรอก มันอาจจะมีประโยชน์ก็ได้)
สัญลักษณ์ซักรีด - แอปพลิเคชันใน Google Play เคยมีปัญหาในการหาว่าสัญลักษณ์ซักรีดเหล่านั้นหมายถึงอะไร? คุณโชคดีแล้ว! แอพนี้มีคำอธิบายเกี่ยวกับสัญลักษณ์ที่ดูแปลก ๆ พร้อมอินเทอร์เฟซและรูปภาพที่ใช้งานง่าย tomerpacificApps บน Google Playคุณสามารถดูซอร์สโค้ดทั้งหมดได้ที่นี่:
TomerPacific/LaundrySymbolsแอปพลิเคชันที่เขียนด้วยภาษา Kotlin แสดงสัญลักษณ์การซักรีดต่างๆ พร้อมคำอธิบาย :cyclone:- TomerPacific/LaundrySymbols TomerPacificGitHub