Android---MVC/MVP/MVVM的演进
Posted 别偷我的猪_09
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android---MVC/MVP/MVVM的演进相关的知识,希望对你有一定的参考价值。
目录
我们通过"#字棋"游戏来展现MVC-->MVP-->MVVM 之间的演进
一个文件打天下
数据、视图以及逻辑都放在一个 class 里面。而一个 class 里最多 500 行代码,当代码过多时是很难维护的。页面是给用户看到,用户的需求是不断改变的,所以需要不定期的维护、迭代代码,所以一个 class 里面的代码要尽量少,不超过500行。
"#字棋"游戏的逻辑实现代码,全部放在了 MainActivity.java 里,如下:
package com.example.jingziqi;
import static com.example.jingziqi.MainActivity.Player.O;
import static com.example.jingziqi.MainActivity.Player.X;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity
private static final String TAG = MainActivity.class.getName();
// 定义一个棋手
public enum Player X, O
// 定义棋盘的格子,里面有个棋手
public static class Cell
private Player value;
public Player getValue()
return value;
public void setValue(Player value)
this.value = value;
// 总共定义 9 个格子
private final Cell[][] cells = new Cell[3][3];
private Player winner; // 记录结果,谁赢了
private GameState state; //记录当前的游戏状态
private Player currentTurn; // 现在是那位棋手在下---X/O
private enum GameState IN_PROGRESS, FINISHED //游戏的状态---进行/结束
// Views
private ViewGroup buttonGrid;
private View winnerPlayerViewGroup;
private TextView winnerPlayerLabel;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
winnerPlayerLabel = findViewById(R.id.winnerPlayerLabel);
winnerPlayerViewGroup = findViewById(R.id.winnerPlayerViewGroup);
buttonGrid = findViewById(R.id.buttonGrid);
restart();
@Override
public boolean onCreateOptionsMenu(Menu menu)
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_jingziqi, menu);
return true;
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item)
switch (item.getItemId())
case R.id.action_reset:
restart();
return true;
default:
return super.onOptionsItemSelected(item);
public void onCellClicked(View view)
Button button = (Button) view;
String tag = button.getTag().toString();
// 取到点击的格子的行/列
int row = Integer.parseInt(tag.substring(0, 1));
int col = Integer.parseInt(tag.substring(1, 2));
Log.i(TAG, "Click Row: [" + row + ", " + col + "]");
Player playerThatMoved = mark(row, col);
if (playerThatMoved != null)
button.setText(playerThatMoved.toString());
if (getWinner() != null)
winnerPlayerLabel.setText(playerThatMoved.toString());
winnerPlayerViewGroup.setVisibility(View.VISIBLE);
/**
* TODO 标记当前选手选择了哪行哪列
* 如果不是在没有选中的9个格子里面点击,将视为无效
* 另外,如果游戏已经结束,本次标记忽略
* @param row [0, 2]
* @param col [0, 2]
* @return 返回当前选手,如果点击无效为 null
*/
public Player mark(int row, int col)
Player playerThatMoved = null;
if(isValid(row, col)) // 判断当前点击格子是否有效
cells[row][col].setValue(currentTurn); // 标记该格子,当前棋手的符合(X/O)
playerThatMoved = currentTurn; // 记录当前下棋的人是谁
if(isWinningMoveByPlayer(currentTurn, row, col))//TODO 判断当前这个棋手下棋后,该棋手是否获胜,即游戏是否需要结束
state = GameState.FINISHED; //棋局的状态--结束
winner = currentTurn; // 当前棋手获胜
else
// 切换到另外一棋手,继续
flipCurrentTurn();
return playerThatMoved;
public Player getWinner()
return winner;
// 清空棋盘
private void clearCells()
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
cells[i][j] = new Cell();
// 判断当前点击是否有效
private boolean isValid(int row, int col)
if (state == GameState.FINISHED) // 看棋子有没有结束
return false;
else if (isCellValueAlreadySet(row, col)) // 看该格子是否之前已经下过
return false;
else
return true;
private boolean isCellValueAlreadySet(int row, int col)
return cells[row][col].getValue() != null;
/**
* TODO判断当前棋手下棋后---是否赢棋
* @param player 棋手
* @param currentRow 当前行
* @param currentCol 当前列
* @return TODO 如果当前行、当前列或者两条对角线为同一棋手,返回 true
*/
private boolean isWinningMoveByPlayer(Player player, int currentRow, int currentCol)
return (cells[currentRow][0].getValue() ==player
&& cells[currentRow][1].getValue() == player
&& cells[currentRow][2].getValue() == player // 3-行
|| cells[0][currentCol].getValue() == player
&& cells[1][currentCol].getValue() == player
&& cells[2][currentCol].getValue() == player // 3-列
|| currentRow == currentCol
&& cells[0][0].getValue() == player
&& cells[1][1].getValue() == player
&& cells[2][2].getValue() == player // 对角线
|| currentRow + currentCol == 2
&& cells[0][2].getValue() == player
&& cells[1][1].getValue() == player
&& cells[2][0].getValue() == player);
private void flipCurrentTurn()
currentTurn = currentTurn == X ? O :X;
/**
* TODO 开始一个新游戏,清除计分板和状态
*/
private void restart()
// 重置数据
clearCells();
winner = null;
currentTurn = X;
state = GameState.IN_PROGRESS;
// 重置 View
winnerPlayerViewGroup.setVisibility(View.GONE);
winnerPlayerLabel.setText("");
for (int i = 0; i < buttonGrid.getChildCount(); i++)
((Button) buttonGrid.getChildAt(i)).setText("");
一个文件--->MVC
适用场景:适合设计类页面,就大多数都是数据,没有太多的控制逻辑。把数据剥离出去就ok
在 MVC 中我们将把写在一个 class 里的代码进行拆分
数据(Model): 数据+对数据进行的操作(不依赖视图的操作)
视图(View): 不同的模式有不同的定义:xml+activity+fragment = View 合集
逻辑(Controller): view 和 model 的通信和交互。
注意:在 MVC 中,activity 是属于 Controller,在 MVP/MVVM 中是属于 View。
如下图所示,我们把原来在 ManiActivity 中的数据+对数据的操作部分抽离到了 model 模块中,而 MainActivity 中留下了对 View 部分 + View 和 Model 的交互。
Model
在 MVC 中,我们把数据部分(Cell/Player/GameState/以及对数据的操作Board)抽离出来,放到 model 模块
1. Cell.java
package com.example.jingziqi_mvc.model;
public class Cell
private Player value;
public Player getValue()
return value;
public void setValue(Player value)
this.value = value;
2. Player.java
package com.example.jingziqi_mvc.model;
/**
* X/O代表两个棋手
*/
public enum Player
X,
O
3. GamaState.java
package com.example.jingziqi_mvc.model;
public enum GameState
IN_PROGRESS,
FINISHED
4. Board.java
package com.example.jingziqi_mvc.model;
import static com.example.jingziqi_mvc.model.Player.X;
import static com.example.jingziqi_mvc.model.Player.O;
public class Board
// 总共定义 9 个格子
private final Cell[][] cells = new Cell[3][3];
private Player winner; // 记录结果,谁赢了
private GameState state; //记录当前的游戏状态
private Player currentTurn; // 现在是那位棋手在下---X/O
public Board()
restart();
/**
* TODO 开始一个新游戏,清除计分板和状态
*/
public void restart()
clearCells();
winner = null;
currentTurn = X;
state = GameState.IN_PROGRESS;
/**
* 清空棋盘上的X/O
*/
private void clearCells()
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
cells[i][j] = new Cell();
/**
* TODO 标记当前选手选择了哪行哪列
* 如果不是在没有选中的9个格子里面点击,将视为无效
* 另外,如果游戏已经结束,本次标记忽略
* @param row [0, 2]
* @param col [0, 2]
* @return 返回当前选手,如果点击无效为 null
*/
public Player mark(int row, int col)
Player playerThatMoved = null;
if(isValid(row, col)) // 判断当前点击格子是否有效
cells[row][col].setValue(currentTurn); // 标记该格子,当前棋手的符合(X/O)
playerThatMoved = currentTurn; // 记录当前下棋的人是谁
if(isWinningMoveByPlayer(currentTurn, row, col))//TODO 判断当前这个棋手下棋后,该棋手是否获胜,即游戏是否需要结束
state = GameState.FINISHED; //棋局的状态--结束
winner = currentTurn; // 当前棋手获胜
else
// 切换到另外一棋手,继续
flipCurrentTurn();
return playerThatMoved;
public Player getWinner()
return winner;
/**
* 判断当前点击是否有效
*/
private boolean isValid(int row, int col)
if (state == GameState.FINISHED) // 看棋子有没有结束
return false;
else if (isCellValueAlreadySet(row, col)) // 看该格子是否之前已经下过
return false;
else
return true;
private boolean isCellValueAlreadySet(int row, int col)
return cells[row][col].getValue() != null;
/**
* TODO判断当前棋手下棋后---是否赢棋
* @param player 棋手
* @param currentRow 当前行
* @param currentCol 当前列
* @return TODO 如果当前行、当前列或者两条对角线为同一棋手,返回 true
*/
private boolean isWinningMoveByPlayer(Player player, int currentRow, int currentCol)
return (cells[currentRow][0].getValue() ==player
&& cells[currentRow][1].getValue() == player
&& cells[currentRow][2].getValue() == player // 3-行
|| cells[0][currentCol].getValue() == player
&& cells[1][currentCol].getValue() == player
&& cells[2][currentCol].getValue() == player // 3-列
|| currentRow == currentCol
&& cells[0][0].getValue() == player
&& cells[1][1].getValue() == player
&& cells[2][2].getValue() == player // 对角线
|| currentRow + currentCol == 2
&& cells[0][2].getValue() == player
&& cells[1][1].getValue() == player
&& cells[2][0].getValue() == player);
/**
* 切换棋手
*/
private void flipCurrentTurn()
currentTurn = currentTurn == X ? O :X;
Controler
在 mvc 中 View 只有 xml 的内容
MainActivity.java 的内容如下:
package com.example.jingziqi_mvc.controller;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import com.example.jingziqi_mvc.R;
import com.example.jingziqi_mvc.model.Board;
import com.example.jingziqi_mvc.model.Player;
public class MainActivity extends AppCompatActivity
private static final String TAG = MainActivity.class.getName();
private Board model;
// View 部分
private ViewGroup buttonGrid;
private View winnerPlayerViewGroup;
private TextView winnerPlayerLabel;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
winnerPlayerLabel = findViewById(R.id.winnerPlayerLabel);
winnerPlayerViewGroup = findViewById(R.id.winnerPlayerViewGroup);
buttonGrid = findViewById(R.id.buttonGrid);
model = new Board();
@Override
public boolean onCreateOptionsMenu(Menu menu)
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_jingziqi, menu);
return true;
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item)
switch (item.getItemId())
case R.id.action_reset:
model.restart();//重置 Model(数据)
restartView(); //重置View
return true;
default:
return super.onOptionsItemSelected(item);
/**
* 重置 View
*/
private void restartView()
winnerPlayerViewGroup.setVisibility(View.GONE);
winnerPlayerLabel.setText("");
for (int i = 0; i < buttonGrid.getChildCount(); i++)
((Button) buttonGrid.getChildAt(i)).setText("");
/**
* 点击格子
*/
public void onCellClicked(View view)
Button button = (Button) view;
String tag = button.getTag().toString();
// 取到点击的格子的行/列
int row = Integer.parseInt(tag.substring(0, 1));
int col = Integer.parseInt(tag.substring(1, 2));
Log.i(TAG, "Click Row: [" + row + ", " + col + "]");
// TODO View 和 Model 的交互
Player playerThatMoved = model.mark(row, col);
if (playerThatMoved != null)
button.setText(playerThatMoved.toString());
if (model.getWinner() != null)
winnerPlayerLabel.setText(playerThatMoved.toString());
winnerPlayerViewGroup.setVisibility(View.VISIBLE);
MVC 对比 一个文件打天下
进步:抽离了 model,对数据进行单独封装
缺陷:controller(activity)权限太大,什么事情都能做(View 和 Model 的交互仍然保留在了 activity 中)。当我们的需求增加时,View 和 Model 的交互也会增加,此时activity 就会变得越来越大。
MVC--->MVP
数据(Model): 数据+对数据进行的操作(不依赖视图的操作)
视图(View): 不同的模式有不同的定义:xml+activity+fragment = View 合集
逻辑(Presenter):实现view 和 model 之间的交互。让 activity 完全成为 view 模块
注意:增加了接口,在 Activity 中实现,然后再在 presenter 里调用
Model
1. Cell.java
package com.example.jingziqi_mvc.model;
public class Cell
private Player value;
public Player getValue()
return value;
public void setValue(Player value)
this.value = value;
2. Player.java
package com.example.jingziqi_mvc.model;
/**
* X/O代表两个棋手
*/
public enum Player
X,
O
3. GamaState.java
package com.example.jingziqi_mvc.model;
public enum GameState
IN_PROGRESS,
FINISHED
4. Board.java
package com.example.jingziqi_mvp.model;
import static com.example.jingziqi_mvp.model.Player.O;
import static com.example.jingziqi_mvp.model.Player.X;
public class Board
// 总共定义 9 个格子
private final Cell[][] cells = new Cell[3][3];
private Player winner; // 记录结果,谁赢了
private GameState state; //记录当前的游戏状态
private Player currentTurn; // 现在是那位棋手在下---X/O
public Board()
restart();
/**
* TODO 开始一个新游戏,清除计分板和状态
*/
public void restart()
clearCells();
winner = null;
currentTurn = X;
state = GameState.IN_PROGRESS;
/**
* 清空棋盘上的X/O
*/
private void clearCells()
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
cells[i][j] = new Cell();
/**
* TODO 标记当前选手选择了哪行哪列
* 如果不是在没有选中的9个格子里面点击,将视为无效
* 另外,如果游戏已经结束,本次标记忽略
* @param row [0, 2]
* @param col [0, 2]
* @return 返回当前选手,如果点击无效为 null
*/
public Player mark(int row, int col)
Player playerThatMoved = null;
if(isValid(row, col)) // 判断当前点击格子是否有效
cells[row][col].setValue(currentTurn); // 标记该格子,当前棋手的符合(X/O)
playerThatMoved = currentTurn; // 记录当前下棋的人是谁
if(isWinningMoveByPlayer(currentTurn, row, col))//TODO 判断当前这个棋手下棋后,该棋手是否获胜,即游戏是否需要结束
state = GameState.FINISHED; //棋局的状态--结束
winner = currentTurn; // 当前棋手获胜
else
// 切换到另外一棋手,继续
flipCurrentTurn();
return playerThatMoved;
public Player getWinner()
return winner;
/**
* 判断当前点击是否有效
*/
private boolean isValid(int row, int col)
if (state == GameState.FINISHED) // 看棋子有没有结束
return false;
else if (isCellValueAlreadySet(row, col)) // 看该格子是否之前已经下过
return false;
else
return true;
private boolean isCellValueAlreadySet(int row, int col)
return cells[row][col].getValue() != null;
/**
* TODO判断当前棋手下棋后---是否赢棋
* @param player 棋手
* @param currentRow 当前行
* @param currentCol 当前列
* @return TODO 如果当前行、当前列或者两条对角线为同一棋手,返回 true
*/
private boolean isWinningMoveByPlayer(Player player, int currentRow, int currentCol)
return (cells[currentRow][0].getValue() ==player
&& cells[currentRow][1].getValue() == player
&& cells[currentRow][2].getValue() == player // 3-行
|| cells[0][currentCol].getValue() == player
&& cells[1][currentCol].getValue() == player
&& cells[2][currentCol].getValue() == player // 3-列
|| currentRow == currentCol
&& cells[0][0].getValue() == player
&& cells[1][1].getValue() == player
&& cells[2][2].getValue() == player // 对角线
|| currentRow + currentCol == 2
&& cells[0][2].getValue() == player
&& cells[1][1].getValue() == player
&& cells[2][0].getValue() == player);
/**
* 切换棋手
*/
private void flipCurrentTurn()
currentTurn = currentTurn == X ? O :X;
View
1. MainActivity.java
package com.example.jingziqi_mvp.view;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import com.example.jingziqi_mvp.R;
import com.example.jingziqi_mvp.presenter.JingziqiPresenter;
public class MainActivity extends AppCompatActivity implements JingziqiView
private static final String TAG = MainActivity.class.getName();
JingziqiPresenter presenter = new JingziqiPresenter(this);
// View 部分
private ViewGroup buttonGrid;
private View winnerPlayerViewGroup;
private TextView winnerPlayerLabel;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
winnerPlayerLabel = (TextView) findViewById(R.id.winnerPlayerLabel);
winnerPlayerViewGroup = findViewById(R.id.winnerPlayerViewGroup);
buttonGrid = (ViewGroup) findViewById(R.id.buttonGrid);
@Override
public boolean onCreateOptionsMenu(Menu menu)
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_jingziqi, menu);
return true;
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item)
switch (item.getItemId())
case R.id.action_reset:
presenter.onResetSelected();
return true;
default:
return super.onOptionsItemSelected(item);
public void onCellClicked(View view)
Button button = (Button) view;
String tag = button.getTag().toString();
// 取到点击的格子的行/列
int row = Integer.parseInt(tag.substring(0, 1));
int col = Integer.parseInt(tag.substring(1, 2));
Log.i(TAG, "Click Row: [" + row + ", " + col + "]");
// TODO View 和 Model 的交互抽离到 presenter 里了
presenter.onButtonSelected(row, col);
/**
* 展示获胜者
*/
@Override
public void showWinner(String winningPlayerDisplayLabel)
winnerPlayerLabel.setText(winningPlayerDisplayLabel);
winnerPlayerViewGroup.setVisibility(View.VISIBLE);
/**
* 把格子下面,展示谁获胜的内容清空
*/
@Override
public void clearWinnerDisplay()
winnerPlayerViewGroup.setVisibility(View.GONE);
winnerPlayerLabel.setText("");
/**
* 把格子清空
*/
@Override
public void clearButton()
for( int i = 0; i < buttonGrid.getChildCount(); i++ )
((Button) buttonGrid.getChildAt(i)).setText("");
/**
* 在格子上标记是那位棋手下的棋(即在格子上标记X/O)
*/
@Override
public void setButtonText(int row, int col, String text)
Button btn = (Button) buttonGrid.findViewWithTag("" + row + col); // 根据 tag 找到对应的 格子
if(btn != null)
btn.setText(text);
2. JingziqiView.java 是一个接口,在 MainActivity 里实现,在 Presenter 里完成调用
package com.example.jingziqi_mvp.view;
public interface JingziqiView
void showWinner(String winningPlayerDisplayLabel);
void clearWinnerDisplay();
void clearButton();
void setButtonText(int row, int col, String text);
Presenter
1. JingziqiPresenter.java
package com.example.jingziqi_mvp.presenter;
import android.view.View;
import com.example.jingziqi_mvp.model.Board;
import com.example.jingziqi_mvp.model.Player;
import com.example.jingziqi_mvp.view.JingziqiView;
public class JingziqiPresenter
private JingziqiView view;
private Board model;
public JingziqiPresenter(JingziqiView view)
this.view = view;
this.model = new Board();
public void onButtonSelected(int row, int col)
// TODO View 和 Model 的交互
Player playerThatMoved = model.mark(row, col);
if (playerThatMoved != null)
//button.setText(playerThatMoved.toString());
view.setButtonText(row, col, playerThatMoved.toString());
if (model.getWinner() != null)
// winnerPlayerLabel.setText(playerThatMoved.toString());
// winnerPlayerViewGroup.setVisibility(View.VISIBLE);
view.showWinner(playerThatMoved.toString());
public void onResetSelected()
model.restart();
view.clearWinnerDisplay();
view.clearButton();
MVP 相对 MVC
进步:activity 只剩下了 view,presenter 承担了 view 和 model 之间的交互,满足单一职责原则,视图数据逻辑是清晰的。
缺陷:引入了 interface,方法增多,增加一个方法要改多个地方。
MVP--->MVVM
数据(Model): 数据+对数据进行的操作(不依赖视图的操作)
视图(View): 不同的模式有不同的定义:xml+activity+fragment = View 合集
VM(viewmodel):将视图与数据进行绑定,使用 dataBinding 完成
viewBinding: 只能省略 findViewById, 不需要修改 xml
dataBinding: 除了 ViewBinding 的功能还能绑定 data,需要修改 xml
Model
model 模块仍然不变,与 MVC/MVP 的 Model 内容一致
View
MainActivity.java
package com.example.jingziqi_mvvm.view;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import com.example.jingziqi_mvvm.R;
import com.example.jingziqi_mvvm.databinding.ActivityMainBinding;
import com.example.jingziqi_mvvm.viewmodel.JingziqiViewModel;
public class MainActivity extends AppCompatActivity
JingziqiViewModel viewModel = new JingziqiViewModel();
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
// TODO dataBinding 的使用
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.setViewModel(viewModel);
@Override
public boolean onCreateOptionsMenu(Menu menu)
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_jingziqi, menu);
return true;
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item)
switch (item.getItemId())
case R.id.action_reset:
viewModel.onResetSelect();
return true;
default:
return super.onOptionsItemSelected(item);
ViewModel
package com.example.jingziqi_mvvm.viewmodel;
import androidx.databinding.ObservableArrayMap;
import androidx.databinding.ObservableField;
import com.example.jingziqi_mvvm.model.Board;
import com.example.jingziqi_mvvm.model.Player;
public class JingziqiViewModel
private Board model;
public final ObservableArrayMap<String, String> cells = new ObservableArrayMap<>();
public final ObservableField<String> winner = new ObservableField<>();
public JingziqiViewModel()
model = new Board();
public void onResetSelect()
model.restart();
winner.set(null);
cells.clear();
public void onClickedCellAt(int row, int col)
Player playerThatMoved = model.mark(row, col);
if (playerThatMoved != null)
cells.put("" + row + col, playerThatMoved.toString());
winner.set(model.getWinner() == null ? null : model.getWinner().toString());
MVVM 使用 dataBinding 将数据绑定在 View 上。所以我们要引入 dataBinding。在 app 级的 build.gradle 里添加
使用 dataBinding,我们还得修改 xml ,将整个布局包裹住 <layout>....</layout > 中,并且引入数据部分。注意,<data></data> 里的 type 所指示的内容,就是我们 VM 模块的 JingziqiViewModel.java 。并且在该 xml 里会用到 JingziqiViewModel的方法。
使用 JingziqiViewModel 里的 onClickedCellAt()方法,以及 cells 成员变量。
通过上述内容,我们把对格子内容的改变(数据)直接绑定在 view 上,这样代码就更简介了。
6大设计原则
单一职责原则、开闭原则、里氏替换原则、接口隔离原则、依赖倒置原则、迪米特原则
单一职责原则:一个 class 完成一件事情。
开闭原则:对扩展、继承开发,对修改关闭。
里氏替换原则:不能改变基类的逻辑(比如,继承父类的run() 方法,哪里里面的实现逻辑就不能是 fly)
依赖倒置原则:两个模块之间通信,通过接口实现(不依赖实现,只依赖接口)
完整demo
下面给出上面 4 种写法的完整代码。可以查看具体的 xml ,以及更多的代码细节。
链接:https://pan.baidu.com/s/1qxJQ-6iIDgm7AX-6CmGuuQ
提取码:2b77
Android MVC MVP MVVM
MVP模型
View主要是Activity,Fragment
MVP和MVC的差别
1.Model和View不再直接通信,通过中间层Presenter来实现。
2.Activity的功能被简化,不再充当控制器,主要负责View层面的工作。
MVPPresenter
public class MVPPresenter { private IMVPView view; private MVPModel model; public MVPPresenter(IMVPView view) { this.view=view; model=new MVPModel(); } public void getData(String accountName) { model.getAccountData(accountName, new ResultCallback() { @Override public void onSuccess(Account account) { view.showSuccessPage(account); } @Override public void onFailure() { view.showFailurePage(); } }); } }
IMVPView
public interface IMVPView { String getUserInput(); void showSuccessPage(Account account); void showFailurePage(); }
MVPModel
public class MVPModel { public void getAccountData(String accountName, ResultCallback cb) { Random random=new Random(); boolean isSuccess=random.nextBoolean(); if (isSuccess) { Account account = new Account(); account.setName(accountName); account.setLevel(100); cb.onSuccess(account); } else { cb.onFailure(); } } }
MVPActivity
public class MVPActivity extends AppCompatActivity implements IMVPView{ private TextView tvResult; private EditText etAccount; private MVPPresenter presenter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.test); tvResult = findViewById(R.id.tvResult); etAccount = findViewById(R.id.etAccount); presenter = new MVPPresenter(this); } public void ButtonClick(View view) { presenter.getData(getUserInput()); } @Override public String getUserInput() { return etAccount.getText().toString(); } @Override public void showSuccessPage(Account account) { tvResult.setText("用户账号: "+account.getName()+ " | "+"用户等级: "+account.getLevel()); } @Override public void showFailurePage() { tvResult.setText("获取数据失败"); } }
以上是关于Android---MVC/MVP/MVVM的演进的主要内容,如果未能解决你的问题,请参考以下文章