Android设计模式

Android设计模式

设计模式

在软件开发中,存储在内存中的数据结构我们叫数据的模型,显示在界面中的我们叫视图,开发过程中,更改模型中的值后,要对应修改视图中的数值,否则就可能存在不一致。但是当我们把数据显示到视图中的时候,我们显然是希望这个数据能和内存中的数据模型保持一致,然而这个操作是繁琐无趣的,因为每当我更改了模型,都要手动刷新视图,因此,有些天才就设计了一系列可观测对象,这些对象在视图中与显示控件进行绑定,同时在内存中与数据模型绑定,当可观测对象的值被改变时,便自动触发界面更新,以此来保证数据显示的一致性的同时,也大大减少了开发的工作量。这种模式叫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() {

            }
        });
LICENSED UNDER CC BY-NC-SA 4.0