Android的MVC设计

前言

大家都知道Android的设计是基于MVC的规范的。对于MVC大家并没有”陌生”,而且一般来说,这个是一个广泛使用的规范。用游戏来讲述MVC是最好的,因为对于Control层的理解比较直观:control就是游戏的控制,上下左右,技能ABC,游戏的时间,事件;View是对绘制UI,场景的Face,人物Body(不理会Model是什么);Model是实体,具有动作。

MVC模型

Created with Raphaël 2.1.0 Control Control Model Model View View Update Model Notify Control update View User action

大致的流向,用户的输入改变Model,并且改变View,View显示动作的Action给Control,Model的改变Notify控制。

重要的是:

  • 模型和视图要严格的分离(不能有交互)

贪吃蛇游戏的简易mvc代码

在结构上安装model,view,control来分类,代码整体的包结构如下:
这里写图片描述

编写View

注意的是view是独立的模块,不能关联control和mode,l那么就有了以下代码。说明,view是游戏的UI,它只关心绘制的点,和场景,不关心蛇,游戏。所以,我们可以独立这个view来测试,譬如测试绘制蛇,绘制食物;

package com.owant.mvcsnakegame.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

import com.owant.mvcsnakegame.model.Location;

import java.util.ArrayList;

/**
 * Created by owant on 16/11/2016.
 */
public class SnakeGameView extends View {

    public static final int screenX = 30;
    public static final int screenY = 30;

    private int dx;
    private int dy;
    public ArrayList<Location> body;
    public Location food;

    private Paint mPaint;

    public SnakeGameView(Context context) {
        this(context, null, 0);
    }

    public SnakeGameView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SnakeGameView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        mPaint = new Paint();
        mPaint.setColor(Color.RED);
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(2);

//        testDrawSnakeBody();

//        testFood();

    }

    private void testFood() {
        food = new Location();
        food.rawY = 3;
        food.rawX = 5;
    }

    private void testDrawSnakeBody() {
        body = new ArrayList<>();
        Location location = new Location();
        location.rawX = 0;
        location.rawY = 0;
        body.add(location);

        Location location1 = new Location();
        location1.rawX = 0;
        location1.rawY = 1;
        body.add(location1);

        Location location2 = new Location();
        location2.rawX = 0;
        location2.rawY = 2;
        body.add(location2);

        Location location3 = new Location();
        location3.rawX = 0;
        location3.rawY = 3;
        body.add(location3);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        drawScreen(canvas);


        drawSnake(canvas);

        drawFood(canvas);

    }

    /**
     * draw the game screen
     *
     * @param canvas
     */
    private void drawScreen(Canvas canvas) {
        mPaint.setColor(Color.RED);

        //x
        for (int i = 1; i < screenX; i++) {
            canvas.drawLine(dx * i, 0, dx * i, getHeight(), mPaint);
        }

        //y
        for (int i = 1; i < screenY; i++) {
            canvas.drawLine(0, dy * i, getWidth(), dy * i, mPaint);
        }
    }

    /**
     * draw the snake body
     *
     * @param canvas
     */
    private void drawSnake(Canvas canvas) {
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(Color.RED);
        if (body != null) {
            for (Location b : body) {
                canvas.drawRect(b.rawX * dx, b.rawY * dy, b.rawX * dx + dx, b.rawY * dy + dy, mPaint);
            }
        }
    }

    /**
     * draw the food
     *
     * @param canvas
     */
    private void drawFood(Canvas canvas) {
        if (food != null) {
            mPaint.setColor(Color.GREEN);
            canvas.drawRect(food.rawX*dx, food.rawY*dx, food.rawX*dx + dx, food.rawY*dx + dy, mPaint);
        }
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        dx = getWidth() / screenX;
        dy = getHeight() / screenY;
    }

    public ArrayList<Location> getBody() {
        return body;
    }

    public void setBody(ArrayList<Location> bodys) {
        this.body = bodys;
    }

    @Override
    public void invalidate() {
        super.invalidate();
    }

    public Location getFood() {
        return food;
    }

    public void setFood(Location food) {
        this.food = food;
    }

    public void clearFood() {
        this.food = null;
    }

}
Model的编写

Snake。说明,蛇对自己的特性负责,不理会control和view;

package com.owant.mvcsnakegame.model;

import java.util.ArrayList;

/**
 * Created by owant on 16/11/2016.
 */
public class Snake {

    public boolean live = true;

    public ArrayList<Location> body;
    public int direction = Direction.down;
    //30ms
    public int speek = 300;

    public Snake() {
        body = new ArrayList<>();
        init();
    }

    private void init() {
        Location location = new Location();
        location.rawX = 0;
        location.rawY = 0;
        body.add(location);

        Location location1 = new Location();
        location1.rawX = 0;
        location1.rawY = 1;
        body.add(location1);

        Location location2 = new Location();
        location2.rawX = 0;
        location2.rawY = 2;
        body.add(location2);

        Location location3 = new Location();
        location3.rawX = 0;
        location3.rawY = 3;
    }

    public int getDirection() {
        return direction;
    }

    public void setDirection(int direction) {
        this.direction = direction;
    }

    public void growUp() {
    }

    public Location getHeard() {
        return body.get(body.size() - 1);
    }

    public void move() {

        Location heard = getHeard();
        Location next = new Location();

        if (direction == Direction.up) {//向上
            next.rawX = heard.rawX;
            next.rawY = heard.rawY - 1;
        } else if (direction == Direction.down) {//向下
            next.rawX = heard.rawX;
            next.rawY = heard.rawY + 1;
        } else if (direction == Direction.left) {//左边
            next.rawX = heard.rawX - 1;
            next.rawY = heard.rawY;
        } else if (direction == Direction.right) {//右
            next.rawX = heard.rawX + 1;
            next.rawY = heard.rawY;
        }

        body.remove(0);
        body.add(next);
    }


}

Location 坐标点,代码省略。

Control

控制层,需要管理View和Model,它应该包括游戏的时间控制,游戏的输入。代码如下:

package com.owant.mvcsnakegame.control;

import com.owant.mvcsnakegame.model.Direction;
import com.owant.mvcsnakegame.model.Snake;
import com.owant.mvcsnakegame.view.SnakeGameView;

/**
 * Created by owant on 16/11/2016.
 */
public class GameControl implements Runnable {

    //control view
    SnakeGameView view;
    Snake snake;

    public GameControl(SnakeGameView view, Snake snake) {
        this.view = view;
        this.snake = snake;
    }

    public void right() {
        if (snake.getDirection() == Direction.down) {
            snake.setDirection(Direction.left);
        } else if (snake.getDirection() == Direction.up) {
            snake.setDirection(Direction.right);
        } else if (snake.getDirection() == Direction.left) {
            snake.setDirection(Direction.up);
        } else if (snake.getDirection() == Direction.right) {
            snake.setDirection(Direction.down);
        }
    }

    public void left() {
        if (snake.getDirection() == Direction.left) {
            snake.setDirection(Direction.down);
        } else if (snake.getDirection() == Direction.right) {
            snake.setDirection(Direction.up);
        } else if (snake.getDirection() == Direction.up) {
            snake.setDirection(Direction.left);
        } else if (snake.getDirection() == Direction.down) {
            snake.setDirection(Direction.right);
        }
    }

    @Override
    public void run() {
        do {
            try {
                Thread.sleep(snake.speek);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            snake.move();

            view.post(new Runnable() {
                @Override
                public void run() {
                    view.invalidate();
                }
            });
        } while (snake.live);
    }
}

Activity

至于为何有Activity内,因为Android的MVC设计,Control落在了Activity上,也可以独立为一层特殊的Control,因为GameControl已经实现了所有的控制功能。Activity只是程序的入口,初始化实体,视图,接受输入传递给GameControl就行。

package com.owant.mvcsnakegame;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.KeyEvent;

import com.owant.mvcsnakegame.control.GameControl;
import com.owant.mvcsnakegame.model.Snake;
import com.owant.mvcsnakegame.view.SnakeGameView;

public class MainActivity extends AppCompatActivity {


    GameControl control;
    Snake snake;
    SnakeGameView snakeGameView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        snake = new Snake();

        snakeGameView = (SnakeGameView) findViewById(R.id.main_snake_view);
        snakeGameView.setBody(snake.body);

        control = new GameControl(snakeGameView, snake);
        new Thread(control).start();

    }


    @Override
    protected void onResume() {
        super.onResume();
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        System.out.println(keyCode);
        if (keyCode == KeyEvent.KEYCODE_A) {
            control.left();
        } else if (keyCode == KeyEvent.KEYCODE_D) {
            control.right();
        }
        return super.onKeyDown(keyCode, event);
    }

}

大致对于MVC的框架有了一定了解了吧。

总结

mvc是一种框架,不是设计模式。框架是很灵活的,可以变动,主要是对model,view,control的分类,而且如何关联到Android的代码中去。注意的是:

  • model和view是独立分开的。不要在view里面进行model和control的操作。

  • 大多数的APP的control大多是loadData

一些错误和有异议的问题

  • 我在网上看了一些人的mvc,发现他们的view层居然出现model和control,这是很错误的。譬如:
    这里写图片描述

    • 在ListView的Adapter中写点击事件;
    • View出现Control和刷新,这个不能说错误,由于View自己刷新是Android自身提供的,而且代码写起来简易,可以考虑View自己刷新;
    • 在游戏中时间轴应该在Control或者是游戏层,不要在View层;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值