Android---Jetpack之DataBinding

DataBinding 的意义

让布局文件承担了部分原本属于页面的工作,使页面与布局耦合度进一步降低。

DataBinding 的应用

使用 dataBinding 需要在 gradle 里添加如下代码

    dataBinding{
        enabled = true
    }

应用实现

 activity_main.xml

<?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="Idol"
            type="com.example.databinding.Idol" />
        <variable
            name="eventHandle"
            type="com.example.databinding.EventHandlerListener" />
        <!-- 引入一个类 -->
        <import type="com.example.databinding.StarUtils"/>
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/guideline3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            app:layout_constraintGuide_percent="0.5" />

        <ImageView
            android:id="@+id/imageView"
            android:layout_width="300dp"
            android:layout_height="300dp"
            android:src="@drawable/lanyangyang"
            app:layout_constraintBottom_toTopOf="@+id/guideline3"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.495"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.56"
            tools:srcCompat="@tools:sample/avatars" />

        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            tools:text="姓名"
            android:text="@{Idol.name}"
            android:textSize="24sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.498"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="@+id/guideline3"
            app:layout_constraintVertical_bias="0.184" />

        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="44dp"
            tools:text="五星"
            android:text="@{StarUtils.getStar(Idol.star)}"
            android:textSize="18sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView" />

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="72dp"
            android:text="喜欢"
            android:onClick="@{eventHandle.buttonOnClick}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.498"
            app:layout_constraintStart_toStartOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

注意:使用 databinding 是要修改 xml 布局的。可以看到,上面的布局整个是包裹住 <layout></layout> 里面的,并且里面还要 <data></data>。当我们在 gradle 里引入了 dataBinding 后,Android studio 也给我们提供了一键生成<layout></layout> 的方法,如下图:

 通过上图3不操作后,就能只能生成 <layout></layout>。下面解释一下<data></data> 以及里面的<variable>变量

 当我在 xml 里有了 Idol 对象,就可以在xml里调用 Idol 里的成员变量方法等。

 直接取 Idol 里 name 变量赋值给 text。

Idol.java

package com.example.databinding;

public class Idol {
    public String name;

    public int star;

    public Idol(String name, int star) {
        this.name = name;
        this.star = star;
    }
}

starUtils.java

package com.example.databinding;

public class StarUtils {
    public static String getStar(int star){
        switch (star) {
            case 1:
                return "一星";
            case 2:
                return "二星";
            case 3:
                return "三";
            case 4:
                return "四星";
            case 5:
                return "五星";
        }
        return "";
    }
}

 EventHandlerListener.java

package com.example.databinding;

import android.content.Context;
import android.view.View;
import android.widget.Toast;

import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.content.ContextCompat;

import com.google.android.material.tabs.TabItem;

public class EventHandlerListener {

    private Context context;

    public EventHandlerListener(Context context) {
        this.context = context;
    }

    public void buttonOnClick(View view){
        Toast.makeText(context, "超可爱", Toast.LENGTH_SHORT).show();
    }
}

MainActivity.java

package com.example.databinding;

import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;

import android.os.Bundle;

import com.example.databinding.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        Idol idol = new Idol("懒洋洋", 4);
        //TODO setIdol 是 activity_main 里面 variable 下的 Idol, 给它赋一个 idol 对象,
        // 那么就可以在 xml 里操作 Idol 里面的成员变量合方法了
        binding.setIdol(idol);

        binding.setEventHandle(new EventHandlerListener(this));
    }
}

 因为我们在 <data></data> 的 <variable/> 里有关 name="Idol" 和 name="eventHandle" 两个,所以就可以直接在 MainActivity 里 setIdol(new Idol("懒洋洋", 4)) 和 setEventHandle(new ....)。

 

> 里有

二级页面的绑定

<include>标签引用二级页面

应用实现

 sub.xml

<?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="Idol"
            type="com.example.databinding2.Idol" />
        <!-- 引入一个类 -->
        <import type="com.example.databinding2.StarUtils"/>
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/textView3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="44dp"
            tools:text="懒洋洋"
            android:text="@{Idol.name}"
            android:textSize="24sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/textView4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="64dp"
            tools:text="五星"
            android:text="@{StarUtils.getStar(Idol.star)}"
            android:textSize="16sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView3" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

使用二级页面的注意点就是,如何将 idol 对象在 activity_main.xml 里传入到二级页面里(sub.xml)

main_activity.xml

<?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="Idol"
            type="com.example.databinding2.Idol" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/guideline3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            app:layout_constraintGuide_percent="0.5" />

        <ImageView
            android:id="@+id/imageView"
            android:layout_width="300dp"
            android:layout_height="300dp"
            android:src="@drawable/lanyangyang"
            app:layout_constraintBottom_toTopOf="@+id/guideline3"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.495"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.56"
            tools:srcCompat="@tools:sample/avatars" />

        <include
            layout="@layout/sub"
            app:Idol="@{Idol}"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="@+id/guideline3" />


    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

 注意:app:Idol 的这个 Idol 要与 sub.xml 里的 name="Idol" 要一样。

 

自定义 BindingAdapter

应用实现

 

\bullet 加载网络图片

activity_main.xml

<?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="netWorkImage"
            type="String" />
        <variable
            name="localImage"
            type="Integer" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <ImageView
            android:id="@+id/imageView"
            app:image="@{netWorkImage}"
            android:layout_width="300dp"
            android:layout_height="300dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.498"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:srcCompat="@tools:sample/avatars" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

ImageViewBindingAdapter.java

package com.example.databinding3;

import android.graphics.Color;
import android.text.TextUtils;
import android.util.Log;
import android.widget.ImageView;

import androidx.databinding.BindingAdapter;

import com.squareup.picasso.Picasso;

public class ImageViewBindingAdapter {

    // TODO 下面的三个方法名相同,但参数不同 ---> 方法重载

    //TODO 加载网络图片
    @BindingAdapter("image")
    public static void setImage(ImageView image, String Url){
        if (!TextUtils.isEmpty(Url)) {
            Picasso.get()
                    .load(Url)
                    .into(image);

        }else {
            image.setBackgroundColor(Color.GRAY);
        }
    }

}

MainActivity.java

package com.example.databinding3;

import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;

import android.os.Bundle;

import com.example.databinding3.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        binding.setNetWorkImage("https://nimg.ws.126.net/?url=http%3A%2F%2Fdingyue.ws.126.net%2F2022%2F0613%2Fcdf32afaj00rdepyy001vc000zk00geg.jpg&thumbnail=660x2147483647&quality=80&type=jpg");
        //binding.setLocalImage(R.drawable.yan);
    }
}

 我们使用 @BindingAdapter 注解,然后 就会将 xml 里 networkImage 的内容和 这个 ImageView 一起出传到有@BindingAdapter 注解的这个方法里(根据"iamge"对应,当然也可以写其它的内容)

 

\bullet 方法重载,加载本地图片

在 ImageViewBindingAdapter 里添加如下代码,并修改 xml 。我们可以看到,注解里的内容(localImage)与 app:localImage 对应。

    //TODO 加载本地图片
    @BindingAdapter("localImage")
    public static void setImage(ImageView image, Integer resId){
        image.setImageResource(resId);
    }

\bullet 多参数重载

多重参数可以实现参数的选择,先加载网络图片,如果失败,就加载本地图片。在 ImageViewBindingAdapter 里添加如下代码,并修改 xml 。

//TODO 参数可选,先加载网络图片,如果 Url 为空,再加载本地图片
    @BindingAdapter(value = {"image", "defaultImage"}, requireAll = false)
    public static void setImage(ImageView image, String Url, Integer resId){
        if (!TextUtils.isEmpty(Url)) {
            Picasso.get()
                    .load(Url)
                    .into(image);

        }else {
            image.setImageResource(resId);
        }
    }

 

双向绑定

\bullet BaseObservable 与 ObservableField

 

应用实现

1. 使用 BaseObservable

activity_main.xml

<?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="userViewModel"
            type="com.example.databinding4.UserViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <EditText
            android:id="@+id/editTextTextPersonName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:ems="10"
            android:inputType="textPersonName"
            android:text="@={userViewModel.userName}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

 User.java

package com.example.databinding4;

public class User {
    public String userName;

    public User(String userName) {
        this.userName = userName;
    }
}

UserViewModel.java 继承 BaseObservable

package com.example.databinding4;

import android.util.Log;

import androidx.databinding.BaseObservable;
import androidx.databinding.Bindable;

public class UserViewModel extends BaseObservable {
    private final User user;

    public UserViewModel(){
        this.user = new User("HL");
    }

    /**
     * TODO 当 user.userName 的内容发生改变时(后台操作),也会同步到界面(EditText)
     */
    @Bindable
    public String getUserName(){
        return user.userName;
    }

    /**
     *TODO 当 EditText 的内容发生改变时(界面操作),也会同步到 user.userName
     * @param userName
     */
    public void setUserName(String userName){
        if (userName != null && !user.userName.equals(userName)) {
            user.userName = userName;
            Log.d("HL", "setUserName: " + userName);
            notifyPropertyChanged(BR.userName);
        }
    }

MainActivity.java 

package com.example.databinding4;

import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;

import android.os.Bundle;

import com.example.databinding4.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        binding.setUserViewModel(new UserViewModel());
    }
}

2. 使用 ObservableField

修改 UserViewModel 内容如下,不用再继承 BaseObservable。这种方法代码更简单

package com.example.databinding5;


import android.util.Log;

import androidx.databinding.ObservableField;

public class UserViewModel{
    // TODO 使用 ObservableField
    private ObservableField<User> userObservableField;

    public UserViewModel(){
        userObservableField = new ObservableField<>();
        User user = new User("HL");
        userObservableField.set(user);
    }

    public String getUserName(){
        return userObservableField.get().userName;
    }

    public void setUserName(String userName){
        Log.d("HL", "userObservableField: " + userName);
        userObservableField.get().userName = userName;
    }
}

RecyclerView 的绑定

MyRecyclerViewAdapter extends RecyclerView.Adapter。在 onCreateViewHolder 里拿到 item.xml。在 onBindViewHolder 里给每个 item 绑定 idol。其它代码的写法与常规使用 RecyclerView 一样。

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        ItemBinding inflate = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.item, parent, false);
        return new MyViewHolder(inflate);
    }
    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        Idol idol = idols.get(position);
        holder.itemBinding.setIdol(idol); //TODO 把 idol 对象设置到 item.xml 里面了
    }

 

DataBinding+ViewModel+LiveData

引用实现

 activity_main.xml

<?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.example.databinding7.MyViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/guideline2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            app:layout_constraintGuide_percent="0.050615594" />

        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/guideline3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            app:layout_constraintGuide_percent="0.15458277" />

        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/guideline4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            app:layout_constraintGuide_begin="310dp" />

        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/guideline5"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            app:layout_constraintGuide_begin="225dp" />

        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/guideline6"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            app:layout_constraintGuide_begin="390dp" />

        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/guideline7"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            app:layout_constraintGuide_begin="482dp" />

        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/guideline8"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            app:layout_constraintGuide_begin="556dp" />

        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/guideline9"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:layout_constraintGuide_end="206dp" />

        <TextView
            android:id="@+id/a_team"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="TeamA"
            android:textSize="16sp"
            app:layout_constraintBottom_toTopOf="@+id/guideline3"
            app:layout_constraintEnd_toStartOf="@+id/guideline9"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="@+id/guideline2" />

        <TextView
            android:id="@+id/b_team"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="TeamB"
            android:textSize="16sp"
            app:layout_constraintBottom_toTopOf="@+id/guideline3"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="@+id/guideline9"
            app:layout_constraintTop_toTopOf="@+id/guideline2" />

        <TextView
            android:id="@+id/a_score"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(viewModel.getaTeamScore)}"
            android:textColor="@android:color/holo_red_light"
            android:textSize="44sp"
            app:layout_constraintBottom_toTopOf="@+id/guideline5"
            app:layout_constraintEnd_toStartOf="@+id/guideline9"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="@+id/guideline3" />

        <TextView
            android:id="@+id/b_score"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(viewModel.getbTeamScore)}"
            android:textColor="@color/teal_200"
            android:textSize="44sp"
            app:layout_constraintBottom_toTopOf="@+id/guideline5"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="@+id/guideline9"
            app:layout_constraintTop_toTopOf="@+id/guideline3" />

        <Button
            android:id="@+id/a_add1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="+1"
            android:onClick="@{()->viewModel.aTeamAdd(1)}"
            android:textSize="16sp"
            app:layout_constraintBottom_toTopOf="@+id/guideline4"
            app:layout_constraintEnd_toStartOf="@+id/guideline9"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="@+id/guideline5" />

        <Button
            android:id="@+id/b_add1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="+1"
            android:onClick="@{()->viewModel.bTeamAdd(1)}"
            android:textSize="16sp"
            app:layout_constraintBottom_toTopOf="@+id/guideline4"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="@+id/guideline9"
            app:layout_constraintTop_toTopOf="@+id/guideline5" />

        <Button
            android:id="@+id/a_add2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="+2"
            android:onClick="@{()->viewModel.aTeamAdd(2)}"
            android:textSize="16sp"
            app:layout_constraintBottom_toTopOf="@+id/guideline6"
            app:layout_constraintEnd_toStartOf="@+id/guideline9"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="@+id/guideline4" />

        <Button
            android:id="@+id/b_add2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="+2"
            android:onClick="@{()->viewModel.bTeamAdd(2)}"
            android:textSize="16sp"
            app:layout_constraintBottom_toTopOf="@+id/guideline6"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="@+id/guideline9"
            app:layout_constraintTop_toTopOf="@+id/guideline4" />

        <Button
            android:id="@+id/a_add3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="+3"
            android:onClick="@{()->viewModel.aTeamAdd(3)}"
            android:textSize="16sp"
            app:layout_constraintBottom_toTopOf="@+id/guideline7"
            app:layout_constraintEnd_toStartOf="@+id/guideline9"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="@+id/guideline6"
            app:layout_constraintVertical_bias="0.454" />

        <Button
            android:id="@+id/b_add3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="+3"
            android:onClick="@{()->viewModel.bTeamAdd(3)}"
            android:textSize="16sp"
            app:layout_constraintBottom_toTopOf="@+id/guideline7"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="@+id/guideline9"
            app:layout_constraintTop_toTopOf="@+id/guideline6" />

        <ImageView
            android:id="@+id/clear"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:onClick="@{()->viewModel.undo()}"
            app:layout_constraintBottom_toTopOf="@+id/guideline8"
            app:layout_constraintEnd_toStartOf="@+id/guideline9"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="@+id/guideline7"
            app:srcCompat="@drawable/clear" />

        <ImageView
            android:id="@+id/resent"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:onClick="@{()->viewModel.resentScore()}"
            app:layout_constraintBottom_toTopOf="@+id/guideline8"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="@+id/guideline9"
            app:layout_constraintTop_toTopOf="@+id/guideline7"
            app:srcCompat="@drawable/resent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

MyViewModel.java

package com.example.databinding7;

import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

public class MyViewModel extends ViewModel {
    private MutableLiveData<Integer> aTeamScore;
    private MutableLiveData<Integer> bTeamScore;
    private Integer aLast;
    private Integer bLast;

    public MutableLiveData<Integer> getaTeamScore() {
        if (aTeamScore == null) {
            aTeamScore = new MutableLiveData<>();
            aTeamScore.setValue(0);
        }
        return aTeamScore;
    }

    public MutableLiveData<Integer> getbTeamScore() {
        if (bTeamScore == null) {
            bTeamScore = new MutableLiveData<>();
            bTeamScore.setValue(0);
        }
        return bTeamScore;
    }

    /**
     * A 队得分
     * @param score 所得的分数(1/2/3)
     */
    public void aTeamAdd(int score){
        recordLastScore();
        aTeamScore.setValue(aTeamScore.getValue() + score);
    }

    /**
     * B 队得分
     * @param score 所得的分数(1/2/3)
     */
    public void bTeamAdd(int score){
        recordLastScore();
        bTeamScore.setValue(bTeamScore.getValue() + score);
    }

    /**
     * 返回上一次的得分
     */
    public void undo(){
        aTeamScore.setValue(aLast);
        bTeamScore.setValue(bLast);
    }

    /**
     * 记录上一次的得分
     */
    public void recordLastScore(){
        this.aLast = aTeamScore.getValue();
        this.bLast = bTeamScore.getValue();
    }

    /**
     * 分数重置为 0
     */
    public void resentScore(){
        aTeamScore.setValue(0);
        bTeamScore.setValue(0);
    }
}

MainActivity.java

package com.example.databinding7;

import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;
import androidx.lifecycle.ViewModelProvider;

import android.os.Bundle;

import com.example.databinding7.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        MyViewModel myViewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyViewModel.class);
        binding.setViewModel(myViewModel);
        binding.setLifecycleOwner(this);
    }
}

 

DataBinding 的优势

\bullet 不再需要 findViewById,项目更加简洁,可读性更高。

\bullet 布局文件可以包含简单的业务逻辑

完整Demo

提供上面所有应用的完整项目:

链接:https://pan.baidu.com/s/1xiK_h64IH1vnEzPOehh1ww 
提取码:5qzr

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/7268.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python argparse的使用,本文基本够用

一、前言 在学习深度学习会发现都比较爱用python这个argparse&#xff0c;虽然基本能理解&#xff0c;但没有仔细自己动手去写&#xff0c;因此这里写下来作为自己本人的学习笔记 argparse是python的一个命令行参数解析包&#xff0c;在代码需要频繁修改参数时&#xff0c;方便…

Shell编程之免交互

目录交互的概念与Linux中的运用Here Document 免交互tee命令重定向输出加标准输出支持变量替换多行注释Expect实例操作免交互预设值修改用户密码创建用户并设置密码实现 ssh 自动登录交互的概念与Linux中的运用 交互&#xff1a;当计算机播放某多媒体程序的时候&#xff0c;编…

步进频雷达的一维距离像matlab仿真

步进频雷达的一维距离像matlab仿真发射与回波信号模型距离高分辨原理仿真分析不进行步进频高分辨一维距离像进行步进频高分辨一维距离像代码发射与回波信号模型 步进频率信号发射得的是一串窄带的相参脉冲&#xff0c;每个脉冲的载频之间是均匀线性步进的&#xff0c;经过相参本…

Spring依赖注入详解

1.set注入 启动容器后看看到底能不能拿到teacherService的值。可以看到拿到了值。我们具体来分析怎么注入的 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean 发现pvs里面有一个我们自己set的值 直接进行属性赋值。 org.springf…

【创作赢红包】Python第3章 流程控制

这里写目录标题【本章导读】真值测试比较运算成员运算for循环while循环项目实训1项目实训2项目实训3项目实训4&#xff1a;项目实训5&#xff1a;项目实训6&#xff1a;项目实训7&#xff1a;项目实训8项目实训9&#xff1a;项目实训10:项目实训11&#xff1a;项目实训12&#…

【Redis7】Redis7 十大数据类型

【大家好&#xff0c;我是爱干饭的猿&#xff0c;本文重点介绍Redis7 十大数据类型。 后续会继续分享Redis7和其他重要知识点总结&#xff0c;如果喜欢这篇文章&#xff0c;点个赞&#x1f44d;&#xff0c;关注一下吧】 上一篇文章&#xff1a;《【Redis7】Redis7概述、安装…

PCB生产工艺流程五:PCB生产工艺流程的第3步,钻孔的分类及目的

PCB生产工艺流程五&#xff1a;PCB生产工艺流程的第3步&#xff0c;钻孔的分类及目的 今天第五期的内容就是详细讲述PCB工艺流程第三步——钻孔&#xff0c;忘记第二步的小伙伴看这里 PCB工艺流程第2步&#xff0c;你了解多少&#xff1f; 钻孔的目的 在板面上钻出层与层之间…

【Java项目】SpringBoot实现一个请求同时上传多个文件和类并附上代码实例

文章目录前言文件多线程兼多文件上传RequestParam和RequestPart的区别前言 项目在做二手市场&#xff0c;然后商品的提交我们希望对商品的描述和商品的照片能一起传递到同一个接口来统一处理&#xff0c;而不是分发到两个接口中去处理&#xff0c;因为如果分到两个接口那么会特…

大语言模型带来的一些启发

仅代表个人看法&#xff0c;不喜勿喷。 The limits of my language means the limits of my world. (Ludwig Wittgenstein) 我的语言的极限意味着我的世界的极限。——维特根斯坦 大语言模型解决的不仅是处理文本相关问题&#xff0c;它带来的是人对世界的理解&#xff0c;或者…

华为网络篇 单臂路由-17

实验难度 2实验复杂度2目录 一、实验原理 二、实验拓扑 三、实验步骤 四、实验过程 总结 一、实验原理 单臂路由&#xff08;router-on-a-stick&#xff09;是指在路由器的一个接口上通过配置子接口&#xff08;或“逻辑接口”&#xff0c;并不存在真正物理接口&…

EasyExcel的简单使用(easyExcel和poi)

EasyExcel的简单使用 前言 Excel读 1.实体类 2.读监听器与测试类 3.输出结果 Excel写 1.实体类 2.写入Excel的测试类 3.输出结果 填充Excel 1.Excel模板 2.测试类 3.输出结果 前言 EasyExcel类是一套基于Java的开源Excel解析工具类&#xff0c;相较于传统的框架如Apache poi、…

Go 语言数组和切片的区别

原文链接&#xff1a; Go 语言数组和切片的区别 在 Go 语言中&#xff0c;数组和切片看起来很像&#xff0c;但其实它们又有很多的不同之处&#xff0c;这篇文章就来说说它们到底有哪些不同。 另外&#xff0c;这个问题在面试中也经常会被问到&#xff0c;属于入门级题目&…

堆及其堆排序

堆是一种特殊的数据结构&#xff0c;底层实现是靠数组存储以及完全二叉树的性质 文章目录一、堆概念二、堆实现三、堆源码四、堆排序一、堆概念 完全二叉树用数组来存储可以达到空间的有效利用且可以直观反映它们之间的逻辑关系&#xff0c;双亲与孩子之间的关系。一般在数组中…

一文说透虚拟内存

为什么我们需要虚拟内存 提供一个虚拟化封装&#xff0c;让上层的程序员不用担心内存分配&#xff0c;物理地址的总大小。同时如果要手动管理内存是一件麻烦的事&#xff0c;比如一个程序读到另一个程序的物理地址&#xff0c;并且也很难保障多个处理器不会同时读取写入同一块…

GitHub Action 使用

GitHub Action 使用 GitHub Actions 是一种持续集成和持续交付 (CI/CD) 平台&#xff0c;可用于自动执行生成、测试和部署管道。 您可以创建工作流程来构建和测试存储库的每个拉取请求&#xff0c;或将合并的拉取请求部署到生产环境。GitHub 提供 Linux、Windows 和 macOS 虚拟…

训练AI数据模型所需要的高性能计算机配置

目录 配置一 配置二 配置三 云服务器和超级计算机 AI模型训练是一种机器学习的过程&#xff0c;通过训练深度学习模型来自动化处理数据和完成任务。AI训练可以帮助企业和研究人员开发出更加智能、高效的应用&#xff0c;从而提高生产力和创新能力。 以下是按训练性能从低到…

对挖矿病毒 kdevtmpfsi 的处理办法

需求背景&#xff1a; 服务器CPU资源使用一直处于100%的状态&#xff0c;通过 top 命令查看&#xff0c;发现可疑进程 kdevtmpfsi。通过 google搜索&#xff0c;发现这是挖矿病毒。 排查方法 首先&#xff1a;查看 kdevtmpfsi 进程&#xff0c;使用 ps -ef | grep kdevtmpfsi …

数据结构之线性表

文章目录1. 线性表的定义2. 线性表的抽象数据类型3. 线性表的顺序存储结构4. 线性表的链式存储结构5. 单链表结构和顺序存储结构优缺点6. 静态链表7. 循环链表8. 双向链表1. 线性表的定义 零个或多个数据元素的有限序列 线性表的定义中强调有限和序列两个方面。 有限&#xff…

华硕ROG|玩家国度 冰刃7双屏 GX650PY Windows11原厂预装系统 工厂模式恢复安装带ASUSRecevory一键还原

华硕ROG|玩家国度 冰刃7双屏 GX650PY Windows11原厂预装系统 工厂模式恢复安装带ASUSRecevory一键还原 文件地址&#xff1a;https://pan.baidu.com/s/1snKOsH3OMl3GZLqeAf-GLA?pwd8888 华硕工厂恢复系统 &#xff0c;安装结束后带隐藏分区以及机器所有驱动软件 需准备一个…

【树】你真的会二叉树了嘛? --二叉树LeetCode专题Ⅲ

Halo&#xff0c;这里是Ppeua。平时主要更新C语言&#xff0c;C&#xff0c;数据结构算法......感兴趣就关注我吧&#xff01;你定不会失望。 &#x1f308;个人主页&#xff1a;主页链接 &#x1f308;算法专栏&#xff1a;专栏链接 我会一直往里填充内容哒&#xff01; &…