设计模式
在软件开发中,存储在内存中的数据结构我们叫数据的模型,显示在界面中的我们叫视图,开发过程中,更改模型中的值后,要对应修改视图中的数值,否则就可能存在不一致。但是当我们把数据显示到视图中的时候,我们显然是希望这个数据能和内存中的数据模型保持一致,然而这个操作是繁琐无趣的,因为每当我更改了模型,都要手动刷新视图,因此,有些天才就设计了一系列可观测对象,这些对象在视图中与显示控件进行绑定,同时在内存中与数据模型绑定,当可观测对象的值被改变时,便自动触发界面更新,以此来保证数据显示的一致性的同时,也大大减少了开发的工作量。这种模式叫Model-View-ViewModel (MVVM) 设计模式。
下面的示例中,是Android开发中配置MVVM的过程,需要注意配置文件中android sdk版本,本文采用的是gradle构造脚本。不适用于其他android构造脚本。
Model-View-ViewModel (MVVM) 设计模式
构造文件配置
在 module 的 build. gradle 文件配置:
plugins {
id 'com.android.application'
}
android {
compileSdk 32
defaultConfig {
applicationId "com.v5lab.fishrobot"
minSdk 24
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
// 默认是 “ cppFlags "" ”
// 如果要修改 Customize C++ Support 部分,可在这里加入
cppFlags "-frtti -fexceptions"
}
}
}
sourceSets {
main {
manifest.srcFile 'src/main/AndroidManifest.xml'
java.srcDirs = ['src/main/java']
resources.srcDirs = ['src/main/java']
res.srcDirs = ['src/main/res']
assets.srcDirs = ['src/main/assets']
jniLibs.srcDirs = ['src/main/cpp/libs']
}
}
buildFeatures {
dataBinding = true
viewBinding true
}
externalNativeBuild {
// Encapsulates your CMake build configurations.
cmake {
// Provides a relative path to your CMake build script.
path "src/main/cpp/CMakeLists.txt"
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildToolsVersion '30.0.3'
ndkVersion '21.1.6352462'
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
// lifecycle是关键组件
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'io.reactivex.rxjava2:rxjava:2.2.21'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
}
构造 ViewModel 类
public class RCViewModel extends ViewModel {
public ObservableField<String> ip = new ObservableField<>("202.38.214.30");
public ObservableField<Integer> port = new ObservableField<>(5000);
public ObservableField<Double> vx = new ObservableField<>(0.0);
public ObservableField<Double> vy = new ObservableField<>(0.0);
public ObservableField<Double> vz = new ObservableField<>(0.0);
public ObservableField<Double> rx = new ObservableField<>(0.0);
public ObservableField<Double> ry = new ObservableField<>(0.0);
public ObservableField<Double> rz = new ObservableField<>(0.0);
public ObservableField<Boolean> grasp = new ObservableField<>(false);
public MutableLiveData<Bitmap> bitmap = new MutableLiveData<>();
public MutableLiveData<Boolean> isConnected = new MutableLiveData<>(false);
}
其中 ObservableField 类型用于展示,ObservableField 类型用于状态监听响应,一般不用于显示
// ObservableField 通过var.set(data)函数,来修改数据
rcViewModel.vx.set(5);
// ObservableField通过observe函数进行监听,当数据发生改变时,调用接口函数,并将改变后的数据作为参数传入
rcViewModel.isConnected.observe(this, aBoolean -> {
if (aBoolean){
activityRemoteControllerBinding.btConnect.setText("连接成功");
}else{
activityRemoteControllerBinding.btConnect.setText("连接断开");
}
});
布局文件修改
添加 layout 标签
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="rcvm"
type="com.v5lab.fishrobot.remotecontroller.RCViewModel" />
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 此处添加其他组件 -->
<TextView
android:id="@+id/x_Text"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text='@{String.format("%.4f", rcvm.vx)}'/>
</RelativeLayout>
</layout>
与 Activity 进行绑定
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// 设置view model的绑定界面
activityRemoteControllerBinding = DataBindingUtil.setContentView(this, R.layout.activity_remote_controller);
// 实例化创建的ViewModel类
rcViewModel = new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).get(RCViewModel.class);
// 将ViewModel类绑定到activity中
activityRemoteControllerBinding.setRcvm(rcViewModel);
// 设置绑定行为的生命周期和当前活动一致
activityRemoteControllerBinding.setLifecycleOwner(this);
}
其中 activityRemoteControllerBinding 是自动生成的,可以使用该对象,对界面组件的操作。
例如对按钮组件设置事件监听函数
activityRemoteControllerBinding.rvTranslation.setOnShakeListener(new RockerView.OnShakeListener() {
@Override
public void onStart() {
}
@Override
public void getData(double angle, double distance) {
double rad = angle/180*Math.PI;
rcViewModel.vx.set(distance * Math.cos(rad) / move_max_level);
rcViewModel.vy.set(distance * Math.sin(rad) / move_max_level);
}
@Override
public void onFinish() {
}
});