简介
使用的技术是观察者与被观察者的模式,在google推荐的案例中也有使用到,现在我把它封装成一个扩展函数,使得使用更加简单明了
注意
1、在build.gradle添加databinding,在android{}节点添加
dataBinding {
enabled true
}
或者(一般用下面的) :
buildFeatures {
dataBinding = true
viewBinding true
}
2、在activity里绑定binding
binding = ActivityMainBinding.inflate(layoutInflater)
baseViewModel = ViewModelProvider(this)[MainViewModel::class.java]
binding.viewModel = baseViewModel
binding.lifecycleOwner = this
setContentView(binding.root)
// 另外一种写法
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
baseViewModel = ViewModelProvider(this)[MainViewModel::class.java]
binding.viewModel = baseViewModel
binding.lifecycleOwner = this
也可以使用:
val parametOrderVM = ViewModelProvider(this)[ParametersSetViewModel::class.java]
在fragment里绑定binding: _binding = FragmentReturnMainBinding.inflate(inflater, container, false)
3、不想写findviewbyid之类的代码,可以添加以下代码到build.gradle里面(这种方法不推荐,测试的项目中使用可以)
id 'kotlin-android-extensions'
推荐使用
buildFeatures {
viewBinding true
}
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// 然后使用binding 去获取控件
1、TextView显示的值随activity的属性值改变同时改变–LiveData
代码
MainActivity
class MainActivity : AppCompatActivity() {
private val mutableLiveData = MutableLiveData<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tv2.bindValue(mutableLiveData,this)
bt1.setOnClickListener {
// 第二种使用扩展函数封装好的模式
mutableLiveData.value="我爱李曼娜"
}
}
/**
* TextView的扩展函数
* 使用方法:
* val mutableLiveData = MutableLiveData<String>()
* tv2.bindValue(mutableLiveData,this)
*
* 在按钮事件里调用:mutableLiveData.value="我爱你"
* TextView中显示的值就会跟着改变
*/
fun TextView.bindValue( mutableLiveData:MutableLiveData<String>,lifecycleOwner:LifecycleOwner){
var text1: LiveData<String> = Transformations.map(mutableLiveData) {
// "观察者: $it"
it
}
text1.observe(lifecycleOwner, Observer<String> {
this.text = it
})
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/tv2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
<Button
android:id="@+id/bt1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="赋值3"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
2、xml使用layout–data方式双向绑定
功能:点击按钮按钮换背景颜色,字体换颜色,textview换内容,效果图:
代码:
public class User {
private String status;
private String hint;
private boolean isOnline;
public User(String status, String hint, boolean isOnline) {
this.status = status;
this.hint = hint;
this.isOnline = isOnline;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getHint() {
return hint;
}
public void setHint(String hint) {
this.hint = hint;
}
public boolean getIsOnline() {
return isOnline;
}
public void setIsOnline(boolean isOnline) {
this.isOnline = isOnline;
}
}
MainActivity :
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import android.os.Bundle;
import android.view.View;
import com.example.databinding.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
User user = new User("已下线", "快开启一天的旅程吧!!", false);
binding.setUser(user);
}
public void switchStatus(View view) {
User user = new User("已上线", "祝您旅途愉快,谢谢!!", true);
binding.setUser(user);
}
}
<?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"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="user"
type="com.example.databinding.User" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tvStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.status}"
app:layout_constraintBottom_toTopOf="@+id/tvHint"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvHint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.hint}"
app:layout_constraintBottom_toTopOf="@+id/button"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvStatus" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@{user.isOnline?@drawable/btn_shape:@drawable/btn_shape_press}"
android:onClick="switchStatus"
android:text="状态切换"
android:textColor="@{user.isOnline?@color/purple_200:@color/teal_200}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvHint" />
<!-- android:background="@{user.isOnline?@color/purple_200:@color/teal_200}"-->
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
btn_shape.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="10dp" />
<solid android:color="#2196F3" />
</shape>
btn_shape_press.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="10dp" />
<solid android:color="#FF018786" />
</shape>
主题:<style name="Theme.Databinding" parent="Theme.MaterialComponents.DayNight.NoActionBar.Bridge">
下载地址:https://download.csdn.net/download/wy313622821/33937925
注意:
1.ActivityMainBinding 这里的命名需要与xml的名称对应
3、MVVM双向绑定(过滤器)
<?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"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.abtalk.databinding.MainViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.age+`:字符串:`}"
android:visibility="@{viewModel.visibility}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<!--android:visibility="@{viewModel.visibility}" 这里比较复杂的逻辑运算放到mainactivity代码里写-->
<TextView
android:id="@+id/tv11"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{(viewModel.age>13)?@string/cnr:@string/w_cnr}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="change"
android:text="改变"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
activity代码
class MainActivity : AppCompatActivity() {
private lateinit var mainViewModel: MainViewModel
private lateinit var databindng: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
databindng = DataBindingUtil.setContentView(this, R.layout.activity_main)
mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)
databindng.viewModel = mainViewModel
}
fun change(view: View) {
mainViewModel.age.set(15)
}
}
MainViewModel
class MainViewModel : ViewModel() {
val age = ObservableInt(1)
val visibility = object : ObservableInt(age) {
override fun get(): Int {
Log.e("wang", "===>")
return if (age.get() > 13)
View.GONE
else
View.VISIBLE
}
}
}
注意:上面的visibility如果没有在xml里面绑定的话,是不会触发输出的(Log.e(“wang”, “===>”))
String.xml
<resources>
<string name="app_name">Databinding</string>
<string name="cnr">成年人</string>
<string name="w_cnr">未成年人</string>
</resources>
完整的代码地址:https://download.csdn.net/download/wy313622821/86905039
4、dataBinding中TextView中字符串的拼接处理
第一种:
android:text="@{ `字符串:` + str}"/>
第二种:
android:text=‘@{@string/user_name+bean.name}’
这里的引号需要使用单引号
5、监听某个值的改变,并做一些其他的操作
某个值为:
var aa: ObservableField = ObservableField(0)
监听:
aa.addOnPropertyChangedCallback(object : OnPropertyChangedCallback() {
override fun onPropertyChanged(observable: Observable?, i: Int) {
Log.e("wang","===>"+i)
}
})
让这个值改变
aa.set(10)
这样就会触发上面的回调接口
6、绑定点击事件
第一步,activity中把xml里的viewmodel跟activity的viewmodel实例进行绑定
private val returnListViewModel by viewModels<ReturnListViewModel>()
//在oncreat中
binding.viewModel = returnListViewModel
// xml代码中
<variable
name="viewModel"
type="***.medicine.module_return.returned.viewmodel.ReturnListViewModel" />
第二步,带参数的点击:@{() -> viewModel.click(1)}
//xml:
android:onClick="@{() -> viewModel.click(1)}"
//ViewModel:
public void click(num:Int){
}
课外知识:可以把xml里的frag跟activity的this实例进行绑定,把绑定点击事件放在fragment里
// xml代码中
<variable
name="frag"
type="***.medicine.module_return.returned.viewmodel.Fragment" />
// fragment代码中
binding.frag = this
双向绑定的显示隐藏需要用到View:
<import type="android.view.View"/>
就可以使用View.Gone
7、高级用法
android:text='@{username??"null"}'
如果表达式username为null时,则显示“null”, 不为空显示username的值
<data>
<!--使用Java某些类都要通过import导入才能正常使用-->
<import type="android.text.TextUtils" alias="textUtlis" />
</data>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/bt_login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="@{!textUtlis.isEmpty(username)?!textUtlis.isEmpty(password)?true:false:false}"
android:onClick='@{v->loginModel.login(username ?? "",password ?? "",v) }'
android:text="登陆" />
使用 && 进行多条件判断
我们都希望可以在databinding layout表达式中直接使用&& 逻辑判断,但遗憾会报错!这里可以使用&的转义字符 & 替代,则上面的用户名密码判空操作可以直接写成
android:enabled="@{!TextUtlis.isEmpty(username) & & !TextUtlis.isEmpty(password)}"
附加:项目中遇到的问题
1、如果发现已经绑定了数据和视图,改变数据发现视图没有变化,怎么办?
检查是否有下面的:
binding = ActivityMainBinding.inflate(layoutInflater)
baseViewModel = ViewModelProvider(this)[MainViewModel::class.java]
binding.viewModel = baseViewModel
binding.lifecycleOwner = this
setContentView(binding.root)
如果是include的控件,要把id加上
2、android mvvm绑定数据和视图,例如textview根据viewmodel里的数字1,2,3分别显示不同的文字,如何写?
在Android开发中,使用MVVM架构时,数据的绑定通常是通过数据绑定库(Data Binding Library)来实现的。当你想根据ViewModel中的数字来显示不同的文字时,你可以利用数据绑定表达式来实现。
以下是一个简单的例子,展示了如何使用MVVM架构和数据绑定库来实现这个需求:
定义ViewModel
首先,你需要定义一个ViewModel,这个ViewModel里有一个数字属性和一个根据这个数字属性返回不同字符串的方法。
class MyViewModel : ViewModel() {
private var number = MutableLiveData(1)
val textToShow: LiveData<String> = Transformations.map(number) {
when (it) {
1 -> "显示文字1"
2 -> "显示文字2"
3 -> "显示文字3"
else -> "未知数字"
}
}
fun setNumber(newNumber: Int) {
number.value = newNumber
}
}
在布局文件中使用数据绑定
在你的布局文件中(比如activity_main.xml),你需要定义一个TextView并使用数据绑定来显示ViewModel中的textToShow。
xml
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewModel"
type="com.example.MyViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.textToShow}" />
<!-- 其他视图 -->
</LinearLayout>
</layout>
在Activity或Fragment中设置ViewModel
在你的Activity或Fragment中,你需要设置这个ViewModel,并将其与布局文件中的数据变量绑定。
kotlin
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var viewModel: MyViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
binding.viewModel = viewModel
// 设置初始数字或根据用户交互改变数字
viewModel.setNumber(1)
}
}
在这个例子中,当number的值改变时,textToShow的值也会自动更新,并且TextView的文本也会相应地改变。这就是MVVM架构和数据绑定库如何协同工作来简化界面和数据之间的同步。