android 数据绑定(4)实用特性及疑惑:使用控件、格式化@string/xxx、对象传递、双向数据绑定
1.在布局内使用其它控件
1.1 效果
箭头所指3个控件的内容随输入框内容而变化。
1.2 示例代码
1 <?xml version="1.0" encoding="utf-8"?> 2 <layout xmlns:app="http://schemas.android.com/apk/res-auto" 3 xmlns:tools="http://schemas.android.com/tools" 4 xmlns:android="http://schemas.android.com/apk/res/android"> 5 <data> 6 <import type="android.view.View" /> 7 <import type="com.example.databind.Exts" /> 8 </data> 9 <androidx.constraintlayout.widget.ConstraintLayout 10 android:clickable="true" 11 android:background="#ffffff" 12 android:layout_width="match_parent" 13 android:layout_height="match_parent"> 14 15 <TextView 16 android:id="@+id/features_title" 17 android:layout_width="wrap_content" 18 android:layout_height="wrap_content" 19 android:layout_marginTop="16dp" 20 android:text="binding features" 21 android:textAllCaps="false" 22 app:layout_constraintEnd_toEndOf="parent" 23 app:layout_constraintStart_toStartOf="parent" 24 app:layout_constraintTop_toTopOf="parent" /> 25 26 27 <TextView 28 android:id="@+id/features_txt1" 29 android:layout_width="wrap_content" 30 android:layout_height="wrap_content" 31 android:layout_marginTop="48dp" 32 android:background="#f8f8f8" 33 android:textSize="12sp" 34 android:text='@{featureEdt.text.toString(),default="取 feature_edt 的值"}' 35 app:layout_constraintEnd_toStartOf="@+id/features_txt2" 36 app:layout_constraintStart_toStartOf="parent" 37 app:layout_constraintTop_toBottomOf="@+id/feature_edt" /> 38 39 <TextView 40 android:id="@+id/features_txt2" 41 android:layout_width="wrap_content" 42 android:layout_height="wrap_content" 43 android:background="#f8f8f8" 44 android:textSize="12sp" 45 android:text='@{featureEdt.text.toString(),default="取 feature_edt 的值"}' 46 app:layout_constraintBottom_toBottomOf="@+id/features_txt1" 47 app:layout_constraintEnd_toEndOf="parent" 48 app:layout_constraintStart_toEndOf="@+id/features_txt1" 49 app:layout_constraintTop_toTopOf="@+id/features_txt1" /> 50 <!--android:textColor="@{featureEdt.text.hasCharX('e') ? @color/colorAccent : @color/colorPrimaryDark }"--> 51 52 <EditText 53 android:id="@+id/feature_edt" 54 android:layout_width="0dp" 55 android:layout_height="64dp" 56 android:layout_marginStart="16dp" 57 android:layout_marginLeft="16dp" 58 android:layout_marginTop="32dp" 59 android:layout_marginEnd="16dp" 60 android:layout_marginRight="16dp" 61 android:background="@drawable/edt_bg" 62 android:ems="10" 63 android:textColor='@{featureEdt.text.toString().length() > 8 ? @color/colorAccent : @color/colorPrimaryDark }' 64 android:inputType="textPersonName" 65 android:paddingLeft="8dp" 66 android:text="Name" 67 android:maxLength="16" 68 android:maxLines="1" 69 android:textAllCaps="false" 70 android:textSize="12sp" 71 app:layout_constraintEnd_toEndOf="parent" 72 app:layout_constraintStart_toStartOf="parent" 73 app:layout_constraintTop_toBottomOf="@+id/features_title" /> 74 75 <TextView 76 android:id="@+id/toast" 77 android:layout_width="wrap_content" 78 android:layout_height="wrap_content" 79 android:textSize="9sp" 80 android:textColor='@{featureEdt.text.toString().length() < 1 ? @color/colorAccent : @color/colorPrimaryDark,default=@color/colorPrimary}' 81 android:text='@{featureEdt.text.toString().length() < 1 ? "不能为空" :"1-16个字符",default = "1-16个字符"}' 82 app:layout_constraintStart_toStartOf="@+id/feature_edt" 83 app:layout_constraintTop_toBottomOf="@+id/feature_edt" /> 84 85 </androidx.constraintlayout.widget.ConstraintLayout> 86 </layout>
1.3 特性描述
- 控件按驼峰式命名法命名,如 : feature_edt -> featureEdt
- 其它控件可以在布局内访问这个控件以及它的成员,第34、45、81行。
- 不可以调用控件的扩展成员。第50行。
- 控件自己可以调用自己,第63行。
2. 可以使用格式化字符串
- 示例,@string/xxx 可以和 “字符串” 相加 ,如下
1 <TextView 2 android:id="@+id/tvFormat" 3 android:layout_width="wrap_content" 4 android:layout_height="wrap_content" 5 android:layout_marginTop="24dp" 6 android:text='@{@string/format("李4",0x20,33.333333f) + " string/xxx 可以和 字符串相加 ",default=@string/format}' 7 app:layout_constraintStart_toStartOf="@+id/feature_edt" 8 app:layout_constraintTop_toBottomOf="@+id/features_txt1" />
- string.xml
1 <resources> 2 <string name="app_name">DataBind</string> 3 <string name="format">format : name=%1$s,age=%2$1d,value=%3$32f </string> 4 //... 5 </resources>
3.对象传递到include布局中
3.1 示例
1 <?xml version="1.0" encoding="utf-8"?> 2 <layout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:tools="http://schemas.android.com/tools" 4 xmlns:app="http://schemas.android.com/apk/res-auto" 5 xmlns:bind="http://schemas.android.com/apk/res-auto"> 6 <data> 7 <variable name="data" type="com.example.databind.Data" /> 8 <variable name="click" type="com.example.databind.Click" /> 9 </data> 10 11 <androidx.constraintlayout.widget.ConstraintLayout 12 android:layout_width="match_parent" 13 android:layout_height="match_parent" 14 android:clickable="true" 15 android:focusable="true" 16 android:focusableInTouchMode="true" 17 android:onClick="@{click::onStartClicked}"> 18 19 //... 20 21 <include 22 android:id="@+id/include" 23 layout="@layout/include" 24 android:layout_width="0dp" 25 android:layout_height="wrap_content" 26 android:layout_marginTop="16dp" 27 app:layout_constraintEnd_toEndOf="@+id/frgmt2" 28 app:layout_constraintStart_toStartOf="@+id/frgmt2" 29 app:layout_constraintTop_toBottomOf="@+id/frgmt2" 30 bind:data="@{data}" 31 bind:title='@{"标题"}' 32 /> 33 ...
代码中把 data 传递给 @layout/include ,要求这个布局也使用数据绑定布局,且也声明data和title变量。
如下:
1 <layout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:app="http://schemas.android.com/apk/res-auto"> 3 4 <data> 5 <variable name="data" type="com.example.databind.Data" /> 6 <variable name="title" type="String" /> 7 </data> 8 9 <androidx.constraintlayout.widget.ConstraintLayout 10 android:layout_width="match_parent" 11 android:layout_height="wrap_content" 12 android:background="#7ff77ff7" 13 > 14 15 //... 16 17 <TextView 18 android:id="@+id/value" 19 android:layout_width="wrap_content" 20 android:layout_height="wrap_content" 21 android:layout_marginStart="32dp" 22 android:layout_marginLeft="32dp" 23 android:text="@{String.valueOf(data.value),default = value}" 24 app:layout_constraintEnd_toEndOf="parent" 25 app:layout_constraintStart_toEndOf="@+id/key" 26 app:layout_constraintTop_toTopOf="@+id/key" /> 27 28 29 30 </androidx.constraintlayout.widget.ConstraintLayout> 31 </layout>
3.2 不支持 merge 为直接子元素
数据绑定不支持 include 作为 merge 元素的直接子元素
1 <?xml version="1.0" encoding="utf-8"?> 2 <layout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:bind="http://schemas.android.com/apk/res-auto"> 4 <data> 5 <variable name="user" type="com.example.User"/> 6 </data> 7 <merge><!-- Doesn't work --> 8 <include layout="@layout/name" 9 bind:user="@{user}"/> 10 <include layout="@layout/contact" 11 bind:user="@{user}"/> 12 </merge> 13 </layout> 14
#.后台线程的疑惑
#.1 问题
英文原版
中文版
#.2 疑惑?
Collection<T> 实现类 里存放的数据,不能在后台线程中修改?
#.3 测试代码
在后台线程中对list 操作,并没有发现问题
1 package com.example.databind 2 3 import android.os.Bundle 4 import android.view.LayoutInflater 5 import android.view.View 6 import android.view.ViewGroup 7 import androidx.databinding.ObservableField 8 import androidx.fragment.app.Fragment 9 import com.example.databind.databinding.MapCollectionBinding 10 import kotlin.concurrent.thread 11 12 class MapCollectionFrgmt : Fragment() { 13 14 lateinit var binding : MapCollectionBinding 15 16 val list = ArrayList<String>() 17 val map = HashMap<String,ObservableField<String>>() 18 val data = Data() 19 20 init { 21 data.key = "data key" 22 data.key = "data value" 23 24 map.put("key1",ObservableField("value1")) 25 map.put("key2",ObservableField("value2")) 26 map.put("key3",ObservableField("value3")) 27 map.put("key4",ObservableField("value4")) 28 29 list.add("value0") 30 list.add("value1") 31 list.add("value2") 32 list.add("value3") 33 } 34 35 36 fun onDataThreadMainClicked(view: View){ 37 val random = (Math.random() * 1000).toInt() 38 data.key = "新Main key$random" 39 data.value = random 40 binding.data = data 41 } 42 43 fun onDataThreadOtherClicked(view: View){ 44 thread { 45 val random = (Math.random() * 1000).toInt() 46 data.key = "新other key$random" 47 data.value = random 48 binding.data = data 49 } 50 } 51 52 fun onMap1ThreadMain(v : View){ 53 val random = (Math.random() * 1000).toInt() 54 val ob = ObservableField<String>() 55 ob.set("新Main value$random") 56 map.put("key1",ob) 57 binding.map = map 58 } 59 fun onMap1ThreadOther(v : View){ 60 thread { 61 val random = (Math.random() * 1000).toInt() 62 val ob = ObservableField<String>() 63 ob.set("新Main value$random") 64 map.put("key1",ob) 65 binding.map = map 66 } 67 } 68 fun onList0ThreadMain(v : View){ 69 val random = (Math.random() * 1000).toInt() 70 list[0] = "新Main value$random" 71 binding.list = list 72 } 73 fun onList0ThreadOther(v : View){ 74 thread { 75 val random = (Math.random() * 1000).toInt() 76 list[0] = "新Main value$random" 77 binding.list = list 78 } 79 } 80 fun initBinding(){ 81 binding.list = list 82 binding.data = data 83 binding.map = map 84 85 binding.threadMainData.setOnClickListener(this::onDataThreadMainClicked) 86 binding.threadOtherData.setOnClickListener(this::onDataThreadOtherClicked) 87 binding.threadMainMap1.setOnClickListener(this::onMap1ThreadMain) 88 binding.threadOtherMap1.setOnClickListener(this::onMap1ThreadOther) 89 binding.threadMainList0.setOnClickListener(this::onList0ThreadMain) 90 binding.threadOtherList0.setOnClickListener(this::onList0ThreadOther) 91 /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 92 val text: String = getString(R.string.map_title, map.size) 93 val styledText: Spanned = Html.fromHtml(text, FROM_HTML_OPTION_USE_CSS_COLORS) 94 binding.mapTitle.text = styledText 95 }*/ 96 } 97 98 99 override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 100 binding = MapCollectionBinding.inflate(inflater,container,false) 101 initBinding() 102 return binding.root 103 } 104 105 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 106 super.onViewCreated(view, savedInstanceState) 107 } 108 109 override fun onDetach() { 110 super.onDetach() 111 } 112 113 }
布局
1 <?xml version="1.0" encoding="utf-8"?> 2 <layout xmlns:app="http://schemas.android.com/apk/res-auto" 3 xmlns:tools="http://schemas.android.com/tools" 4 xmlns:android="http://schemas.android.com/apk/res/android"> 5 <data > 6 <import type="androidx.databinding.ObservableField" /> 7 <variable name="data" type="com.example.databind.Data" /> 8 <variable name="map" type="java.util.HashMap<String,ObservableField<String>>" /> 9 <variable name="list" type="java.util.List<String>" /> 10 </data> 11 <androidx.constraintlayout.widget.ConstraintLayout 12 android:clickable="true" 13 android:background="#ffffff" 14 android:layout_width="match_parent" 15 android:layout_height="match_parent"> 16 17 <TextView 18 android:id="@+id/data_title" 19 android:layout_width="0dp" 20 android:layout_height="wrap_content" 21 android:layout_marginTop="16dp" 22 android:background="#f6f6f6" 23 android:paddingLeft="16dp" 24 android:text="data " 25 app:layout_constraintEnd_toEndOf="parent" 26 app:layout_constraintStart_toStartOf="parent" 27 app:layout_constraintTop_toTopOf="parent" /> 28 29 <TextView 30 android:id="@+id/map_title" 31 android:layout_width="0dp" 32 android:layout_height="wrap_content" 33 android:layout_marginTop="16dp" 34 android:background="#f8f8f8" 35 android:paddingLeft="16dp" 36 android:text="@{@string/map_title(map.size()) ,default=@string/map_title}" 37 app:layout_constraintEnd_toEndOf="parent" 38 app:layout_constraintStart_toStartOf="parent" 39 app:layout_constraintTop_toBottomOf="@+id/data_key" /> 40 41 42 <TextView 43 android:id="@+id/data_key" 44 android:layout_width="wrap_content" 45 android:layout_height="wrap_content" 46 android:layout_marginStart="32dp" 47 android:layout_marginLeft="32dp" 48 android:layout_marginTop="16dp" 49 android:text='@{data.key}' 50 app:layout_constraintStart_toStartOf="@+id/data_title" 51 app:layout_constraintTop_toBottomOf="@+id/data_title" /> 52 53 <TextView 54 android:id="@+id/data_value" 55 android:layout_width="wrap_content" 56 android:layout_height="wrap_content" 57 android:layout_marginStart="16dp" 58 android:layout_marginLeft="16dp" 59 android:text='@{String.valueOf(data.value)}' 60 app:layout_constraintStart_toEndOf="@+id/data_key" 61 app:layout_constraintTop_toTopOf="@+id/data_key" /> 62 63 <TextView 64 android:id="@+id/map_key1" 65 android:layout_width="wrap_content" 66 android:layout_height="wrap_content" 67 android:layout_marginStart="24dp" 68 android:layout_marginLeft="24dp" 69 android:layout_marginTop="16dp" 70 android:text='@{@string/map_key_value("key1",map["key1"]),default=@string/map_key_value}' 71 app:layout_constraintStart_toStartOf="parent" 72 app:layout_constraintTop_toBottomOf="@+id/map_title" /> 73 74 <TextView 75 android:id="@+id/map_key3" 76 android:layout_width="wrap_content" 77 android:layout_height="wrap_content" 78 android:layout_marginTop="8dp" 79 android:text='@{@string/map_key_value("key1",map["key3"]),default=@string/map_key_value}' 80 app:layout_constraintStart_toStartOf="@+id/map_key1" 81 app:layout_constraintTop_toBottomOf="@+id/map_key2" /> 82 83 <TextView 84 android:id="@+id/map_key4" 85 android:layout_width="wrap_content" 86 android:layout_height="wrap_content" 87 android:layout_marginTop="8dp" 88 android:text='@{@string/map_key_value("无效key",map["无效key"]),default=@string/map_key_value}' 89 app:layout_constraintStart_toStartOf="@+id/map_key2" 90 app:layout_constraintTop_toBottomOf="@+id/map_key3" /> 91 92 <TextView 93 android:id="@+id/map_key2" 94 android:layout_width="wrap_content" 95 android:layout_height="wrap_content" 96 android:layout_marginTop="8dp" 97 android:text='@{@string/map_key_value("key2",map["key2"]),default=@string/map_key_value}' 98 app:layout_constraintStart_toStartOf="@+id/map_key1" 99 app:layout_constraintTop_toBottomOf="@+id/map_key1" /> 100 101 102 <TextView 103 android:id="@+id/list_title" 104 android:layout_width="0dp" 105 android:layout_height="wrap_content" 106 android:layout_marginTop="24dp" 107 android:background="#f8f8f8" 108 android:paddingLeft="16dp" 109 android:text="@{@string/list_title(list.size()) ,default=@string/list_title}" 110 app:layout_constraintEnd_toEndOf="parent" 111 app:layout_constraintStart_toStartOf="parent" 112 app:layout_constraintTop_toBottomOf="@+id/map_key4" /> 113 114 115 <TextView 116 android:id="@+id/list_0" 117 android:layout_width="wrap_content" 118 android:layout_height="wrap_content" 119 android:layout_marginStart="24dp" 120 android:layout_marginLeft="24dp" 121 android:layout_marginTop="16dp" 122 android:text='@{@string/list_index(0,list[0]),default=@string/list_index}' 123 app:layout_constraintStart_toStartOf="parent" 124 app:layout_constraintTop_toBottomOf="@+id/list_title" /> 125 126 <TextView 127 android:id="@+id/list_1" 128 android:layout_width="wrap_content" 129 android:layout_height="wrap_content" 130 android:layout_marginTop="8dp" 131 android:text='@{@string/list_index(1,list[1]),default=@string/list_index}' 132 app:layout_constraintStart_toStartOf="@+id/list_0" 133 app:layout_constraintTop_toBottomOf="@+id/list_0" /> 134 135 <TextView 136 android:id="@+id/list_2" 137 android:layout_width="wrap_content" 138 android:layout_height="wrap_content" 139 android:layout_marginTop="8dp" 140 android:text='@{@string/list_index(2,list[2]),default=@string/list_index}' 141 app:layout_constraintStart_toStartOf="@+id/list_1" 142 app:layout_constraintTop_toBottomOf="@+id/list_1" /> 143 144 <TextView 145 android:id="@+id/list_3" 146 android:layout_width="104dp" 147 android:layout_height="15dp" 148 android:layout_marginTop="8dp" 149 android:text='@{@string/list_index(-1,list[-1]) ,default=@string/list_index}' 150 app:layout_constraintStart_toStartOf="@+id/list_2" 151 app:layout_constraintTop_toBottomOf="@+id/list_2" /> 152 153 <TextView 154 android:id="@+id/thread_title" 155 android:layout_width="0dp" 156 android:layout_height="wrap_content" 157 android:layout_marginTop="32dp" 158 android:background="#f8f8f8" 159 android:paddingLeft="16dp" 160 android:text="在线程中修改数据" 161 app:layout_constraintEnd_toEndOf="parent" 162 app:layout_constraintStart_toStartOf="parent" 163 app:layout_constraintTop_toBottomOf="@+id/list_3" /> 164 165 <Button 166 android:id="@+id/thread_main_data" 167 android:layout_width="wrap_content" 168 android:layout_height="wrap_content" 169 android:layout_marginTop="16dp" 170 android:text="主线程修改data" 171 android:textAllCaps="false" 172 app:layout_constraintEnd_toStartOf="@+id/thread_other_data" 173 app:layout_constraintStart_toStartOf="parent" 174 app:layout_constraintTop_toBottomOf="@+id/thread_title" /> 175 176 <Button 177 android:id="@+id/thread_other_data" 178 android:layout_width="wrap_content" 179 android:layout_height="wrap_content" 180 android:layout_marginTop="16dp" 181 android:text="非主线程修改data" 182 android:textAllCaps="false" 183 app:layout_constraintEnd_toEndOf="parent" 184 app:layout_constraintStart_toEndOf="@+id/thread_main_data" 185 app:layout_constraintTop_toBottomOf="@+id/thread_title" /> 186 187 <Button 188 android:id="@+id/thread_main_list0" 189 android:layout_width="wrap_content" 190 android:layout_height="wrap_content" 191 android:layout_marginTop="16dp" 192 android:text="主线程修改list[0]" 193 android:textAllCaps="false" 194 app:layout_constraintEnd_toStartOf="@+id/thread_other_list0" 195 app:layout_constraintStart_toStartOf="parent" 196 app:layout_constraintTop_toBottomOf="@+id/thread_main_data" /> 197 198 <Button 199 android:id="@+id/thread_other_list0" 200 android:layout_width="wrap_content" 201 android:layout_height="wrap_content" 202 android:layout_marginTop="16dp" 203 android:text="非主线程修改list[0]" 204 android:textAllCaps="false" 205 app:layout_constraintEnd_toEndOf="parent" 206 app:layout_constraintStart_toEndOf="@+id/thread_main_list0" 207 app:layout_constraintTop_toBottomOf="@+id/thread_main_data" /> 208 209 <Button 210 android:id="@+id/thread_main_map1" 211 android:layout_width="wrap_content" 212 android:layout_height="wrap_content" 213 android:layout_marginTop="16dp" 214 android:text="主线程修改map[key1]" 215 android:textAllCaps="false" 216 app:layout_constraintEnd_toStartOf="@+id/thread_other_map1" 217 app:layout_constraintStart_toStartOf="parent" 218 app:layout_constraintTop_toBottomOf="@+id/thread_main_list0" /> 219 220 <Button 221 android:id="@+id/thread_other_map1" 222 android:layout_width="wrap_content" 223 android:layout_height="wrap_content" 224 android:layout_marginTop="16dp" 225 android:text="非主线程修改map[key1]" 226 android:textAllCaps="false" 227 app:layout_constraintEnd_toEndOf="parent" 228 app:layout_constraintStart_toEndOf="@+id/thread_main_map1" 229 app:layout_constraintTop_toBottomOf="@+id/thread_main_list0" /> 230 231 </androidx.constraintlayout.widget.ConstraintLayout> 232 </layout>
#.4 viewModel 放在集合里?
布局文件中通过viewModel访问数据,然后viewModel放在集合里?还会这么用?不解。