传统的软件设计MVC模型在WEB开发中很流行,也很合适,因为Servlet的灵活性,使用Servlet中与UI、业务相关的代码可以完全分离。WEB项目中,Servlet充当了Controller的角色,HTML、JSP等视图技术充当了View的角色,JavaBean、DAO等充当于Modle的角色。
虽然MVC模式在WEB端大红大紫,但是,在安卓开发中,MVC并不实用,安卓开发若是强行使用MVC的话,定会使得程序更加复杂,更加难以维护。
因为,在Android开发中,View主要是由资源文件(如layout、values、drawable等)和View的子类构成,而这些View又主要由Activity来控制显示,由此,Activity就是View的Controller(控制器),因为与View相关的操作都在Activity中完成,所以Activity与View之间就紧密相联,这样Activity与View之间的耦合度就非常的高,往往使得一个Activity中充满了上千行与View相关操作的代码。
在Android开发中,业务层的代码,往往都会与Activity打交道,因为业务代码要与View打交道。这样,在Activity中同时也充满了很多与业务相关的代码,就使得一个Activity中的代码数量更多庞大了。
因此在安卓传统开发中,一个Activity往往都容易达到上千行代码,维护起来也相当困难。
本文要讲解的一个安卓端开发模式,虽然功能和火热的MVP模式相同,但是,设计理念也相同,但是,实现方式并不一样,专注点也不一样。本文讲解的开发模式,专注与将UI与业务相关的代码从Activity中抽离出来,使得Activity中代码量极速减少,同时,将业务分离的更加具体,使得Activity专注于Controller的工作,View专注于做View的工作,业务专注于业务处理,我称之MVB模式(Controller、View、Business)。
以下,直接上源码说明CVB模式
(注意,源码中使用了XUtil框架)
1、源码结构图
2、ExampleViewHolder
package com.winway.collectiondata.viewholder;
import com.lidroid.xutils.view.annotation.ViewInject;
import com.winway.collectiondata.R;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class ExampleViewHolder {
@ViewInject(R.id.title)
public TextView titleTV;
@ViewInject(R.id.username)
public EditText usernameET;
@ViewInject(R.id.password)
public EditText passwordET;
@ViewInject(R.id.code)
public EditText validateCodeET;
@ViewInject(R.id.login)
public Button enterBT;
public TextView getTitleTV() {
return titleTV;
}
public void setTitleTV(TextView titleTV) {
this.titleTV = titleTV;
}
public EditText getUsernameET() {
return usernameET;
}
public void setUsernameET(EditText usernameET) {
this.usernameET = usernameET;
}
public EditText getPasswordET() {
return passwordET;
}
public void setPasswordET(EditText passwordET) {
this.passwordET = passwordET;
}
public EditText getValidateCodeET() {
return validateCodeET;
}
public void setValidateCodeET(EditText validateCodeET) {
this.validateCodeET = validateCodeET;
}
public Button getEnterBT() {
return enterBT;
}
public void setEnterBT(Button enterBT) {
this.enterBT = enterBT;
}
}
ExampleViewHolder中使用了XUtil框架的注解
3、BaseBusiness
package com.winway.collectiondata.base;
import com.lidroid.xutils.ViewUtils;
import android.app.Activity;
import android.content.Intent;
/**
* 基本业务类
*
* @author mr-lao
*
*/
public abstract class BaseBusiness<T> {
// Activity对象,业务可以用来获得Activity中拥有的功能代码
protected Activity mActivity;
// ViewHolder对象,存放业务需要乃至的View控件
protected T viewHolder;
public Activity getActivity() {
return mActivity;
}
public void setActivity(Activity mActivity) {
this.mActivity = mActivity;
}
public T getViewHolder() {
return viewHolder;
}
/**
* 设置ViewHolder对象,并对业务实体进行初始化
*
* @param viewHolder
*/
public void setViewHolder(T viewHolder) {
this.viewHolder = viewHolder;
init();
}
/**
* 注入view和事件
*
* @param activity
* @param holderClass
* @throws InstantiationException
* @throws IllegalAccessException
*/
public void initBusiness(Activity activity, Class<T> holderClass)
throws InstantiationException, IllegalAccessException {
this.viewHolder = holderClass.newInstance();
initBusiness(activity, this.viewHolder);
}
/**
* 注入view和事件
*
* @param activity
* @param viewHolder
*/
public void initBusiness(Activity activity, T viewHolder) {
this.mActivity = activity;
// 用xUtil的ViewInject功能初始化viewholder对象
this.viewHolder = viewHolder;
ViewUtils.inject(this.viewHolder, activity);
ViewUtils.inject(this, activity);
// 回调初始化方法
init();
}
/**
* 当Activity调用setViewHolder或initBusiness方法 时,会调用init()方法。
* 子类通过实现init()方法来初始化自身的一些属性和数据,init()方法是业务类的入口方法。
*/
public abstract void init();
// 申明一些与Activity中请用的方法名字相同的方法
/**
* 处理从Activity跳到另外一个Activity返回来的数据(需要用到的话,子类重写此方法即可,不需要用到的话,请忽略)
*
* @param requestCode
* @param resultCode
* @param data
*/
public void onActivityResult(int requestCode, int resultCode, Intent data) {
}
/**
* 获得从另一个Activity跳转到此Activity的意图对象,借此,获得跳转Activity带来的数据
*
* @return
*/
public Intent getIntent() {
return mActivity.getIntent();
}
/**
* 请在Activity的onDestroy()方法中调用
*/
public void onDestroy() {
}
/**
* 请在Activity的onResume()方法中调用
*/
public void onResume() {
}
/**
* 请在Activity的onPause()方法中调用
*/
public void onPause() {
}
/**
* 请在Activity的onStop()方法中调用
*/
public void onStop() {
}
/**
* 请在Activity的onRestart()方法中调用
*/
public void onRestart() {
}
}
4、activity_example.xml (布局)
<?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"
android:padding="10dp" >
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#326373"
android:gravity="center"
android:padding="4dp"
android:text="Title"
android:textColor="#FFFFFF" />
<EditText
android:id="@+id/username"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginTop="5dp"
android:padding="5dp" />
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginTop="5dp"
android:padding="5dp"
android:password="true" />
<EditText
android:id="@+id/code"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginTop="5dp"
android:padding="5dp"
android:phoneNumber="true" />
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="登陆" />
</LinearLayout>
5、ExampleActivity
package com.winway.collectiondata.activity;
import com.winway.collectiondata.R;
import com.winway.collectiondata.business.example.ExampleBusiness;
import com.winway.collectiondata.viewholder.ExampleViewHolder;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
/**
*
* @author mr-lao
*
*/
@SuppressLint("CutPasteId")
public class ExampleActivity extends Activity {
private ExampleBusiness exampleBSS;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_example);
initExampleBBS();
}
private void initExampleBBS() {
exampleBSS = new ExampleBusiness();
try {
exampleBSS.initBusiness(this, ExampleViewHolder.class);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
exampleBSS.onDestroy();
}
@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
exampleBSS.onResume();
}
@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
exampleBSS.onPause();
}
@Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
exampleBSS.onStop();
}
@Override
protected void onRestart() {
// TODO Auto-generated method stub
super.onRestart();
exampleBSS.onRestart();
}
}
6、ExampleBusiness
package com.winway.collectiondata.business.example;
import com.lidroid.xutils.view.annotation.event.OnClick;
import com.winway.collectiondata.R;
import com.winway.collectiondata.base.BaseBusiness;
import com.winway.collectiondata.viewholder.ExampleViewHolder;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Toast;
/**
* 例子业务处理类
* @author mr-lao
*
*/
public class ExampleBusiness extends BaseBusiness<ExampleViewHolder> {
@Override
public void init() {
// 给按纽注册监听器
viewHolder.enterBT.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String userName = viewHolder.usernameET.getText().toString();
String password = viewHolder.passwordET.getText().toString();
String code = viewHolder.validateCodeET.getText().toString();
if (TextUtils.isEmpty(userName)) {
Toast.makeText(getActivity(), "用户名不能为空", Toast.LENGTH_SHORT).show();
return;
}
if (TextUtils.isEmpty(password)) {
Toast.makeText(getActivity(), "密码不能为空", Toast.LENGTH_SHORT).show();
return;
}
if (TextUtils.isEmpty(code)) {
Toast.makeText(getActivity(), "验证码不能为空", Toast.LENGTH_SHORT).show();
return;
}
Toast.makeText(getActivity(),
"username=" + userName + ",password=" + password + ",code=" + code,
Toast.LENGTH_LONG).show();
boolean ll = login(userName, password, code);
if (ll) {
Toast.makeText(mActivity, "登陆成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(mActivity, "登陆失败", Toast.LENGTH_SHORT).show();
}
}
});
// 设置标题
viewHolder.titleTV.setText("ExampleBusiness");
}
/**
* 模拟登陆
* @param username
* @param password
* @param code
* @return
*/
private boolean login(String username, String password, String code) {
if (!"admin".equals(username)) {
Toast.makeText(mActivity, "用户名错误,请填写admin", Toast.LENGTH_SHORT).show();
return false;
}
if (!"password".equals(password)) {
Toast.makeText(mActivity, "密码错误,请填写password", Toast.LENGTH_SHORT).show();
return false;
}
if (!"123456".equals(code)) {
Toast.makeText(mActivity, "验证码错误,请填写123456", Toast.LENGTH_SHORT).show();
return false;
}
return true;
}
@OnClick(R.id.title)
public void clickTitle(View view) {
Toast.makeText(getActivity(), "点击了标题。。。。。。。", Toast.LENGTH_SHORT).show();
}
@Override
public void onDestroy() {
Log.i("info", "ExampleBusiness onDestroy");
}
@Override
public void onPause() {
Log.i("info", "ExampleBusiness onPause");
}
@Override
public void onStop() {
Log.i("info", "ExampleBusiness onStop");
}
@Override
public void onResume() {
Log.i("info", "ExampleBusiness onResume");
}
}
解读:
传统的安卓开发,造成Activity与UI以及业务代码高度耦合,是由于View控件的不独立,View的不独立造成业务代码的不独立。
CVB模式首先是将View从Activity中抽取出来,存放于ViewHolder容器中,使得View从Activity中独立出来。
其次是将Business当成一个普通的Java类,Activity中把ViewHolder对象传递给Business对象即可,同时,Activity也把自身传递给Business,使得Business不仅可以操作View,也可以拥有Activity所拥有的功能。