一、ViewModel
1、基本介绍
- ViewModel 属于 Android Jetpack 架构组件的一部分,ViewModel 被设计用来存储和管理与 UI 相关的数据,这些数据在配置更改(例如,屏幕旋转)时能够幸存下来,ViewModel 的生命周期与 Activity 或 Fragment 的生命周期紧密相关,但比它们更长,这意味着即使 Activity 或 Fragment 被重新创建,ViewModel 中的数据也会保留下来,它有如下特点
-
配置更改时数据保留:当屏幕旋转或发生其他配置更改时,ViewModel 中的数据不会丢失
-
生命周期管理:ViewModel 在关联的 Activity 或 Fragment 被销毁时也会被清理,但它在配置更改时会保留下来
-
数据共享:可以在多个 Fragment 或 Activity 之间共享同一个 ViewModel 实例,以共享数据
-
与 LiveData 配合使用:ViewModel 通常与 LiveData 一起使用,以便在数据发生变化时通知 UI 更新
2、不使用 ViewModel
(1)Activity Layout
- activity_view_model_no_use.xml
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=".ViewModelNoUseActivity">
<Button
android:id="@+id/btn_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="add"
android:text="add"
android:textSize="20sp"
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" />
<TextView
android:id="@+id/tv_num"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.35000002" />
</androidx.constraintlayout.widget.ConstraintLayout>
(2)Activity Code
- ViewModelNoUseActivity.java
java
package com.my.viewmodel;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
public class ViewModelNoUseActivity extends AppCompatActivity {
private TextView tvNum;
private int num;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_model_no_use);
tvNum = findViewById(R.id.tv_num);
}
public void add(View view) {
tvNum.setText(String.valueOf(++num));
}
}
3、使用 ViewModel
(1)ViewModel
- MyViewModel.java
java
package com.my.jetpackdemo.viewmodel;
import androidx.lifecycle.ViewModel;
public class MyViewModel extends ViewModel {
public int num;
}
(2)Activity Layout
- activity_view_model_no_use.xml
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=".ViewModelNoUseActivity">
<Button
android:id="@+id/btn_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="add"
android:text="add"
android:textSize="20sp"
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" />
<TextView
android:id="@+id/tv_num"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.35000002" />
</androidx.constraintlayout.widget.ConstraintLayout>
(3)Activity Code
- ViewModelNoUseActivity.java
java
package com.my.viewmodel;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import com.my.viewmodel.viewmodel.MyViewModel;
public class ViewModelActivity extends AppCompatActivity {
private TextView tvNum;
private MyViewModel myViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_model);
tvNum = findViewById(R.id.tv_num);
myViewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyViewModel.class);
tvNum.setText(String.valueOf(myViewModel.num));
}
public void add(View view) {
tvNum.setText(String.valueOf(++myViewModel.num));
}
}
二、LiveData
1、基本介绍
- LiveData 是 Android Jetpack 架构组件中的一个类,它用于在数据发生变化时通知观察者(通常是 UI 组件),LiveData 是一个可观察的数据持有者,它与生命周期感知组件(例如,Activity、Fragment)紧密集成,这意味着它只会在这些组件处于活跃状态时更新观察者,它有如下特点
-
生命周期感知:LiveData 只在有活跃的观察者时才会分发更新,这有助于避免在组件(例如,Activity、Fragment)不再可见时发生不必要的更新
-
粘性事件:LiveData 可以选择性地保留最后一个值,并在新的观察者开始观察时立即发送该值(粘性事件的行为),这对于如登录状态、配置变化等需要即时知道当前状态的情况很有用
-
线程安全:可以在任何线程上修改 LiveData 的值,而观察者会在主线程上接收到这些更新,从而避免了直接操作 UI 组件的线程安全问题
-
数据一致性:LiveData 保证观察者会收到最新的数据,即使它们是在数据变化之后开始观察的
2、LiveData 方法
方法 | 说明 |
---|---|
setValue | 用于设置 LiveData 对象的新值,当调用此方法时,所有活动的观察者都会收到这个新值 如果 LiveData 对象当前没有活动的观察者,setValue 方法调用不会做任何事情 但是,一旦有观察者,它将立即收到最近设置的值 注:setValue 方法应该在主线程上调用,因为观察者会在主线程上接收更新 |
getValue | 用于获取 LiveData 对象当前持有的值,如果没有设置值,则返回 null |
postValue | 另一个用于设置 LiveData 值的方法,但它可以在任何线程上调用 从非主线程更新 LiveData 时,应该使用 postValue 方法而不是 setValue 方法 postValue 方法会将更新操作安排到主线程上执行,从而确保 UI 的更新是在主线程上进行的 |
3、演示
(1)ViewModel
- MyLiveData.java
java
package com.my.livedata.viewmodel;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
public class MyLiveData extends ViewModel {
private MutableLiveData<Integer> currentSecond;
public MutableLiveData<Integer> getCurrentSecond() {
if (currentSecond == null) {
currentSecond = new MutableLiveData<>();
currentSecond.setValue(0);
}
return currentSecond;
}
}
- 上述代码使用了 MutableLiveData 来存储和分发一个整数值,它允许修改存储的值并通知观察者
(2)Activity Layout
- activity_live_data.xml
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=".LiveDataActivity">
<TextView
android:id="@+id/tv_current_second"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
(3)Activity Code
- LiveDataActivity.java
java
package com.my.livedata;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import android.os.Bundle;
import android.widget.TextView;
import com.my.livedata.viewmodel.MyLiveData;
import java.util.Timer;
import java.util.TimerTask;
public class LiveDataActivity extends AppCompatActivity {
private TextView tvCurrentSecond;
private MyLiveData myLiveData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_live_data);
tvCurrentSecond = findViewById(R.id.tv_current_second);
myLiveData = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyLiveData.class);
myLiveData.getCurrentSecond().observe(this, new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
tvCurrentSecond.setText(String.valueOf(integer));
}
});
startTimer();
}
private void startTimer() {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
myLiveData.getCurrentSecond().postValue(myLiveData.getCurrentSecond().getValue() + 1);
}
}, 1000, 1000);
}
}
-
observe 方法用来观察 LiveDataViewModel 中的 currentSecond,当 currentSecond 的值改变时,onChanged 方法会被调用
-
postValue 方法用来更新 LiveDataViewModel 中的 currentSecond 的值
4、LiveData 优化
(1)ViewModel
- MyAnotherLiveData.java
java
package com.my.livedata.viewmodel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
public class MyAnotherLiveData extends ViewModel {
private MutableLiveData<Integer> currentSecond = new MutableLiveData<>(0);
public LiveData<Integer> getCurrentSecond() {
return currentSecond;
}
public Integer getValue() {
return currentSecond.getValue();
}
public void setValue(Integer integer) {
currentSecond.setValue(integer);
}
public void postValue(Integer integer) {
currentSecond.postValue(integer);
}
}
(2)Activity Layout
- activity_another_live_data.xml
java
<?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=".AnotherLiveDataActivity">
<TextView
android:id="@+id/tv_current_second"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
(3)Activity Code
- AnotherLiveDataActivity.java
java
package com.my.livedata;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;
import android.os.Bundle;
import android.widget.TextView;
import com.my.livedata.viewmodel.MyAnotherLiveData;
import java.util.Timer;
import java.util.TimerTask;
public class AnotherLiveDataActivity extends AppCompatActivity {
private TextView tvCurrentSecond;
private MyAnotherLiveData myAnotherLiveData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_another_live_data);
tvCurrentSecond = findViewById(R.id.tv_current_second);
myAnotherLiveData = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyAnotherLiveData.class);
myAnotherLiveData.getCurrentSecond().observe(this, (integer) -> {
tvCurrentSecond.setText(String.valueOf(integer));
});
startTimer();
}
private void startTimer() {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
myAnotherLiveData.postValue(myAnotherLiveData.getValue() + 1);
}
}, 1000, 1000);
}
}
5、Fragment 通信
(1)ViewModel
- SeekBarViewModel.java
java
package com.my.jetpackdemo.viewmodel;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
public class SeekBarViewModel extends ViewModel {
private MutableLiveData<Integer> process;
public MutableLiveData<Integer> getProcess() {
if (process == null) {
process = new MutableLiveData<>();
process.setValue(0);
}
return process;
}
}
(2)Fragment
- fragment_first.xml
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=".fragment.FirstFragment">
<SeekBar
android:id="@+id/seek_bar_first"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:max="100"
android:min="0"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
- FirstFragment.java
java
package com.my.livedata.fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.SeekBar;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import com.my.livedata.R;
import com.my.livedata.viewmodel.SeekBarViewModel;
public class FirstFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_first, null, false);
SeekBar seekBarFirst = root.findViewById(R.id.seek_bar_first);
SeekBarViewModel seekBarViewModel = new ViewModelProvider(getActivity(),
new ViewModelProvider.AndroidViewModelFactory(getActivity().getApplication())).get(SeekBarViewModel.class);
seekBarViewModel.getProcess().observe(getActivity(), new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
seekBarFirst.setProgress(integer);
}
});
seekBarFirst.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
seekBarViewModel.setValue(progress);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
return root;
}
}
- fragment_second.xml
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=".fragment.SecondFragment">
<SeekBar
android:id="@+id/seek_bar_second"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:max="100"
android:min="0"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
- SecondFragment.java
java
package com.my.livedata.fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.SeekBar;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import com.my.livedata.R;
import com.my.livedata.viewmodel.SeekBarViewModel;
public class SecondFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_second, null, false);
SeekBar seekBarSecond = root.findViewById(R.id.seek_bar_second);
SeekBarViewModel seekBarViewModel = new ViewModelProvider(getActivity(),
new ViewModelProvider.AndroidViewModelFactory(getActivity().getApplication())).get(SeekBarViewModel.class);
seekBarViewModel.getProcess().observe(getActivity(), new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
seekBarSecond.setProgress(integer);
}
});
seekBarSecond.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
seekBarViewModel.setValue(progress);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
return root;
}
}
(3)Activity Layout
- activity_live_data_fragment.xml
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=".LiveDataFragmentActivity">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/gl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_end="365dp" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fcv1"
android:name="com.my.livedata.fragment.FirstFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/gl"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fcv2"
android:name="com.my.livedata.fragment.SecondFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/gl" />
</androidx.constraintlayout.widget.ConstraintLayout>
(4)Activity Code
- LiveDataFragmentActivity.java
java
package com.my.livedata;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
public class LiveDataFragmentActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_live_data_fragment);
}
}
三、数据绑定
1、基本介绍
- Android DataBinding 是一种布局书写方式,它允许将数据直接绑定到布局的 XML 中,使得数据的变化能够直接反映到 View 上,这是 Android团队实现 MVVM 架构的一种方法,旨在减少 Android 开发中的大量模板代码(例如,findViewById()),增加代码及逻辑清晰度,提高开发效率和维护效率
2、演示
(1)Setting
-
模块级 build.gradle
android {
...defaultConfig { ... dataBinding { enabled = true } }
}
(2)Entity
- Idol.java
java
package com.my.jetpackdemo.entity;
public class Idol {
public String name;
public String star;
public Idol(String name, String star) {
this.name = name;
this.star = star;
}
}
(3)Activity Layout
- activity_data_binding.xml
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.my.databinding.entity.Idol" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".DataBindingActivity">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/gl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5" />
<TextView
android:id="@+id/tv_idol_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{ idol.name }"
android:textSize="20sp"
app:layout_constraintBottom_toTopOf="@+id/gl"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_idol_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{ idol.star }"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/gl" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
(4)Activity Code
- DataBindingActivity.java
java
package com.my.databinding;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import com.my.databinding.databinding.ActivityDataBindingBinding;
import com.my.databinding.entity.Idol;
public class DataBindingActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityDataBindingBinding activityDataBindingBinding = DataBindingUtil.setContentView(this, R.layout.activity_data_binding);
Idol idol = new Idol("张学友", "五星");
activityDataBindingBinding.setIdol(idol);
}
}
3、更多用法
(1)Entity
- User.java
java
package com.my.databinding.entity;
public class User {
public String name;
public int star;
public User(String name, int star) {
this.name = name;
this.star = star;
}
}
(2)Listener
- EventHandleListener.java
java
package com.my.databinding.listener;
import android.content.Context;
import android.view.View;
import android.widget.Toast;
public class EventHandleListener {
private Context context;
public EventHandleListener(Context context) {
this.context = context;
}
public void btnOnClick(View view) {
Toast.makeText(context, "点赞", Toast.LENGTH_SHORT).show();
}
}
(3)Util
- UserUtil.java
java
package com.my.jetpackdemo.util;
public class UserUtil {
public static String getStar(int star) {
switch (star) {
case 1:
return "一星";
case 2:
return "一星";
case 3:
return "三星";
case 4:
return "四星";
case 5:
return "五星";
}
return "零星";
}
}
(4)Activity Layout
- activity_user.xml
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="user"
type="com.my.databinding.entity.User" />
<variable
name="eventHandleListener"
type="com.my.databinding.listener.EventHandleListener" />
<import type="com.my.databinding.util.UserUtil" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".UserActivity">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_end="365dp" />
<TextView
android:id="@+id/tv_user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{ user.name }"
android:textSize="20sp"
app:layout_constraintBottom_toTopOf="@+id/guideline2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_user_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{ UserUtil.getStar(user.star) }"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline2" />
<Button
android:id="@+id/btn_like"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="点赞"
android:textSize="20sp"
android:onClick="@{ eventHandleListener.btnOnClick }"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintVertical_bias="0.508" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
(5)Activity Code
- UserActivity.java
java
package com.my.databinding;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import com.my.databinding.databinding.ActivityUserBinding;
import com.my.databinding.listener.EventHandleListener;
import com.my.databinding.entity.User;
public class UserActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user);
ActivityUserBinding activityUserBinding = DataBindingUtil.setContentView(this, R.layout.activity_user);
User user = new User("张三", 3);
activityUserBinding.setUser(user);
EventHandleListener eventHandleListener = new EventHandleListener(this);
activityUserBinding.setEventHandleListener(eventHandleListener);
}
}
4、二级页面数据绑定
(1)Entity
java
package com.my.databinding.entity;
public class User {
public String name;
public int star;
public User(String name, int star) {
this.name = name;
this.star = star;
}
}
(2)Util
- UserUtil.java
java
package com.my.jetpackdemo.util;
public class UserUtil {
public static String getStar(int star) {
switch (star) {
case 1:
return "一星";
case 2:
return "一星";
case 3:
return "三星";
case 4:
return "四星";
case 5:
return "五星";
}
return "零星";
}
}
(3)Activity Layout
- user_son1.xml
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="user"
type="com.my.databinding.entity.User" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/son_tv_user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{ user.name }"
android:textColor="#E91E63"
android:textSize="24sp"
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_son2.xml
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="user"
type="com.my.databinding.entity.User" />
<import type="com.my.databinding.util.UserUtil" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/son_tv_user_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{ UserUtil.getStar(user.star) }"
android:textColor="#E91E63"
android:textSize="20sp"
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>
- activity_user_father.java
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="user1"
type="com.my.databinding.entity.User" />
<variable
name="user2"
type="com.my.databinding.entity.User" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".UserFatherActivity">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/gl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5" />
<include
layout="@layout/user_son1"
app:user="@{ user1 }"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/gl"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<include
layout="@layout/user_son2"
app:user="@{ user2 }"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/gl" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
(4)Activity Code
- UserFatherActivity.java
java
package com.my.databinding;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import android.os.Bundle;
import com.my.databinding.databinding.ActivityUserFatherBinding;
import com.my.databinding.entity.User;
public class UserFatherActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityUserFatherBinding activityUserFatherBinding = DataBindingUtil.setContentView(this, R.layout.activity_user_father);
User user1 = new User("张三", 3);
User user2 = new User("李四", 4);
activityUserFatherBinding.setUser1(user1);
activityUserFatherBinding.setUser2(user2);
}
}
5、RecyclerView 数据绑定
(1)Entity
- Student.java
java
package com.my.databinding.entity;
public class Student {
public String name;
public int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getAgeStr() {
return age + "";
}
}
(2)Activity Layout
- recycler_view_binding_item.xml
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="student"
type="com.my.databinding.entity.Student" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#03A9F4"
android:paddingTop="25dp"
android:paddingBottom="25dp">
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{ student.name }"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_age"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="25dp"
android:text="@{ student.ageStr }"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_name" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
- activity_recycler_view_binding.xml
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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.my.databinding.RecyclerViewBindingActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:ignore="MissingConstraints"
tools:layout_editor_absoluteX="1dp"
tools:layout_editor_absoluteY="1dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
(3)Adapter
- RecyclerViewBindingAdapter.java
java
package com.my.databinding.adapter;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView;
import com.my.databinding.R;
import com.my.databinding.databinding.RecyclerViewBindingItemBinding;
import com.my.databinding.entity.Student;
import java.util.List;
public class RecyclerViewBindingAdapter extends RecyclerView.Adapter<RecyclerViewBindingAdapter.MyViewHolder> {
List<Student> studentList;
public RecyclerViewBindingAdapter(List<Student> studentList) {
this.studentList = studentList;
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
RecyclerViewBindingItemBinding recyclerViewBindingItemBinding =
DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()),
R.layout.recycler_view_binding_item, parent, false);
return new MyViewHolder(recyclerViewBindingItemBinding);
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
Student student = studentList.get(position);
holder.recyclerViewBindingItemBinding.setStudent(student);
}
@Override
public int getItemCount() {
return studentList.size();
}
static class MyViewHolder extends RecyclerView.ViewHolder {
private RecyclerViewBindingItemBinding recyclerViewBindingItemBinding;
public MyViewHolder(RecyclerViewBindingItemBinding recyclerViewBindingItemBinding) {
super(recyclerViewBindingItemBinding.getRoot());
this.recyclerViewBindingItemBinding = recyclerViewBindingItemBinding;
}
}
}
(4)Activity Code
- RecyclerViewBindingActivity.java
java
package com.my.databinding;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import com.my.databinding.adapter.RecyclerViewBindingAdapter;
import com.my.databinding.databinding.ActivityRecyclerViewBindingBinding;
import com.my.databinding.entity.Student;
import java.util.ArrayList;
import java.util.List;
public class RecyclerViewBindingActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityRecyclerViewBindingBinding activityRecyclerViewBindingBinding = DataBindingUtil.setContentView(this, R.layout.activity_recycler_view_binding);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
activityRecyclerViewBindingBinding.rv.setLayoutManager(linearLayoutManager);
List<Student> studentList = new ArrayList<>();
studentList.add(new Student("jack", 18));
studentList.add(new Student("smith", 19));
studentList.add(new Student("tom", 20));
RecyclerViewBindingAdapter recyclerViewBindingAdapter = new RecyclerViewBindingAdapter(studentList);
activityRecyclerViewBindingBinding.rv.setAdapter(recyclerViewBindingAdapter);
}
}
四、双向数据绑定
2、演示
(1)Entity
- Info.java
java
package com.my.databinding.entity;
public class Info {
public String username;
public String password;
public Info(String username, String password) {
this.username = username;
this.password = password;
}
}
(2)viewModel
- InfoViewModel.java
java
package com.my.databinding.viewmodel;
import android.util.Log;
import androidx.databinding.BaseObservable;
import androidx.databinding.Bindable;
import com.my.databinding.BR;
import com.my.databinding.entity.Info;
public class InfoViewModel extends BaseObservable {
private Info info;
public InfoViewModel(Info info) {
this.info = info;
}
@Bindable
public String getUsername() {
return info.username;
}
public void setUsername(String username) {
if (username != null && !username.equals(info.username)) {
info.username = username;
Log.d("my ==========", "setUsername: " + info.username);
notifyPropertyChanged(BR.username);
}
}
@Bindable
public String getPassword() {
return info.password;
}
public void setPassword(String password) {
if (password != null && !password.equals(info.password)) {
info.password = password;
Log.d("my ==========", "setPassword: " + info.password);
notifyPropertyChanged(BR.password);
}
}
}
(3)Activity Layout
- activity_data_binding_two.xml
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="infoViewModel"
type="com.my.databinding.viewmodel.InfoViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".DataBindingTwoActivity">
<EditText
android:id="@+id/et_username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName"
android:text="@={ infoViewModel.username }"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.1" />
<EditText
android:id="@+id/et_password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName"
android:text="@={ infoViewModel.password }"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/et_username"
app:layout_constraintVertical_bias="0.2" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
(4)Activity Code
- DataBindingTwoActivity.java
java
package com.my.databinding;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import com.my.databinding.databinding.ActivityDataBindingTwoBinding;
import com.my.databinding.viewmodel.InfoViewModel;
import com.my.databinding.entity.Info;
public class DataBindingTwoActivity extends AppCompatActivity {
private InfoViewModel infoViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityDataBindingTwoBinding activityDataBindingTwoBinding =
DataBindingUtil.setContentView(this, R.layout.activity_data_binding_two);
Info info = new Info("123", "456");
infoViewModel = new InfoViewModel(info);
activityDataBindingTwoBinding.setInfoViewModel(infoViewModel);
test();
}
public void test() {
new Thread(() -> {
try {
Thread.sleep(5000);
infoViewModel.setUsername("112233");
infoViewModel.setPassword("445566");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
3、另一种实现演示
(1)Entity
- Info.java
java
package com.my.databinding.entity;
public class Info {
public String username;
public String password;
public Info(String username, String password) {
this.username = username;
this.password = password;
}
}
(2)viewModel
java
package com.my.databinding.viewmodel;
import android.util.Log;
import androidx.databinding.ObservableField;
import com.my.databinding.entity.Info;
public class AnotherInfoViewModel {
public ObservableField<String> username;
public ObservableField<String> password;
public AnotherInfoViewModel(Info info) {
username = new ObservableField<>(info.username);
password = new ObservableField<>(info.password);
}
public String getUsername_() {
return username.get();
}
public void setUsername_(String username) {
Log.d("my ==========", "setUsername: " + username);
this.username.set(username);
}
public String getPassword_() {
return password.get();
}
public void setPassword_(String password) {
Log.d("my ==========", "setPassword: " + password);
this.password.set(password);
}
}
(3)Activity Layout
- activity_data_binding_two_another.xml
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="anotherInfoViewModel"
type="com.my.databinding.viewmodel.AnotherInfoViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.my.databinding.DataBindingTwoAnotherActivity">
<EditText
android:id="@+id/et_username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName"
android:text="@={ anotherInfoViewModel.username }"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.1" />
<EditText
android:id="@+id/et_password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName"
android:text="@={ anotherInfoViewModel.password }"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/et_username"
app:layout_constraintVertical_bias="0.2" />
<Button
android:id="@+id/btn_change"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Change"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
(4)Activity Code
- DataBindingTwoAnotherActivity.java
java
package com.my.databinding;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import android.os.Bundle;
import com.my.databinding.databinding.ActivityDataBindingTwoAnotherBinding;
import com.my.databinding.entity.Info;
import com.my.databinding.viewmodel.AnotherInfoViewModel;
public class DataBindingTwoAnotherActivity extends AppCompatActivity {
private AnotherInfoViewModel anotherInfoViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityDataBindingTwoAnotherBinding activityDataBindingTwoAnotherBinding = DataBindingUtil.setContentView(this, R.layout.activity_data_binding_two_another);
Info info = new Info("123", "456");
anotherInfoViewModel = new AnotherInfoViewModel(info);
activityDataBindingTwoAnotherBinding.setAnotherInfoViewModel(anotherInfoViewModel);
findViewById(R.id.btn_change).setOnClickListener((v) -> {
anotherInfoViewModel.setUsername_("112233");
anotherInfoViewModel.setPassword_("445566");
});
test();
}
public void test() {
new Thread(() -> {
try {
Thread.sleep(5000);
anotherInfoViewModel.setUsername_("112233");
anotherInfoViewModel.setPassword_("445566");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}