开发模式——MVC框架开发

简介

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等。

MVC的优点:

(1)耦合性低。所谓耦合性就是模块代码之间的关联程度。利用MVC框架使得View(视图)层和Model(模型)层可以很好的分离,这样就达到了解耦的目的,所以耦合性低,减少模块代码之间的相互影响。

(2)可扩展性好。由于耦合性低,添加需求,扩展代码就可以减少修改之前的代码,降低bug的出现率。

(3)模块职责划分明确。主要划分层M,V,C三个模块,利于代码的维护。

代码Demo

以登录界面为例

这里写图片描述

编写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">
<LinearLayout
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_centerInParent="true"
android:layout_height="wrap_content">
<EditText
android:layout_width="match_parent"
android:maxLines="1"
android:hint="输入账号"
android:id="@+id/account_et"
android:layout_height="wrap_content" />
<EditText
android:layout_width="match_parent"
android:maxLines="1"
android:hint="输入密码"
android:id="@+id/pwd_et"
android:layout_height="wrap_content" />
<Button
android:layout_width="match_parent"
android:onClick="login"
android:text="登录"
android:layout_height="wrap_content" />
<ProgressBar
android: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;
@Override
protected 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() {
@Override
public 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();
      }
   }
}

代码Demo2

参考别人写的代码 点击查看

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值