data binding xml中无须做null判断,自带
module的build.gradle
1 2 3 4 5
| android { dataBinding { enabled true } }
|
xml
自动创建:在xml文件的根节点点击,就会出现灯泡,点击灯泡可选“Convert to data binding layout”
手动创建:
1 2 3 4 5 6 7 8 9 10
| <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <data> <variable name="viewmodel" type="io.github.araisann.navigationtest.WordViewModel" /> </data> </layout>
|
xml中调用:
1 2
| android:text="@{@string/fragment0 + viewmodel.allWords.size()}" android:onClick="@{(v) -> viewmodel.jumpFragment2(v)}"
|
Fragment
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class Fragment0 : Fragment() { lateinit var wordViewModel: WordViewModel
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) wordViewModel = ViewModelProviders.of(activity!!).get(WordViewModel::class.java) }
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val fragmentTodayBinding = DataBindingUtil.inflate( inflater, R.layout.fragment0, container, false ) as Fragment0Binding
val view = fragmentTodayBinding.root fragmentTodayBinding.viewmodel = wordViewModel return view }
}
|
ViewModel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| class WordViewModel : ViewModel() { val allWords: MutableLiveData<List<Word>>
init { val list = ArrayList<Word>() list.add(Word("DEFAULT")) list.add(Word("DEFAULT")) list.add(Word("DEFAULT")) list.add(Word("DEFAULT")) allWords = MutableLiveData() allWords.value = list }
fun modify(position: Int, wordStr: String) { if (position > allWords.value?.size ?: 0 || position < 0 ){ return } allWords.value?.let { it[position].word = wordStr } }
fun jumpFragment2(v:View){ val action = Fragment0Directions.actionFragment0ToFragment1("time", 1) Navigation.findNavController(v).navigate(action) } }
|
Adapter使用Data Binding
点击事件依旧通过手动设置
item:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="word" type="io.github.araisann.navigationtest.Word" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{word.word}" /> </LinearLayout> </layout>
|
adapter:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| class MyRecyclerAdapter(private val allWords: List<Word>?) : RecyclerView.Adapter<MyRecyclerAdapter.MyViewHolder>() { var listener: OnItemClickListener? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { val layoutInflater = LayoutInflater.from(parent.context) val itemBinding = ItemWordBinding.inflate(layoutInflater, parent, false) return MyViewHolder(itemBinding) }
override fun getItemCount(): Int { return allWords?.size ?: 0 }
override fun onBindViewHolder(holder: MyViewHolder, position: Int) { allWords?.let { holder.bind(it[position]) } holder.binding.itemRoot.setOnClickListener { view -> allWords?.let { listener?.onClick(view, allWords[position]) } } }
fun setOnItemClickListener(listener: OnItemClickListener) { this.listener = listener }
interface OnItemClickListener { fun onClick(view: View, data: Word) }
class MyViewHolder(val binding: ItemWordBinding) : RecyclerView.ViewHolder(binding.root) { fun bind(word: Word) { binding.word = word binding.executePendingBindings() } } }
|
Fragment:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| class Fragment0 : Fragment() { private lateinit var wordViewModel: WordViewModel private lateinit var fragment0Binding: Fragment0Binding private lateinit var adapter: MyRecyclerAdapter
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) wordViewModel = ViewModelProviders.of(activity!!).get(WordViewModel::class.java) } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { fragment0Binding = DataBindingUtil.inflate( inflater, R.layout.fragment0, container, false ) as Fragment0Binding fragment0Binding.viewmodel = wordViewModel setupRecyclerView() return fragment0Binding.root } private fun setupRecyclerView(){ val recyclerView = fragment0Binding.recyclerView recyclerView.layoutManager = LinearLayoutManager(context) adapter = MyRecyclerAdapter(wordViewModel.allWords.value) adapter.setOnItemClickListener(object : MyRecyclerAdapter.OnItemClickListener{ override fun onClick(view: View, data: Word) { } }) recyclerView.adapter = adapter } }
|
相关注解
@BindAdapter(‘name’):自定义方法
给View增加一个自定义的属性,并绑定。
不推荐覆盖系统自带的属性。
可以参考ImageViewBindingAdapter等自带实现,里面的”android:src”等自带属性也是通过@BindingAdapter实现的。
1 2 3 4 5 6 7 8
| <data> <variable name="myValue" type="xx.xx.xx.A"/> </data> <MyView app:myAttribute="@{myValue}" />
|
1 2 3 4 5
| @BindingAdapter("myAttribute") @JvmStatic fun setMyAttribute(view: MyView, value: xx.xx.xx.A) { }
|
@BindingMethod、 @BindingMethods
可以参考ImageViewBindingAdapter等自带实现。
类似简单版@BindAdapter。通过给类增加注释,实现xml属性和方法的bind。直接set。
1 2 3 4
| @BindingMethods({ BindingMethod(type = MyView::class, attribute = "myAttribute", method = "setMyAttribute"), BindingMethod(type = MyView::class, attribute = "myAttribute2", method = "setMyAttribute2") })
|
@Bindable
绑定,实现通知UI更新。
不过有LiveData了,似乎不太需要这个了。
DataBinding调用setLifecycleOwner,xml即可随着数据改变而改变。