Android MVC,MVP,MVVM模式入门——重构登陆注册功能
来源:互联网 发布:js选项卡自动切换 编辑:程序博客网 时间:2024/06/07 14:31
M:model,业务逻辑
V:view,对应布局文件
C:Controllor,对应Activity
项目框架:
代码部分:
layout文件(适用于MVC和MVP两个Demo):
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <EditText android:id="@+id/edit_username" android:layout_width="match_parent" android:layout_height="50dp" android:hint="username"/> <EditText android:id="@+id/edit_password" android:layout_width="match_parent" android:layout_height="50dp" android:inputType="textPassword" android:hint="password"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <Button android:id="@+id/login" android:layout_marginLeft="10dp" android:layout_width="120dp" android:layout_height="50dp" android:text="Login"/> <Button android:id="@+id/clear" android:layout_width="120dp" android:layout_height="50dp" android:layout_marginLeft="100dp" android:text="Clear"/> </LinearLayout> <ProgressBar android:id="@+id/login_progressbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:visibility="invisible" /></LinearLayout>
User类,定义成员对象:
package com.example.liang.userloginformvc.modle;/** * Created by liang on 2016/8/22. */public class User { private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; }}
OnLoginListener,j监听登陆状态:
package com.example.liang.userloginformvc.modle;/** * Created by liang on 2016/8/22. */public interface OnLoginListener { void loginSuccess(User user); void loginFailed();}
UserBiz,登陆逻辑:
package com.example.liang.userloginformvc.modle;/** * Created by liang on 2016/8/22. */public class UserBiz { public void login(final String username,final String password,final OnLoginListener loginListener){ new Thread(){ @Override public void run() { try{ Thread.sleep(2000); }catch(InterruptedException e){ e.printStackTrace(); } //模拟登陆 if("lqy".equals(username)&&"123".equals(password)){ User user=new User(); user.setUsername(username); user.setPassword(password); loginListener.loginSuccess(user); }else{ loginListener.loginFailed(); } } }.start(); }}
MVC模式总结:
首先从响应用户点击事件,到MainActivity,获取View中的数据再交给Model层处理,通过返回的数据,MainActivity再改变视图。
从MVC到MVP:由于View和Model之间的依赖还是太强,希望他们可以绝对独立的存在,慢慢的就演化出了MVP
二 MVP模式
M:业务逻辑与实体模型
V:负责View的绘制以及用户交互,对应Activity
P:负责完成View与Modle之间的交换
项目框架:
代码部分:
user:
package com.example.liang.userloginformvp.bean;/** * Created by liang on 2016/8/22. */public class User{ private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; }}
IUserBiz:
package com.example.liang.userloginformvp.biz;/** * Created by liang on 2016/8/22. */public interface IUserBiz{ public void login(String username,String password,OnLoginListener onLoginListener);}
OnLoginListener:
package com.example.liang.userloginformvp.biz;/** * Created by liang on 2016/8/22. */public interface IUserBiz{ public void login(String username,String password,OnLoginListener onLoginListener);}
UserBiz:
package com.example.liang.userloginformvp.biz;import com.example.liang.userloginformvp.bean.User;/** * Created by liang on 2016/8/22. */public class UserBiz implements IUserBiz { //在这里重写IUserBiz中的方法 @Override public void login(final String username, final String password, final OnLoginListener loginListener) { //注意,这里并没有new OnLoginListener的对象,所以也没有重写方法 //要留在UserLoginPresenter中重写,并通过登陆的状态调用相应的视图 new Thread(){ @Override public void run() { try{ Thread.sleep(2000); }catch(InterruptedException e){ e.printStackTrace(); } //模拟登陆成功 if("lqy".equals(username)&&"123".equals(password)){ User user=new User(); user.setUsername(username); user.setPassword(password); loginListener.loginSuccess(user); }else{ loginListener.loginFailed(); } } }.start(); }}
UserLoginPresenter:
public class UserLoginPresenter { private IUserBiz userBiz; private IUserLoginView userLoginView; private Handler mHandler = new Handler(); public UserLoginPresenter(IUserLoginView userLoginView) { this.userLoginView = userLoginView; //给userBiz一个子类的空间,此时方法已经被重写,登陆逻辑已被判断 this.userBiz = new UserBiz(); } public void login() { userLoginView.showLoading(); userBiz.login(userLoginView.getUserName(), userLoginView.getPassword(), new OnLoginListener() { @Override public void loginSuccess(final User user) { //需要在UI线程执行 mHandler.post(new Runnable() { @Override public void run() { userLoginView.toMainActivity(user); userLoginView.hideLoading(); } }); } @Override public void loginFailed() { //需要在UI线程执行 mHandler.post(new Runnable() { @Override public void run() { userLoginView.showFailedError(); userLoginView.hideLoading(); } }); } }); } public void clear() { userLoginView.clearUserName(); userLoginView.clearPassword(); }}
IUserLoginPresenter:
package com.example.liang.userloginformvp.view;import com.example.liang.userloginformvp.bean.User;/** * Created by liang on 2016/8/22. */public interface IUserLoginView { String getUserName(); String getPassword(); void clearUserName(); void clearPassword(); void showLoading(); void hideLoading(); void toMainActivity(User user); void showFailedError();}
MainActivity:
package com.example.liang.userloginformvp;import android.support.v7.app.ActionBarActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.ProgressBar;import android.widget.Toast;import com.example.liang.userloginformvp.bean.User;import com.example.liang.userloginformvp.presenter.UserLoginPresenter;import com.example.liang.userloginformvp.view.IUserLoginView;public class MainActivity extends ActionBarActivity implements IUserLoginView{ private EditText usernaem_edit,password_edit; private Button login_btn,clear_btn; private ProgressBar loading; private UserLoginPresenter userLoginPresenter=new UserLoginPresenter(this); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initViews(); } private void initViews(){ usernaem_edit=(EditText)findViewById(R.id.edit_username); password_edit=(EditText)findViewById(R.id.edit_password); login_btn=(Button)findViewById(R.id.login); clear_btn=(Button)findViewById(R.id.clear); loading=(ProgressBar)findViewById(R.id.login_progressbar); loading.setVisibility(View.INVISIBLE); login_btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { userLoginPresenter.login(); } }); clear_btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { userLoginPresenter.clear(); } }); } @Override public String getUserName() { return usernaem_edit.getText().toString(); } @Override public String getPassword() { return password_edit.getText().toString(); } @Override public void clearUserName() { usernaem_edit.setText(""); } @Override public void clearPassword() { password_edit.setText(""); } @Override public void showLoading() { loading.setVisibility(View.VISIBLE); } @Override public void hideLoading() { loading.setVisibility(View.GONE); } @Override public void toMainActivity(User user) { Toast.makeText(this,user.getUsername()+" login success , to MainActivity",Toast.LENGTH_SHORT) .show(); } @Override public void showFailedError() { Toast.makeText(this, "login failed", Toast.LENGTH_SHORT).show(); }}
MVP总结:Activity作为View加载视图并响应点击事件(直接想登陆),点击事件交由Presenter处理,Presenter将登陆逻辑交给Model处理,并根据model处理结果告诉View改返回什么试图。
------------------------------------分隔线
从MVP到MVVM中:至于MVVM基本上和MVP一模一样,感觉只是名字替换了一下。他的关键技术就是今天的主题(Data Binding)。View的变化可以自动的反应在ViewModel,ViewModel的数据变化也会自动反应到View上。这样开发者就不用处理接收事件和View更新的工作,框架已经帮你做好了。
三 MVVM模式
M:model,业务逻辑
V:加载视图,和layout通信,进行数据绑定与更新,对应Activity(由于代码是在太少,有时自带启动自己的方法)
VM:判断条件,逻辑实现
项目框架:
首先配置环境
加一句:
LoginViewModel
package com.example.liang.userloginformvvm.viewmodel;import android.content.Context;import android.databinding.ObservableField;import android.databinding.ObservableInt;import android.text.Editable;import android.text.TextWatcher;import android.view.View;import com.example.liang.userloginformvvm.modle.User;import com.example.liang.userloginformvvm.view_activity.MainActivity;/** * Created by liang on 2016/8/23. */public class LoginViewModel implements ViewModel { private Context context; //用于数据刷新的便捷类型:ObservableField,ObservableInt···· public ObservableField<String> loginMessage; public ObservableInt loginMessageVisibility; private String editTextUsernameValue = ""; private String editTextPasswordValue = ""; public LoginViewModel(Context context) { this.context = context; this.loginMessage = new ObservableField<>(""); this.loginMessageVisibility = new ObservableInt(View.INVISIBLE); } //登陆,实际上这个方法是在layout文件中调用的 public void loginAuthentication(View view) { if ((editTextUsernameValue.equals("lqy")) && (editTextPasswordValue.equals("123"))) { loginMessage.set(""); loginMessageVisibility.set(View.INVISIBLE); User user = new User(editTextUsernameValue, editTextPasswordValue); context.startActivity(MainActivity.newIntent(context, user)); } else if ((editTextUsernameValue.equals("")) || (editTextPasswordValue.equals(""))) { loginMessage.set("Username or Password can't be empty!"); loginMessageVisibility.set(View.VISIBLE); } else { loginMessage.set("Username = lqy \n Password = 123"); loginMessageVisibility.set(View.VISIBLE); } } //观察Text变化的TextWatcher public TextWatcher getUsernameUpdate() { return new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { } @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { editTextUsernameValue = charSequence.toString(); } @Override public void afterTextChanged(Editable editable) { } }; } public TextWatcher getPasswordUpdate() { return new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { } @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { editTextPasswordValue = charSequence.toString(); } @Override public void afterTextChanged(Editable editable) { } }; } @Override public void destroy() { }}
MainViewModel:
package com.example.liang.userloginformvvm.viewmodel;import android.content.Context;import com.example.liang.userloginformvvm.modle.User;/** * Created by liang on 2016/8/23. */public class MainViewModel { private Context context; private User user; public MainViewModel(Context context, User user) { this.context = context; this.user = user; } public String getUsername(){ return user.username; } public String getPassword(){ return user.password; }}
ViewModel:
package com.example.liang.userloginformvvm.viewmodel;/** * Created by liang on 2016/8/23. */public interface ViewModel { void destroy();}
User:
package com.example.liang.userloginformvvm.modle;import android.os.Parcel;import android.os.Parcelable;/** * Created by liang on 2016/8/23. *///实现Parcelable接口,可以让类在网络或进程中传递public class User implements Parcelable{ public String username; public String password; public User(String username,String password){ this.username=username; this.password=password; } public User(Parcel in) { username = in.readString(); password = in.readString(); } public static final Creator<User> CREATOR=new Creator<User>() { @Override public User createFromParcel(Parcel parcel) { return new User(parcel); } @Override public User[] newArray(int i) { return new User[i]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel parcel, int i) { parcel.writeString(this.username); parcel.writeString(this.password); }}
LoginActivity
package com.example.liang.userloginformvvm.view_activity;import android.databinding.DataBindingUtil;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import com.example.liang.userloginformvvm.R;import com.example.liang.userloginformvvm.databinding.ActivityLoginBinding;import com.example.liang.userloginformvvm.viewmodel.LoginViewModel;/** * Created by liang on 2016/8/23. */public class LoginActivity extends AppCompatActivity{ //当给布局指定格式之后会产生相应的Bing类,例如activity_login.xml会产生ActivityLoginBinding类 ActivityLoginBinding loginBinding; LoginViewModel viewModel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //指定布局,没有XXX.findViewById了 loginBinding = DataBindingUtil.setContentView(this, R.layout.activity_login); viewModel=new LoginViewModel(this); loginBinding.setViewmodel(viewModel); }}
MainActivity
package com.example.liang.userloginformvvm.view_activity;import android.content.Context;import android.content.Intent;import android.databinding.DataBindingUtil;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import com.example.liang.userloginformvvm.R;import com.example.liang.userloginformvvm.databinding.ActivityMainBinding;import com.example.liang.userloginformvvm.modle.User;import com.example.liang.userloginformvvm.viewmodel.MainViewModel;/** * Created by liang on 2016/8/23. */public class MainActivity extends AppCompatActivity{ ActivityMainBinding activityMainBinding; MainViewModel mainViewModel; //传递数据的标志 private static final String EXTRA_USER = "extra_user"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); activityMainBinding= DataBindingUtil.setContentView(this, R.layout.activity_main); User user=getIntent().getParcelableExtra(EXTRA_USER); mainViewModel=new MainViewModel(this,user); activityMainBinding.setMainviewmodel(mainViewModel); } //viewmodel唤醒这个Activity的方法 public static Intent newIntent(Context context, User user){ Intent intent = new Intent(context, MainActivity.class); intent.putExtra(EXTRA_USER,user); return intent; }}
下面就是非常重要的layout文件了:
activity_mian.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="viewmodel" type="com.example.liang.userloginformvvm.viewmodel.LoginViewModel"> </variable> </data> <!--先要指定绑定的类--> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".view_activity.LoginActivity"> <TextView android:id="@+id/loginmessage" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{viewmodel.loginMessage}" android:textAlignment="center" android:textColor="@color/colorAccent" android:layout_centerHorizontal="true" android:layout_marginTop="20dp" android:layout_marginBottom="20dp"/> <!--监听text的变化--> <EditText android:id="@+id/login_username" android:layout_width="wrap_content" android:layout_height="wrap_content" android:inputType="textPersonName" android:ems="10" android:layout_centerHorizontal="true" android:layout_below="@+id/loginmessage" android:hint="UserName" android:layout_marginBottom="10dp" app:addTextChangedListener="@{viewmodel.usernameUpdate}"/> <EditText android:id="@+id/login_password" android:layout_width="wrap_content" android:layout_height="wrap_content" android:inputType="textPassword" android:hint="Password" android:ems="10" android:layout_below="@id/login_username" android:layout_centerHorizontal="true" android:layout_marginBottom="10dp" app:addTextChangedListener="@{viewmodel.passwordUpdate}" /> <Button android:layout_width="210dp" android:layout_height="wrap_content" android:text="LOGIN" android:layout_below="@id/login_password" android:layout_centerHorizontal="true" android:onClick="@{viewmodel.loginAuthentication}"/> </RelativeLayout></layout>
activity_login.xml
<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <data> <variable name="mainviewmodel" type="com.example.liang.userloginformvvm.viewmodel.MainViewModel"></variable> </data> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".view_activity.MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="60dp" android:orientation="horizontal"> <TextView android:layout_width="0dp" android:layout_height="match_parent" android:text="Welcome" android:layout_weight="1"/> <TextView android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="3" android:text="@{mainviewmodel.username}"/> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="60dp" android:orientation="horizontal"> <TextView android:layout_width="0dp" android:layout_height="match_parent" android:text="Password is" android:layout_weight="1"/> <TextView android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="3" android:text="@{mainviewmodel.password}"/> </LinearLayout> </LinearLayout></layout>
MVVM总结:通过View加载布局,并通过布局调用ViewModel中的方法,VeiwModel可以通过调用后的结果决定启动什么视图,同时ViewModel保持和model的通信。
2016.8.24首次截稿
有什么错误麻烦大家指出来,在线更新。
参考资料:
鸿洋老师的博客: http://blog.csdn.net/lmj623565791/article/details/46596109
胡笛老师的微信推文
还有github上的资源
以及http://my.oschina.net/u/1175007/blog/613889
- Android MVC,MVP,MVVM模式入门——重构登陆注册功能
- Android MVC MVP MVVM 模式
- MVC,MVP,MVVM模式
- MVC、MVP、MVVM模式
- Android中MVC、MVP、MVVM模式<一>
- Android MVC、MVP、MVVM模式框架
- Android架构模式:MVC & MVP & MVVM
- android设计模式(MVC MVP MVVM)
- android MVC,MVP,MVVM
- Android MVC MVP MVVM
- Android MVC MVP MVVM
- Android app重构:mvp和mvvm
- MVC、MVP、MVVM框架模式
- mvc、mvvm、mvp模式区分
- MVC、MVP、MVVM模式总结
- 架构模式 -- MVC、MVP、MVVM
- MVC、MVP、MVVM模式浅析
- 架构模式:MVC,MVP&MVVM
- OkHttp使用Get和Post访问服务器的工具类(一)
- 为什么Android无法设置无标题栏?
- 在Fragment中加一个嵌套了ListView的ScrollView(一)
- ScrollView中嵌套ExpandableListView
- 【096】Linux CentOS 7.3 允许或禁止root用户远程登录
- Android MVC,MVP,MVVM模式入门——重构登陆注册功能
- SVN 回滚(撤回)提交的代码
- 【备忘】2017年7月最新微服务架构的分布式事务解决视频教程
- android实时显示触屏坐标
- MVP简单使用+RecyclerView
- Linux下Mysql自启动
- 单例模式的双重检查成例的研究
- Ubuntu安装Mac主题
- IOError: RPC server not started