observability๋ ๊ฐ์ฒด๊ฐ ๋ฐ์ดํฐ ๋ณํ๋ฅผ ๋ค๋ฅธ ๊ฐ์ฒด์๊ฒ ์๋ฆด ์ ์๋ ๋ฅ๋ ฅ์ ๋ํ๋. ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ฉด ๊ฐ์ฒด, ํ๋ ๋๋ ์ปฌ๋ ์
์ ๊ด์ฐฐ ๊ฐ๋ฅํ๊ฒ ๋ง๋ค ์ ์์.
๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ ์ฌ์ฉํ๋ฉด ๋ฐ์ดํฐ ๊ฐ์ฒด๋ฅผ ์์ ํด๋ UI๊ฐ ์๋์ผ๋ก ์
๋ฐ์ดํธ๋์ง ์์. ์ด ๋ ๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ๋ ๋ ๋ค๋ฅธ ๊ฐ์ฒด(๋ฆฌ์ค๋)์๊ฒ ์๋ฆด ์ ์๋ ๋ฅ๋ ฅ์ ๋ถ์ฌํ ์ ์์.
๊ด์ฐฐ ๊ฐ๋ฅํ ๋ฐ์ดํฐ ๊ฐ์ฒด ์ค ํ๋๊ฐ UI์ ๋ฐ์ธ๋ฉ๋๊ณ ๋ฐ์ดํฐ ๊ฐ์ฒด์ ์์ฑ์ด ๋ณ๊ฒฝ๋๋ฉด UI๊ฐ ์๋์ผ๋ก ์
๋ฐ์ดํธ ๋จ.
ํด๋์ค์ ์์ฑ์ด ๋ช ๊ฐ ์๋ค๋ฉด Observable ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ ํด๋์ค๋ฅผ ๋ง๋๋ ๊ฒ๋ณด๋ค ์ ๋ค๋ฆญ Observable ํด๋์ค์ ๋ค์๊ณผ ๊ฐ์ ์์ ํ์ ๋ณ ํด๋์ค๋ฅผ ์ฌ์ฉํ์ฌ ํ๋๋ฅผ ๊ด์ฐฐ ๊ฐ๋ฅํ๊ฒ ๋ง๋ค ์ ์์.
Observable ํ๋๋ ๋จ์ผ ํ๋๋ฅผ ๊ฐ์ง ๋ ๋ฆฝ์ ๊ด์ฐฐ ๊ฐ๋ฅํ ๊ฐ์ฒด.
class User {
val firstName = ObservableField<String>()
val lastName = ObservableField<String>()
val age = ObservableInt()
}
ํ๋์ ์ ๊ทผํ๋ ค๋ฉด set() ๋ฐ get() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๊ฑฐ๋ ์ฝํ๋ฆฐ ํ๋กํผํฐ ๊ตฌ๋ฌธ์ ์ฌ์ฉ.
user.firstName = "Google"
val age = user.age
๋ฐ์ดํฐ๋ฅผ ๋ณด์ ํ๋ ๋์ ์๋ฃ๊ตฌ์กฐ๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, observable collection์ ํค๋ฅผ ์ฌ์ฉํ์ฌ ์ด๋ฐ ๊ตฌ์กฐ์ ์ก์ธ์คํ ์ ์๊ฒ ํจ.
ObservableArrayMap<String, Any>().apply {
put("firstName", "Google")
put("lastName", "Inc.")
put("age", 17)
}
<data>
<import type="android.databinding.ObservableMap"/>
<variable name="user" type="ObservableMap<String, Object>"/>
</data>
โฆ
<!-- string ํค๋ฅผ ์ฌ์ฉํ๋ map-->
<TextView
android:text="@{user.lastName}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text="@{String.valueOf(1 + (Integer)user.age)}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
integerํ ํค๋ฅผ ์ฌ์ฉํ ๋ ObservableArrayList๋ฅผ ์ฌ์ฉํ๋ฉด ์ ์ฉ.
ObservableArrayList<Any>().apply {
add("Google")
add("Inc.")
add(17)
}
<data>
<import type="android.databinding.ObservableList"/>
<import type="com.example.my.app.Fields"/>
<variable name="user" type="ObservableList<Object>"/>
</data>
โฆ
<TextView
android:text='@{user[Fields.LAST_NAME]}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
๊ฐ๋ฐ์ ๋ ์ฝ๊ฒํ๊ธฐ ์ํด ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋ฆฌ์ค๋ ๋ฑ๋ก ๋ฉ์ปค๋์ฆ์ ๊ตฌํํ๋ BaseObservable ํด๋์ค ์ ๊ณต.
BaseObservable์ ๊ตฌํํ๋ ๋ฐ์ดํฐ ํด๋์ค๋ ํ๋กํผํฐ๊ฐ ๋ณ๊ฒฝ๋ ๋ ์๋ฆผ์ ๋ด๋น.
class User : BaseObservable() {
@get:Bindable
var firstName: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.firstName)
}
@get:Bindable
var lastName: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.lastName)
}
}
๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ ๋ชจ๋ ํจํค์ง ๋ด์ BR์ด๋ผ๋ ์ด๋ฆ์ ํด๋์ค๋ฅผ ์์ฑ. ์ด ํด๋์ค๋ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ ์ฌ์ฉ๋ ๋ฆฌ์์ค์ ID๋ฅผ ํฌํจํจ. Bindable ์ด๋ ธํ ์ด์ ์ ์ปดํ์ผ ์ค BR ํด๋์ค ํ์ผ์ ํญ๋ชฉ์ ์์ฑํจ.
๋ฐ์ดํฐํด๋์ค์ ๊ธฐ๋ณธ ํด๋์ค๋ฅผ ๋ณ๊ฒฝํ ์ ์๋ ๊ฒฝ์ฐ PropertyChangeRegistry ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ฌ ๋ฆฌ์ค๋๋ฅผ ํจ์จ์ ์ผ๋ก ๋ฑ๋ก ๋ฐ ํต์งํ ์ ์๋๋ก Observable ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ์ ์์.
์ฑ์ ๋ ์ด์์์ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ ์์ค์ ๋ฐ์ธ๋ฉํ ์ ์์ผ๋ฉฐ ๋ฐ์ดํฐ ๋ณ๊ฒฝ ์ฌํญ์ด UI์ ์๋์ผ๋ก ํต์ง๋จ.
๐ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ด ๋ผ์ดํ์ฌ์ดํด์ ์ธ์ํ๋ฉฐ UI๊ฐ ํ๋ฉด์ ํ์๋ ๋๋ง ํธ๋ฆฌ๊ฑฐ๋จ.
๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ StateFlow์ LiveData๋ฅผ ์ง์ํจ.
์ฝํ๋ฆฐ ๋ฐ ์ฝ๋ฃจํด์ ์ฌ์ฉํ๋ ์ฑ์ ๊ฒฝ์ฐ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ ์์ค๋ก StateFlow ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ ์ ์์. StateFlow ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด lifecycle owner๋ฅผ ์ง์ ํด์ผ ํจ.
class ViewModelActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// Inflate view and obtain an instance of the binding class.
val binding: UserBinding = DataBindingUtil.setContentView(this, R.layout.user)
// Specify the current activity as the lifecycle owner.
binding.lifecycleOwner = this
}
}
StateFlow์ ViewModel์ ๋ค์๊ณผ ๊ฐ์ด ํจ๊ป ์ฌ์ฉํ ์ ์์.
class ScheduleViewModel : ViewModel() {
private val _username = MutableStateFlow<String>("")
val username: StateFlow<String> = _username
init {
viewModelScope.launch {
_username.value = Repository.loadUserName()
}
}
}
๋ ์ด์์์์ ๋ฐ์ธ๋ฉ ํํ์์ ์ฌ์ฉํ์ฌ ViewModel ๊ฐ์ฒด์ ์์ฑ ๋ฐ ๋ฉ์๋๋ฅผ ํด๋น ๋ทฐ์ ํ ๋น.
<TextView
android:id="@+id/name"
android:text="@{viewmodel.username}" />
Data Binding ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋ ์ด์์ ๋ณ์์ ๋ทฐ์ ์ก์ธ์คํ ์ ์๋ ๋ฐ์ธ๋ฉ ํด๋์ค๋ฅผ ์์ฑํจ.
์์ฑ๋ ๋ฐ์ธ๋ฉ ํด๋์ค๋ ๋ ์ด์์ ๋ณ์๋ฅผ ๋ ์ด์์ ๋ทฐ์ ์ฐ๊ฒฐํจ.
๋ชจ๋ ์์ฑ๋ ๋ฐ์ธ๋ฉ ํด๋์ค๋ ViewDataBinding ํด๋์ค๋ฅผ ์์ํจ.
๊ธฐ๋ณธ์ ์ผ๋ก ํด๋์ค์ ์ด๋ฆ์ ๋ ์ด์์ ํ์ผ์ ์ด๋ฆ์ ํ์ค์นผ ์ผ์ด์ค๋ก ๋ณํํ๊ณ "Binding" ์ ๋ฏธ์ฌ๋ฅผ ์ถ๊ฐํ ๊ฒ.
๋ฐ์ธ๋ฉ ๊ฐ์ฒด๋ ๋ ์ด์์์ ์ธํ๋ ์ดํธํ ์งํ ์์ฑ๋จ. ๊ฐ์ฒด๋ฅผ ๋ ์ด์์์ ๋ฐ์ธ๋ฉํ๋ ๊ฐ์ฅ ์ผ๋ฐ์ ์ธ ๋ฐฉ๋ฒ์ ๋ฐ์ธ๋ฉ ํด๋์ค์ ์ ์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ ๊ฒ.
๋ฐ์ธ๋ฉ ํด๋์ค์ inflate()
๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ๋ทฐ ๊ณ์ธต ๊ตฌ์กฐ๋ฅผ ์ธํ๋ ์ดํธํ๊ณ ๊ฐ์ฒด๋ฅผ ๋ฐ์ธ๋ฉํ ์ ์์.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: MyLayoutBinding = MyLayoutBinding.inflate(layoutInflater)
setContentView(binding.root)
}
inflate()
๋ฉ์๋์ ๋์ ๋ฒ์ ์ LayoutInflater ๊ฐ์ฒด ์ธ์๋ ViewGroup ๊ฐ์ฒด๋ฅผ ์ธ์๋ก ์ฌ์ฉํจ.
val binding: MyLayoutBinding = MyLayoutBinding.inflate(getLayoutInflater(), viewGroup, false)
val binding: MyLayoutBinding = MyLayoutBinding.bind(viewRoot)
val viewRoot = LayoutInflater.from(this).inflate(layoutId, parent, attachToParent)
val binding: ViewDataBinding? = DataBindingUtil.bind(viewRoot)
ํ๋๊ทธ๋จผํธ, ๋ฆฌ์คํธ๋ทฐ ๋๋ ๋ฆฌ์ฌ์ดํด๋ฌ๋ทฐ ์ด๋ํฐ ๋ด์์ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ ํญ๋ชฉ์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, ๋ฐ์ธ๋ฉ ํด๋์ค์ inflate()
๋ฉ์๋ ๋๋ DataBindingUtil ํด๋์ค๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ ์ ์์.
val listItemBinding = ListItemBinding.inflate(layoutInflater, viewGroup, false)
// or
val listItemBinding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false)
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="user" type="com.example.User"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}"
android:id="@+id/firstName"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.lastName}"
android:id="@+id/lastName"/>
</LinearLayout>
</layout>
๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋ ์ด์์์์ id๋ฅผ ๊ฐ์ง ๋ทฐ์ ๋ํด ๋ฐ์ธ๋ฉ ํด๋์ค์ immutable ํ๋๋ฅผ ์์ฑํจ. ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋ ์ด์์์ id๋ฅผ ํฌํจํ ๋ชจ๋ ๋ทฐ๋ฅผ ํ๋ฒ์ ๋ทฐ ๊ณ์ธต ๊ตฌ์กฐ์์ ์ถ์ถํจ.
findViewById()
๋ฅผ ํธ์ถํ๋ ๊ฒ๋ณด๋ค ๋น ๋ฅผ ์ ์์.
<data>
<import type="android.graphics.drawable.Drawable"/>
<variable name="user" type="com.example.User"/>
<variable name="image" type="Drawable"/>
<variable name="note" type="String"/>
</data>
์ ์ฝ๋์์ user, image ๋ฐ note ๋ณ์์ ๋ํ setter์ getter๋ฅผ ์์ฑํจ.
ViewStub์ ๋ฐํ์ ์ ๋ณด์ด์ง ์์ผ๋ฉฐ ํฌ๊ธฐ๊ฐ 0์ด๊ณ ๋ ์ด์์ ๋ฆฌ์์ค๋ฅผ ๋ฆ๊ฒ ์ธํ๋ ์ดํธํ ์ ์๋ ๋ทฐ. ViewStub์ด ํ์๋๊ฑฐ๋
inflate()
๊ฐ ํธ์ถ๋๋ฉด ๋ ์ด์์ ๋ฆฌ์์ค๊ฐ ์ธํ๋ ์ดํธ๋จ.
๊ทธ ๋ค์ ViewStub์ ์์ ๋ทฐ๋ฅผ ์ธํ๋ ์ดํธ๋ ๋ทฐ๋ก ๋์ฒดํจ.
๋ฐ๋ผ์ ViewStub์setVisibility(int)
๋๋inflate()
๊ฐ ํธ์ถ๋ ๋ ๊น์ง ๋ทฐ ๊ณ์ธต ๊ตฌ์กฐ์ ์กด์ฌํจ.
ํ์ฅ๋ ๋ทฐ๋ ViewStub์ ๋ ์ด์์ ๋งค๊ฐ๋ณ์๋ฅผ ์ฌ์ฉํ์ฌ ViewStub์ ์์ ๋ทฐ์ ์ถ๊ฐ๋จ.
๋ง์ฐฌ๊ฐ์ง๋ก ViewStub์ inflateId ์์ฑ์ ์ฌ์ฉํ์ฌ ์ธํ๋ ์ดํธ ๋ ๋ทฐ์ id๋ฅผ ์ ์, ์ฌ์ ์ํ ์ ์์.
<ViewStub android:id="@+id/stub"
android:inflatedId="@+id/subTree"
android:layout="@layout/mySubTree"
android:layout_width="120dip"
android:layout_height="40dip" />
์ด๋ ๊ฒ ์ ์๋ ViewStub์ stub ์ด๋ผ๋ id๋ฅผ ์ฌ์ฉํ์ฌ ์ฐพ์ ์ ์์.
๋ ์ด์์ ๋ฆฌ์์ค mySubTree๋ฅผ inflateํ ํ ViewStub์ ์์ํญ๋ชฉ์์ ์ ๊ฑฐ๋จ.
mySubTree๋ฅผ inflateํ์ฌ ์์ฑ๋ ๋ทฐ๋ inflatedId ์์ฑ์ ์ง์ ๋ subTree id๋ฅผ ์ฌ์ฉํ์ฌ ์ฐพ์ ์ ์์. inflate๋ ๋ทฐ์๋ ์ต์ข ์ ์ผ๋ก ๋๋น 120dip, ๋์ด 40dip์ด ํ ๋น๋จ.
ViewStub stub = findViewById(R.id.stub);
View inflated = stub.inflate();
inflate()
๊ฐ ํธ์ถ๋๋ฉด ViewStub์ด inflate๋ ๋ทฐ๋ก ๋์ฒด๋๊ณ inflate๋ ๋ทฐ๊ฐ ๋ฐํ๋จ. ์ด์ ๋ฐ๋ผ์ ์ ํ๋ฆฌ์ผ์ด์
์ ์ถ๊ฐ์ ์ผ๋ก findViewById()๋ฅผ ์คํํ์ง ์๊ณ ๋ inflate๋ ๋ทฐ์ ๋ํ ์ฐธ์กฐ๋ฅผ ์ป์ ์ ์์
ViewStub์ด ๋ทฐ ๊ณ์ธต ๊ตฌ์กฐ์์ ์ฌ๋ผ์ง๊ธฐ ๋๋ฌธ์ ๊ฐ๋น์ง ์ปฌ๋ ํฐ์ ์ํด ์ฒ๋ฆฌ๋ ์ ์๋๋ก ๋ฐ์ธ๋ฉ ๊ฐ์ฒด์ ๋ทฐ๋ ์ฌ๋ผ์ ธ์ผ ํจ.
๋ทฐ๋ final์ด๊ธฐ ๋๋ฌธ์ ViewStubProxy ๊ฐ์ฒด๋ ์์ฑ๋ ๋ฐ์ธ๋ฉ ํด๋์ค์์ ViewStub์ ๋์ ํ์ฌ ViewStub์ด ์์ ๋ ViewStub์ ๋ํ ์ก์ธ์ค๋ฅผ ์ ๊ณตํ๊ณ ViewStub์ด inflate๋ ๋ inflate๋ ๋ทฐ ๊ณ์ธต ๊ตฌ์กฐ์ ๋ํ ์ก์ธ์ค๋ฅผ ์ ๊ณต.
๋ค๋ฅธ ๋ ์ด์์์ inflateํ ๋ ์ ๋ ์ด์์์ ๋ํ ๋ฐ์ธ๋ฉ์ ์ค์ ํด์ผ ํ๋ฏ๋ก ViewStubProxy๋ ViewStub OnInflateListener๋ฅผ ์์ ํ๊ณ ํ์ํ ๋ ๋ฐ์ธ๋ฉ์ ์ค์ .
ํ๋ฒ์ ํ๋์ ๋ฆฌ์ค๋๋ง ์กด์ฌํ ์ ์์ผ๋ฏ๋ก ViewStubProxy๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฐ์ธ๋ฉ์ ์ค์ ํ ํ ํธ์ถํ๋ OnInflateListener๋ฅผ ์ค์ ํ ์ ์์.
RecyclerView.Adapter์ ๊ฐ์ ๊ฒฝ์ฐ๋ ํน์ ๋ฐ์ธ๋ฉ ํด๋์ค๋ฅผ ์์ง ๋ชปํจ. onBindViewHolder()
๋ฅผ ํธ์ถํ๋ ๋์ ๋ฐ์ธ๋ฉ ๊ฐ์ ํ ๋นํด์ผ ํจ.
override fun onBindViewHolder(holder: BindingHolder, position: Int) {
item: T = items.get(position)
holder.binding.setVariable(BR.item, item);
holder.binding.executePendingBindings(); // ๋ฐ์ธ๋ฉ์ ์ฆ์ ์คํํด์ผ ํ ๊ฒฝ์ฐ ๊ฐ์ ๋ก ์คํํ๋ ๋ฉ์๋
}
๋ชจ๋ ํจํค์ง๊ฐ com.example.my.app์ธ ๊ฒฝ์ฐ ๋ฐ์ธ๋ฉ ํด๋์ค๋ com.example.my.app.databind ํจํค์ง์ ๋ฐฐ์น๋จ.
data element์ ํด๋์ค ์์ฑ์ ์กฐ์ ํ์ฌ ํด๋์ค ์ด๋ฆ์ ๋ฐ๊พธ๊ฑฐ๋ ๋ค๋ฅธ ํจํค์ง์ ๋ฐฐ์นํ ์ ์์.
<data class="ContactItem">
...
</data>
<data class=".ContactItem"> <!--ํด๋์ค ์ด๋ฆ ์์ ๋ง์นจํ๋ฅผ ์ถ๊ฐํ์ฌ ๋ค๋ฅธ ํจํค์ง์ ๋ฐ์ธ๋ฉ ํด๋์ค ์์ฑ ๊ฐ๋ฅ-->
...
</data>
<data class="com.example.ContactItem"> <!-- ๋ฐ์ธ๋ฉ ํด๋์ค๋ฅผ ์์ฑํ๋ ค๋ ์ ์ฒด ํจํค์ง ์ด๋ฆ์ ์ฌ์ฉํ ์๋ ์์.-->
...
</data>
๋ฐ์ธ๋ฉ ์ด๋ํฐ๋ ๊ฐ ์ค์ ์ ์ํด ์ ์ ํ ํ๋ ์์ํฌ ํธ์ถ์ ์ํํ๋ ์ผ์ ๋ด๋นํจ.
(์: setText() ํธ์ถ๊ณผ ๊ฐ์ ์์ฑ๊ฐ์ ์ค์ ํ๋ ๊ฒ, setOnClickListener()๋ฅผ ํธ์ถํ๋ ๊ฒ๊ณผ ๊ฐ์ด ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ์ค์ ํ๋ ๊ฒ.)
๋ฐ์ธ๋ฉ๋ ๊ฐ์ด ๋ณ๊ฒฝ๋ ๋๋ง๋ค ์์ฑ๋ ๋ฐ์ธ๋ฉ ํด๋์ค๋ ๋ฐ์ธ๋ฉ ํํ์์ ์ฌ์ฉํ์ฌ ๋ทฐ์์ setter ๋ฉ์๋๋ฅผ ํธ์ถํด์ผ ํจ. ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์๋์ผ๋ก ๋ฉ์๋๋ฅผ ๊ฒฐ์ ํ ์ ์๋๋ก ํ ์ ์๊ณ ,
๋ฉ์๋๋ฅผ ๋ช
์์ ์ผ๋ก ์ ์ธํ๊ฑฐ๋ ์ปค์คํ
๋ก์ง์ ์ ๊ณตํ์ฌ ๋ฉ์๋๋ฅผ ์ ํํ ์ ์์.
๐ xml์์ ์ฌ์ฉ์๊ฐ ๋ง๋ ์์ฑ๊ณผ ๋ก์ง์ ์ฌ์ฉํ ์ ์์.
๋ง์ฝ android:text="@{user.name}"
๊ณผ ๊ฐ์ ํํ์์ด ์ฃผ์ด์ง๋ฉด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ user.getName()
์์ ๋ฐํ๋ ํ์
์ ํ์ฉํ๋ setText(arg)
๋ฉ์๋๋ฅผ ์ฐพ์.
user.getName()
์ ๋ฐํ ํ์
์ด String์ธ ๊ฒฝ์ฐ, ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ String ์ธ์๋ฅผ ํ์ฉํ๋ setText()
๋ฉ์๋๋ฅผ ์ฐพ์. ํํ์์ด int์ธ์๋ฅผ ๋ฐํํ๋ ๊ฒฝ์ฐ int ์ธ์๋ฅผ ํ์ฉํ๋ setText()
๋ฉ์๋๋ฅผ ํ์ํจ
๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ ํด๋น ์ด๋ฆ์ ์์ฑ์ด ์กด์ฌํ์ง ์๋ ๊ฒฝ์ฐ์๋ ๋์. ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ ์ฌ์ฉํ์ฌ ๋ชจ๋ setter์ ๋ํ ์์ฑ์ ์์ฑํ ์ ์์.
<!-- DrawerLayout์๋ ์์ฑ์ด ์์ง๋ง ๋ง์ setter๊ฐ ์์ผ๋ฉฐ setScrimColor(int) ๋ฐ addDrawerListener(DrawerListener) ๋ฉ์๋๋ฅผ ์๋์ผ๋ก ์ฌ์ฉํจ-->
<android.support.v4.widget.DrawerLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:scrimColor="@{@color/scrim}"
app:drawerListener="@{fragment.drawerListener}">
์ผ๋ถ ์์ฑ์๋ ์ด๋ฆ์ด ์ผ์นํ์ง ์๋ setter๊ฐ ์์. ์ด๋ฐ ๊ฒฝ์ฐ์ BindingMethods ์ด๋
ธํ
์ด์
์ ์ฌ์ฉํ์ฌ ์์ฑ์ setter์ ์ฐ๊ฒฐํ ์ ์์.
์ด๋
ธํ
์ด์
์ ํด๋์ค์ ํจ๊ป ์ฌ์ฉ๋๋ฉฐ ์ด๋ฆ์ด ๋ฐ๋ ๊ฐ ๋ฉ์๋๋ง๋ค BindingMethod ์ด๋
ธํ
์ด์
์ ํฌํจํ ์ ์์.
@BindingMethods(value = [
BindingMethod(
type = android.widget.ImageView::class,
attribute = "android:tint",
method = "setImageTintList")])
์ผ๋ฐ์ ์ผ๋ก ์๋๋ก์ด๋ ํ๋ ์์ํฌ ํด๋์ค์์ ์์ฑ์ ์ด๋ฏธ ๋ค์ด๋ฐ ๊ท์น์ ์ฌ์ฉํ์ฌ ๊ตฌํ๋๊ณ ์๋์ผ๋ก ์ผ์นํ๋ ๋ฉ์๋๋ฅผ ์ฐพ์ผ๋ฏ๋ก setter์ ์ด๋ฆ์ ๋ฐ๊ฟ ํ์๊ฐ ์์.
์ผ๋ถ ์์ฑ์๋ ์ฌ์ฉ์ ์ ์ ๋ฐ์ธ๋ฉ ๋ก์ง์ด ํ์ํ ๊ฒฝ์ฐ๊ฐ ์์.
android:paddingLeft
์์ฑ์๋ ์ฐ๊ฒฐ๋ setter๊ฐ ์์. ๋์ setPadding(left, top, right, bottom)
์ด ์ ๊ณต๋จ.
BindingAdapter ์ด๋ ธํ ์ด์ ์ด ํฌํจ๋ ์ ์ ๋ฐ์ธ๋ฉ ์ด๋ํฐ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด ์์ฑ์ ๋ํ setter๊ฐ ํธ์ถ๋๋ ๋ฐฉ์์ ์ฌ์ฉ์ ์ง์ ํ ์ ์์.
/** ์ฒซ ๋ฒ์งธ ๋งค๊ฐ๋ณ์๋ ์์ฑ๊ณผ ์ฐ๊ด๋ ๋ทฐ์ ํ์
, ๋ ๋ฒ์งธ ๋งค๊ฐ๋ณ์๋ ์ง์ ๋ ์์ฑ์ ๋ํ ํ์
.**/
@BindingAdapter("android:paddingLeft")
fun setPaddingLeft(view: View, padding: Int) {
view.setPadding(padding,
view.getPaddingTop(),
view.getPaddingRight(),
view.getPaddingBottom())
}
์ฌ๋ฌ ์์ฑ์ ๋ฐ๋ ์ด๋ํฐ๊ฐ ์์ ์๋ ์์.
@BindingAdapter("imageUrl", "error")
fun loadImage(view: ImageView, url: String, error: Drawable) {
Picasso.get().load(url).error(error).into(view)
}
<ImageView app:imageUrl="@{venue.imageUrl}" app:error="@{@drawable/venueError}" />
์์ฑ์ด ์ค์ ๋ ๋ ์ด๋ํฐ๊ฐ ํธ์ถ๋๋๋ก ํ๋ ค๋ฉด ๋ค์ ์์ ํ์๋ ๋๋ก ์ด๋ํฐ์ requireAll ํ๋๊ทธ๋ฅผ false๋ก ์ค์ .
@BindingAdapter(value = ["imageUrl", "placeholder"], requireAll = false)
fun setImageUrl(imageView: ImageView, url: String?, placeHolder: Drawable?) {
if (url == null) {
imageView.setImageDrawable(placeholder);
} else {
MyImageLoader.loadInto(imageView, url, placeholder);
}
}
๋ฐ์ธ๋ฉ ์ด๋ํฐ ๋ฉ์๋๋ ํด๋น ํธ๋ค๋ฌ์์ ์ด์ ๊ฐ์ ์ฌ์ฉํ ์ ์์. ์ด์ ๊ฐ๊ณผ ์๋ก์ด ๊ฐ์ ์ฌ์ฉํ๋ ๋ฉ์๋๋ ์์ฑ์ ๋ํ ์ด์ ๊ฐ์ ์๋ก์ด ๊ฐ๋ณด๋ค ๋จผ์ ์ ์ธํด์ผ ํจ.
@BindingAdapter("android:paddingLeft")
fun setPaddingLeft(view: View, oldPadding: Int, newPadding: Int) {
if (oldPadding != newPadding) {
view.setPadding(newPadding,
view.getPaddingTop(),
view.getPaddingRight(),
view.getPaddingBottom())
}
}
์ด๋ฒคํธ ํธ๋ค๋ฌ๋ ๋ค์ ์์ ์ ๊ฐ์ด ํ๋์ ์ถ์ ๋ฉ์๋๊ฐ ์๋ ์ธํฐํ์ด์ค ๋๋ ์ถ์ ํด๋์ค์์๋ง ์ฌ์ฉํ ์ ์์.
@BindingAdapter("android:onLayoutChange")
fun setOnLayoutChangeListener(
view: View,
oldValue: View.OnLayoutChangeListener?,
newValue: View.OnLayoutChangeListener?
) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
if (oldValue != null) {
view.removeOnLayoutChangeListener(oldValue)
}
if (newValue != null) {
view.addOnLayoutChangeListener(newValue)
}
}
}
<View android:onLayoutChange="@{() -> handler.layoutChanged()}"/>
๋ฆฌ์ค๋์ ๋ฉ์๋๊ฐ ์ฌ๋ฌ ๊ฐ์ธ ๊ฒฝ์ฐ ์ฌ๋ฌ ๋ฆฌ์ค๋๋ก ๋ถํ ํด์ผ ํจ.
// Translation from provided interfaces in Java:
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
interface OnViewDetachedFromWindow {
fun onViewDetachedFromWindow(v: View)
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
interface OnViewAttachedToWindow {
fun onViewAttachedToWindow(v: View)
}
@BindingAdapter(
"android:onViewDetachedFromWindow",
"android:onViewAttachedToWindow",
requireAll = false
)
fun setListener(view: View, detach: OnViewDetachedFromWindow?, attach: OnViewAttachedToWindow?) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
val newListener: View.OnAttachStateChangeListener?
newListener = if (detach == null && attach == null) {
null
} else {
object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: View) {
attach.onViewAttachedToWindow(v)
}
override fun onViewDetachedFromWindow(v: View) {
detach.onViewDetachedFromWindow(v)
}
}
}
val oldListener: View.OnAttachStateChangeListener? =
ListenerUtil.trackListener(view, newListener, R.id.onAttachStateChangeListener)
if (oldListener != null) {
view.removeOnAttachStateChangeListener(oldListener)
}
if (newListener != null) {
view.addOnAttachStateChangeListener(newListener)
}
}
}
๋ฐ์ธ๋ฉ ํํ์์์ ๊ฐ์ฒด๊ฐ ๋ฐํ๋๋ฉด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์์ฑ ๊ฐ์ ์ค์ ํ๋๋ฐ ์ฌ์ฉ๋๋ ๋ฉ์๋๋ฅผ ์ ํ.
๊ฐ์ฒด๋ ์ ํํ ๋ฉ์๋์ ๋งค๊ฐ๋ณ์ ํ์
์ผ๋ก ์บ์คํ
๋จ.
<TextView
android:text='@{userMap["lastName"]}'
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
object.key ํ๊ธฐ๋ฒ์ ์ฌ์ฉํ์ฌ ๋งต์ ๊ฐ์ ์ฐธ์กฐํ ์๋ ์์.
@{userMap["lastName"]}
์ @{userMap.lastName}
์ผ๋ก ๋ฐ๊ฟ ์ ์์.
์ ์์์ userMap ๊ฐ์ฒด๋ android:text
์์ฑ์ ๊ฐ์ ์ค์ ํ๋ ๋ฐ ์ฌ์ฉ๋๋ setText(CharSequence)
๋ฉ์๋์ ์๋ ๋งค๊ฐ๋ณ์ ์ ํ์ผ๋ก ์๋์ผ๋ก ์บ์คํ
๋๋ ๊ฐ์ ๋ฐํํจ.
๋งค๊ฐ๋ณ์ ์ ํ์ด ๋ชจํธํ ๊ฒฝ์ฐ ํํ์์์ ๋ฐํ ์ ํ์ ์บ์คํ
ํ ๊ฒ.
๋ทฐ์ android:Background
์์ฑ์ Drawable์ ๊ธฐ๋ํ์ง๋ง ์ง์ ๋ ์์ ๊ฐ์ ์ ์.
<View
android:background="@{isError ? @color/red : @color/white}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
Drawable์ด ์์๋๊ณ ์ ์๊ฐ ๋ฐํ๋ ๋๋ง๋ค int๋ฅผ ColorDrawable๋ก ๋ณํ.
BindingConversion ์ด๋
ธํ
์ด์
๊ณผ ํจ๊ป ์ ์ ๋ฉ์๋๋ฅผ ์ฌ์ฉ.
@BindingConversion
fun convertColorToDrawable(color: Int) = ColorDrawable(color)
๋ฐ์ธ๋ฉ ํํ์์ ์ ๊ณต๋๋ ๊ฐ ํ์ ์ ๋์ผํ ํ์ ์ด์ด์ผ ํจ.
// The @drawable and @color represent different value types in the same
// expression, which causes a build error.
<!-- ์๋ฌ๊ฐ ์ผ์ด๋๋ ์ฝ๋-->
<View
android:background="@{isError ? @drawable/error : @color/white}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>