android——databinding中字符串的拼接处理、TextView显示的值随activity的属性值改变同时改变--LiveData、双向绑定过滤器、监听某个值的改变

简介

使用的技术是观察者与被观察者的模式,在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) &amp; &amp;  !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架构和数据绑定库如何协同工作来简化界面和数据之间的同步。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
好的,下面是一个简单的示例: 首先,在build.gradle文件添加以下依赖项: ```groovy // ViewModel and LiveData implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' // Data Binding implementation 'androidx.databinding:databinding-runtime:4.0.1' ``` 接下来,创建一个名为MainActivityActivity,并在其布局文件添加两个Fragment的占位符: activity_main.xml: ```xml <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <FrameLayout android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent"/> </layout> ``` MainActivity.java: ```java public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); // 加载第一个Fragment getSupportFragmentManager().beginTransaction() .replace(R.id.container, new FirstFragment()) .commit(); } } ``` 接下来,创建一个名为FirstFragment的Fragment,并在其布局文件使用DataBinding绑定数据: first_fragment.xml: ```xml <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{viewModel.text}" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Next" android:onClick="@{viewModel::onNextClicked}" /> </LinearLayout> </layout> ``` FirstFragment.java: ```java public class FirstFragment extends Fragment { private FirstViewModel viewModel; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { // 使用DataBinding绑定布局文件 FirstFragmentBinding binding = DataBindingUtil.inflate(inflater, R.layout.first_fragment, container, false); // 创建ViewModel实例 viewModel = ViewModelProviders.of(this).get(FirstViewModel.class); // 将ViewModel与布局文件的变量绑定 binding.setViewModel(viewModel); // 设置LifecycleOwner,以便LiveData知道何时更新UI binding.setLifecycleOwner(this); return binding.getRoot(); } } ``` 下面是FirstViewModel.java: ```java public class FirstViewModel extends ViewModel { private MutableLiveData<String> textLiveData = new MutableLiveData<>(); public FirstViewModel() { // 初始化LiveData的默认 textLiveData.setValue("Hello, World!"); } public LiveData<String> getText() { return textLiveData; } public void onNextClicked() { // 更新LiveData textLiveData.setValue("Next Clicked!"); } } ``` 最后,创建另一个名为SecondFragment的Fragment,并在MainActivity添加一个方法来加载它: second_fragment.xml: ```xml <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{viewModel.text}" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Previous" android:onClick="@{viewModel::onPreviousClicked}" /> </LinearLayout> </layout> ``` SecondFragment.java: ```java public class SecondFragment extends Fragment { private SecondViewModel viewModel; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { // 使用DataBinding绑定布局文件 SecondFragmentBinding binding = DataBindingUtil.inflate(inflater, R.layout.second_fragment, container, false); // 创建ViewModel实例 viewModel = ViewModelProviders.of(this).get(SecondViewModel.class); // 将ViewModel与布局文件的变量绑定 binding.setViewModel(viewModel); // 设置LifecycleOwner,以便LiveData知道何时更新UI binding.setLifecycleOwner(this); return binding.getRoot(); } } ``` 下面是SecondViewModel.java: ```java public class SecondViewModel extends ViewModel { private MutableLiveData<String> textLiveData = new MutableLiveData<>(); public SecondViewModel() { // 初始化LiveData的默认 textLiveData.setValue("Goodbye, World!"); } public LiveData<String> getText() { return textLiveData; } public void onPreviousClicked() { // 更新LiveData textLiveData.setValue("Previous Clicked!"); } } ``` 最后,在MainActivity添加一个方法来加载SecondFragment: ```java private void loadSecondFragment() { getSupportFragmentManager().beginTransaction() .replace(R.id.container, new SecondFragment()) .commit(); } ``` 现在,你的MVVM Android项目就完成了!你可以使用LiveData和ViewModel来管理数据,并使用DataBinding将数据绑定到UI上。Lifecycle组件可确保UI在活动和片段之间正确地进行管理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wy313622821

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值