第2章第4节界面MVC(上)
内容摘要
上节我们学习了简单的Android程序入门,使大家对Android开发有了初步的印象,本节我们讲解一下简单的前端架构设计。
1.MVC简介
2.坏的设计
3.稍好点的设计
4.一点设计经验
5.总结
其实架构设计无处不在,而APP开发很少涉及架构设计,因为APP的功能定位比较简单,都是在单进程下的单用户操作,很少有APP开发者设计非常复杂的多线程架构,但是这并不等于APP的开发就无需架构设计。
对于经验欠缺的APP开发者,你会发现他们设计的目录结构以及代码与代码之间的逻辑关系非常混乱,很多功能都罗列在一切,只是简单的让程序能跑起来,并不怎么关心可维护性和可持续改进的能力。
我曾经见到过这样的代码:布局文件既有xml文件来设置,也有java代码来设置的,非常混乱的掺杂在一起,导致后期维护非常苦难。你会发现对于这样的代码,接手者的第一反应是,修改还不如重写。
因此本节介绍一种简单的设计模式,MVC模式,让你的APP代码目录结构简洁,做到高内聚,低耦合。写出漂亮的代码既能够方便自己的管理和维护,又方便他人阅读和维护。
MVC简介
MVC:Model View Controller ,这里我简单说一下我的理解,Model就是数据, View就是数据的展示, Controller 是控制如何展示数据以及用户操作界面之后,怎样存储最新数据。如果有兴趣的同学,可以去网上搜索一下更详细的知识。我从网上搜了一个比较准确的图片:
我们以展示某学生的简单信息为例,假设学生信息只有4个属性:姓名,学号,年龄,年级。我们的主要工作就是展示某学生的基本信息,并支持简单的内容修改。
首先我们把流程描述一下,你就会发现,每个步骤都要有一个角色去实现
step1:我们根据学生的学号,从数据库或者网络获取关于该学生的基本信息,并存储到某个对象里面。
step2:我们需要生成或者加载一个界面来展示学生的基本信息。
step3:我们需要把step1中存储的学生信息,赋值给展示界面的各个控件,当用户修改这些信息的时候,还需要将用户修改的数据,赋值给存储对象,并存储回数据库或者网络。
那么根据以上流程,你就会发现,step1中的功能就需要一个model这样的角色来处理,step2中的展示功能,就需要view这样的角色来处理,step3中的逻辑处理就是controller的工作。
这应该是最简单的MVC的解释了,关于MVC设计模式,并没有非常清晰的定义和概念的描述,总的原则就是分离数据,展示,处理逻辑这三部分,让代码清晰可读,容易理解。接下来我将用两个例子来展示,好的设计和一锅粥式的代码实现,有什么区别。
坏的设计
我们在上一节中的Hello World基础上,增加一个新的Activity ,并从MainActivity跳转到新界面,这个新建的Activity将为大家展示一个不好的设计代码。
首先新建一个basic activity。
点击finish, 然后我们在Hello Word的MainActivity里面增加一个跳转到我们示例的按钮,并增加响应代码,如下:
public void jumpToBad(View view){
Intent intent = new Intent(this, MVCBadActivity.class);
intent.putExtra("name", "李四");
intent.putExtra("idCard", "wx001");
intent.putExtra("age", 20);
intent.putExtra("grade", 2);
startActivity(intent);
}
我们在跳转到新的界面之前,将数据传输到新的界面,在生产环境下,一般都是从本地数据库获取或者从网络获取,然后再在新界面展示。
接下来我们就是要创建一些页面元素来展现我们得到的数据。这是本节要讲解的重点,我们先看一个不好的例子,一个Activity这样的Controller如何把学生信息这个Model展现在Textview,EditText这些android的View上的。先看代码:
public class MVCBadActivity extends AppCompatActivity {
String stu_name, stu_idCard;
int stu_age, stu_grade;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
stu_name = intent.getStringExtra("name");
stu_idCard = intent.getStringExtra("idCard");
stu_age = intent.getIntExtra("age", 18);
stu_grade = intent.getIntExtra("grade", 1);
......
首先定义私有成员变量,接收从前一个界面传输过来的数据,这部分逻辑就是Model模块的角色应该承担的任务。
RelativeLayout layout = new RelativeLayout(this);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
layout.setLayoutParams(layoutParams);
然后创建一个相对布局,用来展示这些数据,创建布局,以及设置布局的大小和相对位置,这些工作是Controller这个角色来完成的,而创作出来的对象就是View这个角色,View的任务就是作为一个手机操作系统可以理解并能够显示的对象,用来展示Controller赋予他的内容 。
TextView label_name = new TextView(this);
label_name.setId(1);
label_name.setText("学生姓名:");
RelativeLayout.LayoutParams params1 = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
EditText text_name = new EditText(this);
text_name.setId(2); text_name.setEms(5);
text_name.setInputType(InputType.TYPE_TEXT_VARIATION_PERSON_NAME);
text_name.setText(stu_name);
RelativeLayout.LayoutParams params2 = new RelativeLayout.LayoutParams(300, RelativeLayout.LayoutParams.WRAP_CONTENT);
params2.addRule(RelativeLayout.RIGHT_OF, label_name.getId());
params1.addRule(RelativeLayout.ALIGN_BOTTOM, text_name.getId());
params2.leftMargin = 180;
layout.addView(label_name, params1);
layout.addView(text_name, params2);
添加一个标签,提示当前数据表示的是学生姓名。然后添加一个可以编辑的控件,用来展示学生姓名给用户并且可以接受用户对学生姓名的修改。
这里需要注意的是需要通过代码来设置两个控件的大小,距离,以及相对位置等信息,最后通过addview函数将两个控件放到整个页面的相对布局中。
其它三个控件的设置与此类似,不再赘述。这里所用到的EditText和TextView控件,都是继承自android的android.view.View这个类,通过android studio的查看源码的方式,可以跟踪到这些控件的父类,此处的View类的命名,很好地诠释了MVC设计中的View角色应该承担的工作。
提示:在oncreate函数的最后要调用setContentView(layout);
所有控件添加完成之后,效果是这样的:
这里需要再深入讲解一下的是“修改”这个按钮的布局和逻辑处理:
RelativeLayout.LayoutParams params9 = new RelativeLayout.LayoutParams(300, RelativeLayout.LayoutParams.WRAP_CONTENT);
params9.topMargin = 200;
params9.leftMargin = 100;
params9.addRule(RelativeLayout.BELOW, label_grade.getId());
Button btn_ok = new Button(this);
btn_ok.setText("修改");
btn_ok.setId(9);
btn_ok.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
stu_name = text_name.getText().toString();
stu_idCard = text_idCard.getText().toString();
stu_age = Integer.valueOf(text_age.getText().toString());
stu_grade = Integer.valueOf(text_grade.getText().toString());
//调用数据库存储或者网络存储
Toast.makeText(MVCBadActivity.this, "处理完成",
Toast.LENGTH_SHORT).show();
finish();
}
});
代码前半部分是布局设置,与其它控件的设置相似,重点是按钮的响应代码。当用户修改了基本信息的时候,我们从这些View获取数据并把数据复制给对应的私有成员变量。这个过程就是在Controller的控制下,将View的数据赋值给Model角色。
还有一点需要说明的就是 finish()函数的调用,这个动作是将当前Activity从堆栈中弹出并且释放内存资源,此知识点在本章中第二节中详细的介绍过,不理解的同学可以温习一下。
至此已经将这个demo讲解完毕,通篇代码设计的缺陷也比较明显,数据的加载赋值,View的创建和位置的设置,数据反馈给Model的代码逻辑,控件的响应函数,全部融合在Controller这一个角色里面。
当工程比较简单的情况下,尚且可以维护,但是如果代码逻辑变得复杂,数据展示内容变多,各种动画效果和图片都融合在一起的时候,这样的代码将会变得非常复杂和冗长。
但是所有的事情有利有弊,这段代码的为数不多的优势就是,方便初学者学习和理解代码逻辑,特别是在我们讲解MVC模式的时候,通过一个函数就可以直观看到Controller如何操作Model和View的,以及他们之间如何交互。(未完待续)