MVVM在Android中的初学之路
MVVM在Android中的初学之路
上篇写了MVP在Android中的初学之路 https://blog.csdn.net/Ae_fring/article/details/85158579。本篇继续架构之路MVVM,记录下初学的笔记。
MVVM的模型图:
当然这里也贴上盗来的MVC和MVP的模型图:(个人感觉比较清晰)
通过图可以了解最初的MVC的结构,由于Android中纯粹作为View的XML视图功能太弱,我们大量处理View的逻辑只能写在Activity中,这样Activity就充当了View和Controller两个角色,直接导致Activity中的代码量增多。相信大多数Android开发者都遇到过一个Acitivty数以千行的代码情况吧。
MVVM优点:
1.可重用性
可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。 布局里可以进行一个视图逻辑,并且Model发生变化,View也随着发生变化。
2.低耦合
以前Activity、Fragment中需要把数据填充到View,还要进行一些视图逻辑。现在这些都可在布局中完成(具体代码请看后面) 甚至都不需要再Activity、Fragment去findViewById。这时候Activity、Fragment只需要做好的逻辑处理就可以了。
MVVM的三大角色:
View: 对应于Activity和XML,负责View的绘制以及与用户交互。
Model: 实体模型。
ViewModel: 负责完成View与Model间的交互,负责业务逻辑。
通过图我们可以看到MVVM采用一种新的方式Data Binding来作为View和Model之间的绑定关系,增强XML视图功能 减少我们对控件的findViewbyId(下面会分析)
既然知道MVVM是一种架构模式,而DataBinding是一个实现数据和UI绑定的框架,是构建MVVM模式的一个工具。下面记录下代码实现部分:
app的build.gradle:
apply plugin: 'com.android.application'android { compileSdkVersion 26 defaultConfig { applicationId "com.ken.mvvmdemo" minSdkVersion 14 targetSdkVersion 26 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } dataBinding { enabled true }}dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'com.android.support:appcompat-v7:26.1.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' implementation 'it.sephiroth.android.library.picasso:picasso:2.5.2.4b' implementation 'com.squareup.okhttp3:okhttp:3.6.0'}
最主要的 activity_main:
<?xml version="1.0" encoding="utf-8"?>
User类
public class User extends BaseObservable { private String name; private String password; private String headimg; public User(String name, String password, String img) { this.name = name; this.password = password; this.headimg = img; } @BindingAdapter("bind:headimg") public static void getHeader(ImageView view, String url) { Picasso.with(view.getContext()).load(url).into(view); } @Bindable public String getName() { return name; } public void setName(String name) { notifyPropertyChanged(BR.name); this.name = name; } @Bindable public String getPassword() { return password; } public void setPassword(String password) { notifyPropertyChanged(BR.password); this.password = password; } public String getHeadimg() { return headimg; } public void setHeadimg(String headimg) { this.headimg = headimg; }}
可以看到根布局不在是我们以前常见的五大布局中的其中之一 而是layout 接着包裹这是DataBing的表达式写法
name="user" type="com.ken.mvvmdemo.User" /> name:引用名 type:对应需要使用到的类包名 文字显示使用@{user.name}方式: android:text="@{user.name}" 图片显示使用 app:headimg="@{user.headimg}" 后面分析源码 MainActivity代码: 现在结合MainActivity的代码分析下:首先变化的是setContentView 源码 通过源码我们了解到其实DataBind底层还是走到了之前的setContentView 不过加入了一个ViewDataBinding进来,其次返回了一个ActivityMainBinding。 由于DataBinding是编译时的工具。看到最终要执行的文件分析得出ViewDataBinding 实现了一个监听对layout的每一个控件实现Tag标识 在getBinding()后getTag()每一个控件 ActivityMainBindingImpl ViewDataBinding 最终在UI线程执行Runable----->executePendingBindings();通过源码可以看到最终让子类去实现了一个抽象的方法,所以这就解释了为什么在User类中实现加载图片为何这样写成static了。 下面记录下在ListView中使用方法 这里就写下Adapter的方法,Activity还是和最开始的写法一样,主要是适配器的getView()方法,还是以ViewDataBinding为核心 最终还是通过一系列的调用去拿到数据和前面的分析差不多的。 先记录到此附上下载源码:https://github.com/eternityzqf/MvvmDemo 谢谢! public class MainActivity extends AppCompatActivity { Handler handler = new Handler(); UserField userField = new UserField(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //setContentView(R.layout.activity_main); ActivityMainBinding mainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main); final User user = new User("张三", "123456", "http://pic6.huitu.com/res/20130116/84481_20130116142820494200_1.jpg"); mainBinding.setUser(user); mainBinding.setField(userField); handler.postDelayed(new Runnable() { @Override public void run() {// user.setName("李四");// user.setPassword("123"); userField.name.set("李四"); userField.password.set("123"); } }, 2000); }}
ActivityMainBinding mainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
/** * Set the Activity's content view to the given layout and return the associated binding. * The given layout resource must not be a merge layout. * * @param
private ActivityMainBindingImpl(android.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) { super(bindingComponent, root, 3 ); this.mboundView0 = (android.widget.LinearLayout) bindings[0]; this.mboundView0.setTag(null); this.mboundView1 = (android.widget.ImageView) bindings[1]; this.mboundView1.setTag(null); this.mboundView2 = (android.widget.TextView) bindings[2]; this.mboundView2.setTag(null); this.mboundView3 = (android.widget.TextView) bindings[3]; this.mboundView3.setTag(null); setRootTag(root); // listeners invalidateAll();}@Overridepublic void invalidateAll() { synchronized(this) { mDirtyFlags = 0x10L; } requestRebind();}
/** * @hide */protected void requestRebind() { if (mContainingBinding != null) { mContainingBinding.requestRebind(); } else { synchronized (this) { if (mPendingRebind) { return; } mPendingRebind = true; } if (mLifecycleOwner != null) { Lifecycle.State state = mLifecycleOwner.getLifecycle().getCurrentState(); if (!state.isAtLeast(Lifecycle.State.STARTED)) { return; // wait until lifecycle owner is started } } if (USE_CHOREOGRAPHER) { mChoreographer.postFrameCallback(mFrameCallback); } else { mUIThreadHandler.post(mRebindRunnable); } }}
public class ComonAdapter
更多相关文章
随机推荐