MVC是一种设计模式,也就是一种解决问题的方法和思路, 是上世纪80年代提出的,到现在已经颇有历史了。 MVC的意义在于指导开发者将数据与表现解耦,提高代码,特别是模型部分代码的复用性。
MVC模式的意思是,软件可以分成三个部分。 视图(View):用户界面。 控制器(Controller):业务逻辑 模型(Model):数据保存
View 传送指令到 Controller Controller 完成业务逻辑后,要求 Model 改变状态 Model 将新的数据发送到 View,用户得到反馈
所有通信都是单向的。 最上面的一层,是直接面向最终用户的”视图层”(View)。它是提供给用户的操作界面,是程序的外壳。 最底下的一层,是核心的”数据层”(Model),也就是程序需要操作的数据或信息。 中间的一层,就是”控制层”(Controller),它负责根据用户从”视图层”输入的指令,选取”数据层”中的数据,然后对其进行相应的操作,产生最终结果。
这三层是紧密联系在一起的,但又是互相独立的,每一层内部的变化不影响其他层。每一层都对外提供接口(Interface),供上面一层调用。这样一来,软件就可以实现模块化,修改外观或者变更数据都不用修改其他层,大大方便了维护和升级。
举例 用Windows的计算器小程序为例,解释一下MVC模式,虽然它不一定使用这个模式编写。 在这个计算器程序中,外部的那些按钮和最上面的显示条,就是”视图层”,那些需要运算的数字就是”数据层”,执行加减乘除的那些内部运算步骤就是”控制层”。每一层执行不同的功能,整个程序的结构非常清楚。 如果我们扩大一点想象,就会发现,很多程序本质上都是这种模式:对外提供一组触发器(本例中是按钮),然后执行一些内部操作,最后返回结果。因此,MVC模式的应用是非常广泛的。
以家用微波炉为例,可以将它也理解成三层结构。最简单的情况下,微波炉的操作用两个转盘实现,一个控制温度,另一个控制时间。这两个转盘就是”视图层”(view),而其内部的微波产生装置则是”数据层”(Model),这里的”数据”需要理解成”核心功能”。至于将用户通过转盘输入的信息,转换成对微波产生器的操作,则用”控制层”来实现。 如果每一层都是独立的,那么微波炉外部更换一个新潮的外壳,或者内部更换更大功率的微波产生器,完全可以在不更改其他层的情况下实现。这就是MVC模式的优势。
View:自定义View或ViewGroup,负责将用户的请求通知Controller,并根据model更新界面;
Controller:Activity或者Fragment,接收用户请求并更新model;
Model:数据模型,负责数据处理相关的逻辑,封装应用程序状态,响应状态查询,通知View改变,对应Android中的datebase、SharePReference等。
(1)耦合性低。所谓耦合性就是模块代码之间的关联程度。利用MVC框架使得View(视图)层和Model(模型)层可以很好的分离,这样就达到了解耦的目的,所以耦合性低,减少模块代码之间的相互影响。
(2)可扩展性好。由于耦合性低,添加需求,扩展代码就可以减少修改之前的代码,降低bug的出现率。
(3)模块职责划分明确。主要划分层M,V,C三个模块,利于代码的维护。
以登录界面为例
编写V
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main"android:layout_width="match_parent" android:layout_height="match_parent"tools:context="com.itheima.a001mvclogin.MainActivity"><LinearLayoutandroid:layout_width="match_parent"android:orientation="vertical"android:layout_centerInParent="true"android:layout_height="wrap_content"><EditTextandroid:layout_width="match_parent"android:maxLines="1"android:hint="输入账号"android:id="@+id/account_et"android:layout_height="wrap_content" /><EditTextandroid:layout_width="match_parent"android:maxLines="1"android:hint="输入密码"android:id="@+id/pwd_et"android:layout_height="wrap_content" /><Buttonandroid:layout_width="match_parent"android:onClick="login"android:text="登录"android:layout_height="wrap_content" /><ProgressBarandroid:layout_width="wrap_content"android:id="@+id/pbar"android:visibility="invisible"android:layout_gravity="center_horizontal"android:layout_height="wrap_content" /></LinearLayout></RelativeLayout>编写M http或者database
public class User {public String pwd;public String account;public User(String account, String pwd) {this.account = account;this.pwd = pwd;}}编写C
public class MainActivity extends AppCompatActivity {private EditText accountEt;private EditText pwdEt;private ProgressBar pBar;@Overrideprotected void onCreate(Bundle savedInstanceState{ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);//编写登录//1.编写V 在android中就是在layout里面布局 accountEt = (EditText) findViewById(R.id.account_et); pwdEt = (EditText) findViewById(R.id.pwd_et); pBar = (ProgressBar) findViewById(R.id.pbar);//2.编写M 将页面上获取的账号密码提交给服务器, 返回校验结果 //3.编写C 事件 线程 判断 } public void login(View view) {//3.1.获取页面值 String inputAccount = accountEt.getText().toString(); String inputPwd = pwdEt.getText().toString();//3.2.进行逻辑判断 if (!TextUtils.isEmpty(inputAccount) && !TextUtils.isEmpty(inputPwd)) { //3.3.提示等待 pBar.setVisibility(View.VISIBLE);//3.4.数据提交 final User user = new User(inputAccount, inputPwd);//发送 new Handler().postDelayed(new Runnable() {@Overridepublic void run() {//3.5.获取请求结果 int code = new Random().nextInt(2);//3.6.关闭等待 pBar.setVisibility(View.INVISIBLE);if (code == 0)//账号密码正确 { Toast.makeText(MainActivity.this, "登录成功,欢迎" + user.account, Toast.LENGTH_SHORT).show(); } else {Toast.makeText(MainActivity.this, "账号密码错误", Toast.LENGTH_SHORT).show(); } } }, 3000); } else { Toast.makeText(this, "账号密码不能为空", Toast.LENGTH_SHORT).show(); } }}参考别人写的代码 点击查看
View实现:
public class TrackCtrlView implements View.OnClickListener{ private ImageView btnStartTrack, btnStopTrack, btnPauseTrack; private TrackCtrlViewListener listener; private TrackRecordInfo trackRecordInfo; public TrackCtrlView(Activity activity, TrackCtrlViewListener listener){ this.listener = listener; btnStartTrack = (ImageView) activity.findViewById(R.id.btnStartTrack); btnStopTrack = (ImageView) activity.findViewById(R.id.btnStopTrack); btnPauseTrack = (ImageView) activity.findViewById(R.id.btnPauseTrack); btnStartTrack.setOnClickListener(this); btnStopTrack.setOnClickListener(this); btnPauseTrack.setOnClickListener(this); btnPauseTrack.setOnClickListener(this); } /** * 将用户请求通知Controller */ @Override public void onClick(View v) { switch(v.getId()){ case R.id.btnStartTrack: if(listener != null){ listener.trackStatusRequest(TrackRecordStatus.Recording); } break; case R.id.btnStopTrack: if(listener != null){ listener.trackStatusRequest(TrackRecordStatus.Stoped); } break; case R.id.btnPauseTrack: if(listener != null){ if(trackRecordInfo.status == TrackRecordStatus.Paused){ listener.trackStatusRequest(TrackRecordStatus.Recording); }else{ listener.trackStatusRequest(TrackRecordStatus.Paused); } } break; default: break; } } private void refreshView(){ TrackRecordStatus trackStatus = trackRecordInfo == null ? TrackRecordStatus.Stoped : trackRecordInfo.status; if (trackStatus == TrackRecordStatus.Recording) { btnStartTrack.setVisibility(View.GONE); btnPauseTrack.setVisibility(View.VISIBLE); btnStopTrack.setVisibility(View.VISIBLE); btnPauseTrack.setImageResource(R.drawable.btn_track_ctrl_pause); } else if (trackStatus == TrackRecordStatus.Paused) { btnStartTrack.setVisibility(View.GONE); btnPauseTrack.setVisibility(View.VISIBLE); btnStopTrack.setVisibility(View.VISIBLE); btnPauseTrack.setImageResource(R.drawable.btn_track_ctrl_resume); } else { // TrackRecordStatus.Stoped btnStartTrack.setVisibility(View.VISIBLE); btnPauseTrack.setVisibility(View.GONE); btnStopTrack.setVisibility(View.GONE); } } public void setTrackRecordInfo(@Nullable TrackRecordInfo trackRecordInfo) { this.trackRecordInfo = trackRecordInfo; refreshView(); } public interface TrackCtrlViewListener{ /** * 用户点击按钮 */ public void trackStatusRequest(@Nullable TrackRecordStatus newStatus); }}Model实现:
public class TrackRecordInfo { private static final Gson gson = new Gson(); /** * 应该是保存轨迹数据库id,此demo中数据库操作不实现,暂时trackId一直为0 */ public int trackId; public TrackRecordStatus status; public TrackRecordInfo(int trackId, TrackRecordStatus status) { this.trackId = trackId; this.status = status; } @NonNull public static TrackRecordInfo loadTrackRecordInfo(@NonNull Context context){ String pref = SpUtil.getString(context, SpUtil.KEY_TRACK_RECORD_INFO, ""); if(!TextUtils.isEmpty(pref)){ return gson.fromJson(pref, TrackRecordInfo.class); } return null; } public static void changeTrackRecordInfo(@NonNull Context context, @Nullable TrackRecordInfo info){ SpUtil.saveString(context, SpUtil.KEY_TRACK_RECORD_INFO, info == null ? "" : gson.toJson(info)); //model通过消息总线,通知View刷新 EventBus.getDefault().post(new EventTrackRecordInfoChanged(info)); }}Controller实现:
public class MainActivity extends ActionBarActivity implements TrackCtrlView.TrackCtrlViewListener{ private TrackCtrlView trackCtrlView; private TrackRecordInfo trackRecordInfo; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); trackCtrlView = new TrackCtrlView(this, this); EventBus.getDefault().register(this); trackRecordInfo = TrackRecordInfo.loadTrackRecordInfo(this); trackCtrlView.setTrackRecordInfo(trackRecordInfo); } @Override protected void onDestroy() { super.onDestroy(); EventBus.getDefault().unregister(this); } @Override public void trackStatusRequest(@Nullable TrackRecordStatus newStatus) { if(newStatus == TrackRecordStatus.Recording){ int trackId = 0; //在数据库创建一条轨迹,并获取到数据库id trackRecordInfo = new TrackRecordInfo(trackId, TrackRecordStatus.Recording); }else if (newStatus == TrackRecordStatus.Paused) { if(trackRecordInfo != null){ trackRecordInfo.status = newStatus; } } else { trackRecordInfo = null; } TrackRecordInfo.changeTrackRecordInfo(this, trackRecordInfo); } public void onEventMainThread(EventTrackRecordInfoChanged event){ trackRecordInfo = event.info; trackCtrlView.setTrackRecordInfo(trackRecordInfo); }}参考博客John-Chen
新闻热点
疑难解答