Android MVVM

关于Android MvvM的一些体会 关于Android MvvM的一些体会 前言 由于我司项目较老有很多历史包袱代码结构也比较混乱,需求复杂的页面动辄activity中1000多行,看着很是头疼,于是趁着加班提前做完需求余下的时间学习了mvvm对项目部分功能进行了改造,目前已经使用3个版本了,本篇博文分享下我使用的感受。 准备 这里先说说关于mvvm的几个问题(如有不对请轻喷 (╹▽╹)) 首先说说我为啥选择mvvm而不... 阅读详情

原文地址
http://tech.vg.no/2015/07/17/android-databinding-goodbye-presenter-hello-viewmodel/

1、Android DataBinding:再见MVP,你好MVVM

当我们谈到android应用程序的架构模式时,MVP一直是占主流的地位。就像 Ted Mosby, Nucleus and Mortar 框架都是用Presenters 架构,使我们的app结构变得非常清晰,还帮助我们解决一个凑名昭着的问题:android设备的旋转和状态持久。当然这不是直接的MVP概念,但事实上这一架构模型,使我们的代码解耦。

数据绑定,是在Google I/O 2015提出并且Android M预览版是的支持库。声称改变一切。根据Wikipedia 是介绍的MVP,Presenter 有以下作用:

Presenter 根据模型和视图。它从存储库(模型)中检索数据,并将其格式化为在视图中显示的数据。

问题是,数据绑定框架将代替Presenter 的主要职责(作用在模型和视图),而其余的功能则是留给增强模型–ViewModel(检索数据库和并格式处理)。视图模型是ViewModel是一个标准的java类,其唯一的责任是代表在一个View中的数据。它可以合并来自多个源(模型)的数据,并展示数据。我以前有一篇在ViewModel的短文章,它主要讲述了Data Model 和Transport Model的不同。

构建我们最终的MVVM–模型视图ViewModel,MVVM概念最初是由微软在2005年(不要让它吓到你了)。下面我说明MVP到MVVM的变化,引用Hanne Dorfmann介绍他的 Ted Mosby framework中使用的图片
这里写图片描述

这里写图片描述

所有的数据的绑定和更新都是通过数据绑定框架(Data Binding Framework)来完成的。Observablefield类允许视图对模型的变化做出反应,而XML引用字段改变时也会改变ViewModel 。当作用在视图上时,你也可以通过程序订阅变化,比如一个CheckBox点击时,让TextView时禁用。如果有一个标准的java类能够表示View的状态,这样的好处显而易见。并且我们可以很容易的单元测试。

注意在MVP上图有一个方法调用 Presenter.loadUsers()的方法。在MVVM模式中这所有的方法都定义在 ViewMode。从维基百科文章:

The view model is an abstraction of the view that exposes public properties and commands

在MVP模式很中,你的模型很可能是“简单”的类,它只保存数据。不要害怕把业务逻辑放在你的模型或视图模型中。这是面向对象编程的一个核心原则。回到 Presenter.loadUsers()这个方法,我们没有在ViewModel中调用这个方法,视图是通过绑定在xml文件中。如果我们不把数据绑定,我们仍然必须使用以前的android语法:onclick语法,或手动添加监听代码。

2、处理系统调用

仍然有一系列的工作在View中去做,比如初始化系统的回调,打开dialog或者涉及Context的对象。不要这些代码放到ViewModel。如果你把像这种Context放入到ViewModel中,这是错误的。

我还没有找到最好的解决办法,但有一些可以尝试。一种方式是在View的ViewModel保持一个presenter 一个接口的引用。这样你就不会降低可测试性。但是没有一个单独的presenter 的类在我们程序中,我坚持认为,只要保持它的简单接口的具体实现。另一种方式就像event bus或者Square Otto初始化方式 ShowToastMessage(“hello world”)(具体参见eventbus 或者otto的使用)。这将产生一个更大的分离View和ViewModel –但那是好事情吗?

3、我们是否需要框架呢?

因此,Data Binding框架代替其他的框架吗?只有部分。我希望看到的是这些框架进化为MVVM风格框架,使我们可以利用最好的数据绑定同时依赖第三方的框架到最小,并保持框架小而简单。而Presenter 的时代就要结束了,这个Presenter仅仅做一些重要的工作比如生命周期管理和视图状态(ViewModel)持久性。不幸的是,这并没有改变。

我最近了解到Android ViewModel框架,这实际上可能是一个很好的适合MVVM和Android的数据绑定。

4、总结

当我第一听说Android M 所有工作是为了改进sdk和关注开发者们。我是多么的兴奋。当接受到数据绑定的时候,我是如此的吃惊,我已经和其他平台上的数据绑定工作多年:WinForms,WPF,Silverlight和Windows手机。我知道这将有助于我们写更清洁的架构和更少的耦合代码。这个框架与我们一起工作,而不是反对我们,现在,它终于要来了。

但是,它仍然有一些缺点。事实上你定义的xml文件,它没有编译,也就是没办法进行单元测试。这样你会了现许多错误在运行期间,而不是在编译期间。不幸的是,如果你经常忘记绑定view呢?
这就是为什么我希望谷歌能够让Android Studio支持数据绑定到最大的语法和编译期间检查的XML,自动完成。支持重命名在xml字段中。从我用的Android Studdio1.3测试版-中思考这样的问题。有些东西是支持的,很也有许多不支持,但是我看到了很大的希望。

5、Code example

下面分别使用mvp和mvvm的代码示例
这里写图片描述
MVP – VIEW – XML

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:paddingLeft="@dimen/activity_horizontal_margin"
                android:paddingRight="@dimen/activity_horizontal_margin"
                android:paddingTop="@dimen/activity_vertical_margin"
                android:paddingBottom="@dimen/activity_vertical_margin"
                tools:context=".MainActivityFragment">

    <TextView
        android:text="..."
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentEnd="true"
        android:id="@+id/loggedInUserCount"/>

    <TextView
        android:text="# logged in users:"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentEnd="false"
        android:layout_toLeftOf="@+id/loggedInUserCount"/>

    <RadioGroup
        android:layout_marginTop="40dp"
        android:id="@+id/existingOrNewUser"
        android:gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:orientation="horizontal">

        <RadioButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Returning user"
            android:id="@+id/returningUserRb"/>

        <RadioButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="New user"
            android:id="@+id/newUserRb"
            />

    </RadioGroup>

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/username_block"
        android:layout_below="@+id/existingOrNewUser">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:text="Username:"
            android:id="@+id/textView"
            android:minWidth="100dp"/>

        <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/username"
            android:minWidth="200dp"/>
    </LinearLayout>

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="false"
        android:id="@+id/password_block"
        android:layout_below="@+id/username_block">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:text="Password:"
            android:minWidth="100dp"/>

        <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:inputType="textPassword"
            android:ems="10"
            android:id="@+id/password"/>

    </LinearLayout>

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/password_block"
        android:id="@+id/email_block">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:text="Email:"
            android:minWidth="100dp"/>

        <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:inputType="textEmailAddress"
            android:ems="10"
            android:id="@+id/email"/>
    </LinearLayout>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Log in"
        android:id="@+id/loginOrCreateButton"
        android:layout_below="@+id/email_block"
        android:layout_centerHorizontal="true"/>
</RelativeLayout>

MVP – VIEW – JAVA

package com.nilzor.presenterexample;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.RadioButton;
import android.widget.TextView;
import android.widget.Toast;
import com.hannesdorfmann.mosby.mvp.MvpFragment;
import com.hannesdorfmann.mosby.mvp.MvpView;
import butterknife.InjectView;
import butterknife.OnClick;

public class MainActivityFragment extends MvpFragment implements MvpView {
    @InjectView(R.id.username)
    TextView mUsername;

    @InjectView(R.id.password)
    TextView mPassword;

    @InjectView(R.id.newUserRb)
    RadioButton mNewUserRb;

    @InjectView(R.id.returningUserRb)
    RadioButton mReturningUserRb;

    @InjectView(R.id.loginOrCreateButton)
    Button mLoginOrCreateButton;

    @InjectView(R.id.email_block)
    ViewGroup mEmailBlock;

    @InjectView(R.id.loggedInUserCount)
    TextView mLoggedInUserCount;

    public MainActivityFragment() {
    }

    @Override
    public MainPresenter createPresenter() {
        return new MainPresenter();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_main, container, false);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        attachEventListeners();
    }

    private void attachEventListeners() {
        mNewUserRb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                updateDependentViews();
            }
        });
        mReturningUserRb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                updateDependentViews();
            }
        });
    }

    /** Prepares the initial state of the view upon startup */
    public void setInitialState() {
        mReturningUserRb.setChecked(true);
        updateDependentViews();
    }

    /** Shows/hides email field and sets correct text of login button depending on state of radio buttons */
    public void updateDependentViews() {
        if (mReturningUserRb.isChecked()) {
            mEmailBlock.setVisibility(View.GONE);
            mLoginOrCreateButton.setText(R.string.log_in);
        }
        else {
            mEmailBlock.setVisibility(View.VISIBLE);
            mLoginOrCreateButton.setText(R.string.create_user);
        }
    }

    public void setNumberOfLoggedIn(int numberOfLoggedIn) {
        mLoggedInUserCount.setText(""  + numberOfLoggedIn);
    }

    @OnClick(R.id.loginOrCreateButton)
    public void loginOrCreate() {
        if (mNewUserRb.isChecked()) {
            Toast.makeText(getActivity(), "Please enter a valid email address", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(getActivity(), "Invalid username or password", Toast.LENGTH_SHORT).show();
        }
    }
}

MVP – PRESENTER

package com.nilzor.presenterexample;

import android.os.Handler;
import android.os.Message;
import com.hannesdorfmann.mosby.mvp.MvpPresenter;

public class MainPresenter implements MvpPresenter {
    MainModel mModel;
    private MainActivityFragment mView;

    public MainPresenter() {
        mModel = new MainModel();
    }

    @Override
    public void attachView(MainActivityFragment view) {
        mView = view;
        view.setInitialState();
        updateViewFromModel();
        ensureModelDataIsLoaded();
    }

    @Override
    public void detachView(boolean retainInstance) {
        mView = null;
    }

    private void ensureModelDataIsLoaded() {
        if (!mModel.isLoaded()) {
            mModel.loadAsync(new Handler.Callback() {
                @Override
                public boolean handleMessage(Message msg) {
                    updateViewFromModel();
                    return true;
                }
            });
        }
    }

    /** Notifies the views of the current value of "numberOfUsersLoggedIn", if any */
    private void updateViewFromModel() {
        if (mView != null && mModel.isLoaded()) {
            mView.setNumberOfLoggedIn(mModel.numberOfUsersLoggedIn);
        }
    }
}

MVP – MODEL

package com.nilzor.presenterexample;

import android.os.AsyncTask;
import android.os.Handler;
import java.util.Random;

public class MainModel {
    public Integer numberOfUsersLoggedIn;
    private boolean mIsLoaded;
    public boolean isLoaded() {
        return mIsLoaded;
    }

    public void loadAsync(final Handler.Callback onDoneCallback) {
        new AsyncTask() {
            @Override
            protected Void doInBackground(Void... params) {
                // Simulating some asynchronous task fetching data from a remote server
                try {Thread.sleep(2000);} catch (Exception ex) {};
                numberOfUsersLoggedIn = new Random().nextInt(1000);
                mIsLoaded = true;
                return null;
            }

            @Override
            protected void onPostExecute(Void aVoid) {
                onDoneCallback.handleMessage(null);
            }
        }.execute((Void) null);
    }
}

用mvvm代码如下
MVVM – VIEW – XML

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <data>
        <variable name="data" type="com.nilzor.presenterexample.MainModel"/>
    </data>
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:paddingBottom="@dimen/activity_vertical_margin"
        tools:context=".MainActivityFragment">

        <TextView
            android:text="@{data.numberOfUsersLoggedIn}"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentEnd="true"
            android:id="@+id/loggedInUserCount"/>

        <TextView
            android:text="# logged in users:"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentEnd="false"
            android:layout_toLeftOf="@+id/loggedInUserCount"/>

        <RadioGroup
            android:layout_marginTop="40dp"
            android:id="@+id/existingOrNewUser"
            android:gravity="center"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:orientation="horizontal">

            <RadioButton
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Returning user"
                android:checked="@{data.isExistingUserChecked}"
                android:id="@+id/returningUserRb"/>

            <RadioButton
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="New user"
                android:id="@+id/newUserRb"
                />

        </RadioGroup>

        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/username_block"
            android:layout_below="@+id/existingOrNewUser">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textAppearance="?android:attr/textAppearanceMedium"
                android:text="Username:"
                android:id="@+id/textView"
                android:minWidth="100dp"/>

            <EditText
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/username"
                android:minWidth="200dp"/>
        </LinearLayout>

        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentStart="false"
            android:id="@+id/password_block"
            android:layout_below="@+id/username_block">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textAppearance="?android:attr/textAppearanceMedium"
                android:text="Password:"
                android:minWidth="100dp"/>

            <EditText
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:inputType="textPassword"
                android:ems="10"
                android:id="@+id/password"/>

        </LinearLayout>

        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/password_block"
            android:id="@+id/email_block"
            android:visibility="@{data.emailBlockVisibility}">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textAppearance="?android:attr/textAppearanceMedium"
                android:text="Email:"
                android:minWidth="100dp"/>

            <EditText
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:inputType="textEmailAddress"
                android:ems="10"
                android:id="@+id/email"/>
        </LinearLayout>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{data.loginOrCreateButtonText}"
            android:id="@+id/loginOrCreateButton"
            android:layout_below="@+id/email_block"
            android:layout_centerHorizontal="true"/>
    </RelativeLayout>
</layout>

MVVM – VIEW – JAVA

package com.nilzor.presenterexample;

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.Toast;

import com.nilzor.presenterexample.databinding.FragmentMainBinding;

public class MainActivityFragment extends Fragment {
    private FragmentMainBinding mBinding;
    private MainModel mViewModel;

    public MainActivityFragment() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_main, container, false);
        mBinding = FragmentMainBinding.bind(view);
        mViewModel = new MainModel(this, getResources());
        mBinding.setData(mViewModel);
        attachButtonListener();
        return view;
    }

    private void attachButtonListener() {
        mBinding.loginOrCreateButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mViewModel.logInClicked();
            }
        });
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        ensureModelDataIsLodaded();
    }

    private void ensureModelDataIsLodaded() {
        if (!mViewModel.isLoaded()) {
            mViewModel.loadAsync();
        }
    }

    public void showShortToast(String text) {
        Toast.makeText(getActivity(), text, Toast.LENGTH_SHORT).show();
    }
}

MVVM – VIEWMODEL

package com.nilzor.presenterexample;

import android.content.res.Resources;
import android.databinding.ObservableField;
import android.os.AsyncTask;
import android.view.View;

import java.util.Random;

public class MainModel {
    public ObservableField numberOfUsersLoggedIn = new ObservableField();
    public ObservableField isExistingUserChecked = new ObservableField();
    public ObservableField emailBlockVisibility = new ObservableField();
    public ObservableField loginOrCreateButtonText = new ObservableField();
    private boolean mIsLoaded;
    private MainActivityFragment mView;
    private Resources mResources;

    public MainModel(MainActivityFragment view, Resources resources) {
        mView = view;
        mResources = resources; // You might want to abstract this for testability
        setInitialState();
        updateDependentViews();
        hookUpDependencies();
    }
    public boolean isLoaded() {
        return mIsLoaded;
    }

    private void setInitialState() {
        numberOfUsersLoggedIn.set("...");
        isExistingUserChecked.set(true);
    }

    private void hookUpDependencies() {
        isExistingUserChecked.addOnPropertyChangedCallback(new android.databinding.Observable.OnPropertyChangedCallback() {
            @Override
            public void onPropertyChanged(android.databinding.Observable sender, int propertyId) {
                updateDependentViews();
            }
        });
    }

    public void updateDependentViews() {
        if (isExistingUserChecked.get()) {
            emailBlockVisibility.set(View.GONE);
            loginOrCreateButtonText.set(mResources.getString(R.string.log_in));
        }
        else {
            emailBlockVisibility.set(View.VISIBLE);
            loginOrCreateButtonText.set(mResources.getString(R.string.create_user));
        }
    }

    public void loadAsync() {
        new AsyncTask() {
            @Override
            protected Void doInBackground(Void... params) {
                // Simulating some asynchronous task fetching data from a remote server
                try {Thread.sleep(2000);} catch (Exception ex) {};
                numberOfUsersLoggedIn.set("" + new Random().nextInt(1000));
                mIsLoaded = true;
                return null;
            }
        }.execute((Void) null);
    }

    public void logInClicked() {
        // Illustrating the need for calling back to the view though testable interfaces.
        if (isExistingUserChecked.get()) {
            mView.showShortToast("Invalid username or password");
        }
        else {
            mView.showShortToast("Please enter a valid email address");
        }
    }
}

更多连接
http://willowtreeapps.com/blog/mvvm-on-android-what-you-need-to-know/

安卓(Android)平台上的MVVM架构:关键知识点、优劣分析及实践示例 本文旨在探讨安卓平台上广泛应用的Model-View-ViewModel(MVVM)架构模式的核心概念、主要优点与潜在不足,并通过实际示例代码阐明其在实际项目中的应用方式。MVVM作为一款推动关注点分离和提高软件质量的架构方案,在安卓应用开发中起着至关重要的作用。 阅读详情

相关推荐

使用Kotlin构建更适合Android的MVVM应用程序,安卓已死

自定义view,Android开发的基本功。

2201_75604580的博客 678

android复选框 xml,MvvmCross复选框绑定到命令android xml

标准方法是在viewmodel中简单地绑定bool类型的属性,并在此属性的setter中执行逻辑.您的绑定将如下所示:local:MvxBind="Checked IsChecked"但是,如果您确实需要绑定到Command,您还可以绑定到Click事件:local:MvxBind="Checked IsChecked; Click YourCommand;"视图模型:private bool _...

weixin_39782500的博客 250

Android MVVM架构分析

前言 本文俩个任务: 1.对MVVM进行学习 2.总结梳理MVC-MVP-MVVM的演进过程 什么是MVVM MVP是对MVC的C的演化,MVVM是对MVP的P的演化。而Android领域的MVVM自身也进行了一次演化,即从2015年DataBinding推出开始,由传统的MVVM到2017谷歌推出了AAC标准架构。并在二者迭代的过程中,也出现了基于传统的DataBinding增强...

点滴积累 7748

Android技术框架篇(三):MVVM框架

MVVM框架的介绍

看,好自为之的博客!!! 1万+

LMS Virtual.Lab二次开发:声学仿真结果导出方法(VBScript、Python)

Siemens LMS Virtual.Lab 提供一套集成软件平台用以模拟机械系统的品质属性,如结构完整性、振动噪声、耐久性、系统动力学特性、驾驶的平顺性及操纵的稳定性等。

爱看书的小沐 6021

【邻接表存图模板】vector邻接表、数组邻接表、链式前向星

一、目录 一、目录 二、前言 三、邻接表“详解” (一)边的信息 (二)几种邻接表的实现方法“详解” 1.数组邻接表 代码 2.vector邻接表 代码 vector邻接表还有一种魔性写法: 3.链式前向星 代码 四、总结 二、前言 本来之前自己太弱了,一直只会用邻接矩阵,后来发现邻接表才是主流... 参考&特别鸣谢: https://www.cnblog...

十里寒塘路♣烟花一半醒 3113

Android MVVM详解

一、MVVM是什么? (1).Model:数据层,包含数据实体和对数据实体的操作 (2).View:界面层,对应于Activity,XML,View,负责数据显示以及用户交互。 (3).ViewModel:关联层,将Model和View进行绑定,Model或者View更改时,实时刷新对方。

Mubly的博客 9995

深入解析Gerber RS274X文件格式:从基础语法到实战应用

本文深入解析PCB制造的核心标准Gerber RS274X文件格式,详细介绍了其基础语法、坐标系统、功能码以及嵌入式光圈定义等关键特性。通过实战案例演示了如何解析和手写Gerber指令,并总结了常见错误与调试技巧,帮助PCB工程师掌握这一与制造工厂沟通的“通用语言”,确保设计准确无误地转化为实体电路板。

weixin_29252171的博客 94

Android架构组件中的MVVM

Model代表数据模型,负责数据的获取和持久化。在Android中,可以通过创建一个Java类或使用数据库来实现模型。// 构造函数、Getter和Setter方法等return age;MVVM模式在Android开发中的应用非常广泛,它通过将应用程序分解为Model、View和ViewModel三个主要部分,实现了代码的解耦合、提高了开发效率、易于维护和测试。在实际项目中,我们可以结合Android Jetpack中的组件和库来进一步增强MVVM模式的应用效果。

shiming8879的博客 4598

从SPICE到电磁场:非线性器件在CST场路协同仿真中的建模艺术

本文深入探讨了非线性器件在CST场路协同仿真中的建模技术,重点分析了SPICE模型与电磁场仿真的集成方法。通过对比线电流模型和面电流模型的优缺点,结合实际案例展示了二极管阵列在电磁散射调控中的应用,为工程师提供了优化仿真精度与效率的实用技巧。

pandas7gardener的博客 198

STM32F030F4P6实战:LL库+DMA驱动WS2812全流程(附避坑指南)

本文详细介绍了如何使用STM32F030F4P6的LL库和DMA驱动WS2812 LED灯带的全流程,包括硬件选型、CubeMX配置、LL库与DMA关键实现、数据格式转换及调试技巧。特别提供了避坑指南,帮助开发者避免常见错误,提升开发效率。

weixin_29194693的博客 484

Android 架构模式之 MVVM

如果有看过 MVP 架构,会感觉这两个是一样的,不用怀疑,就是一样的,还有 MVC 也是一样的,因为这些都是从 MVC 演变过来的,只是每次演变都是为了解决特定的问题,区别就是实现方式不一样了,MVVM 变成了基于数据驱动。

villen_t的博客 4889

小程序HTTPS证书配置踩坑记:为什么Android访问失败而iOS正常?

本文深入剖析了小程序开发中常见的跨平台网络问题:Android访问HTTPS接口失败而iOS正常。核心原因在于服务器(如Nginx)HTTPS证书链配置不完整,Android系统对此校验严格,而iOS相对宽松。文章提供了从问题诊断、使用在线工具检测到修复Nginx证书链配置的完整解决方案,并总结了HTTPS部署的最佳实践,帮助开发者从根本上避免此类“玄学”故障。

weixin_29207149的博客 394

安卓中的MVC、MVP、MVVM

安卓中的MVC、MVP、MVVM

似霰的博客 1024

S32的进阶之路->3,S32DS新建/删除/导入工程

右击工程名,点击Delect进行工程删除。选中Delect project contents on disk则会彻底删除工程,不选中Delect project contents on disk工程文件会保存在工作空间中。在Improt Projects中选择工程所在文件夹,选中所需要导入的工程,勾选Copy projects into workspace。选中目标示例工程,点击Finish,完成创建工程。导入已有工程,右击右击Project Explorer空白处,选择Import。

LC_8575的博客 2226

华为OD机试真题:明日之星选举 (Java/Py/C/ C++/JS/GO)

华为OD机试(机考)双机位C卷 【明日之星选举】,提供Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD机试 - 明日之星选举。

qq_45776114的博客 919

Android安卓进阶技巧之Kotlin结合Jetpack构建MVVM

Jetpack Jetpack 是一个由多个库组成的套件,可帮助开发者遵循最佳做法,减少样板代码并编写可在各种 Android 版本和设备中一致运行的代码,让开发者精力集中编写重要的代码。 Android Architecture Component (AAC)。 文末有福利!!! 官方推荐架构 请注意,每个组件仅依赖于其下一级的组件。例如,Activity 和 Fragment 仅依赖于视图模型。存储区是唯一依赖于其他多个类的类;在本例中,存储区依赖于持久性数据模型和远程后端数据源。 MVVM

m0_58479348的博客 1493

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Android mvvm框架之ViewModel,面试安卓开发自我介绍

这意味着无论是转屏还是系统字体变化等因配置变化产生的 Controller 重建都不会回收 ViewModel 中维护的数据,重建的 Controller 仍然可以从同一个 ViewModel 中通过获取数据恢复状态。 2 ViewModel 实现原理 2.1 ViewModel 类 如果大家去看一下 ViewModel 类的实现,会发现虽然它是一个 abstract 类,但是没有暴露任何外部可访问的方法,其预留的方法都是 package 访问权限的, 其预留了一些数据清理工作的功能,推测可能是系统保留用作

m0_64382743的博客 411

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Android MVVM架构 这一篇就够

AndroidMVVM架构Android MVVM架构,这一篇就够相关技术Activity模式的准备工作一些控件的绑定1.TextView2. 绑定点击事件3. 绑定其他控件,就拿ImageView举例(任何控件都可以如此绑定)4. 绑定列表等有适配器的控件,ListView为例Fragment模式的准备工作1.给MainActivity加上VM2.Fragment的vm我们还种方式绑定针对适配器实体类的绑定补充 Android MVVM架构,这一篇就够 这篇文章主要是分享我学习安卓前沿技术架构Jetpac

qq_25890433的博客 9396

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

安卓如何搭建Mvvm框架

安卓架构从我们最初熟悉的Mvc模式,发展到用Mvp,再到现在比较火的Mvvm架构。一路走来感觉架构都是浮云,最主要的还是个人对业务的理解深入,然后再进行代码的封装解耦,才能真正是代码逻辑简洁和维护成本减小。 下面我们就来对比一下这三种模式的区别和各自的特点。 MVC框架 MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的...

阿甘正传 1万+

Kotlin Android MVVM Starter 项目教程

Kotlin Android MVVM Starter 项目教程 1. 项目介绍 Kotlin Android MVVM Starter 是一个开源项目,旨在为开发者提供一个基于 Kotlin 语言的 Android 项目模板,该模板实现了 MVVM(Model-View-ViewModel)架构模式。项目包含了常用的库和工具,如 RxJava2、RxAndroid、RxKotlin、Retrof...

gitblog_01149的博客 712

《Android构建MVVM》系列(一) 之 MVVM架构快速入门

前言 本文属于《Android 构建MVVM》系列开篇,共六个篇章,详见目录树 该系列文章旨在为Android的开发者入门MVVM架构,掌握其基本开发模式 辅以讲解Android架构组件,使得更好的实现MVVM 目录树 《Android 构建MVVM》系列(一) 之 MVVM架构快速入门 前言 分层思想 什么MVC / MVP? MVVM是什么,与MVC / MVP有何区别? Andro...

qq_36791310的博客 2657

Android MVVM架构

MVVM是Model-View-ViewModel的简写,是M-V-VM三部分组成。它本质上就是MVC 的改进 版。MVVM 就是将其中的View 的状态和行为抽象化,其中ViewModel将视图 UI 和业务逻辑分 开,它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。2.MVVM采用双向数据绑定,view中数据变化将自动反映到viewmodel上,反之,model中数据变化 也将会自动展示在页面上。把Model和View关联起来的就是ViewModel。...

weixin_53431933的博客 1万+

Android安卓架构MVC、MVP、MVVM模式的概念与区别

MVC(Model-View-Controller)、MVP(Model-View-Presenter)、MVVM(Model-View-ViewModel)是三种常见的软件架构模式,它们的目的都是将应用程序的不同部分分离开来,以提高代码的可维护性、可扩展性和可测试性。在 Android 开发中,MVP(Model-View-Presenter)是一种常用的架构模式,它是基于MVC模式的改进,旨在进一步分离应用程序的各个组件,提高代码的可测试性和可维护性。同时,也使得代码更加清晰、简洁,降低了维护成本。

m0_74293254的博客 4237

android mvvm 官方例子,Android下的MVVM架构

写在文章开头:由于自己是一名学习Android开发的咸鱼玩家,如果理解不到位或者出错的地方,欢迎大家指出错误或者提出宝贵的意见;另外文章可能涉及到各个途径来源的资源,如有侵犯,立刻删除。概述1 MVC1.1 MVC含义M(Model):模型,这个模块很抽象,但是总的来说,就是提供数据源的模块,它可以是本地数据库数据,可以是网络请求数据,可以是一切数据源。V(View):视图,能够实现数据源有目的性...

weixin_26713059的博客 2186

安卓 框架模式——MVC、MVP、MVVM

安卓 框架模式——MVC、MVP、MVVM一、MVC1、MVC介绍2、MVC流程、实现方式3、MVC关系4、MVC的优点5、MVC的缺点二、MVP1、MVP介绍2、MVP流程、实现方式3、MVP关系4、MVP的优点5、MVP的缺点6、MVP和MVC的区别三、MVVM1、MVVM介绍2、MVVM流程、实现方式3、MVVM的优点4、MVVM的缺点5、MVP和MVVM的区别 一、MVC 1、MVC介绍 MVC全名是Model是模型(model) 、View视图(view)、 Controller控制器(contr

zxsenqiu的博客 388
上一篇: Android MVP
下一篇: Android MVVM ViewModel
android_xiaogang
博客等级 码龄12年 89粉丝 142原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值