Android快乐贪吃蛇游戏实战项目开发教程-05虚拟方向键(四)四个三角形按钮

该系列教程概述与目录:http://www.cnblogs.com/chengyujia/p/5787111.html

一、如何判断点击的是哪个方向键按钮

在上篇教程中我们实现了左边的三角形按钮效果,本篇教程我们将左、上、右、下四个三角形按钮都一起实现了。
能做出一个来,另外三个应该不难了吧?但实际并非怎么简单哦。
首先我们来解决一下上节课遗留的一个问题,如何判断当前手指点击的是哪个三角形按钮?


这个需要用解析几何大法来解决。
假设我们的控件是边长为1的正方形,建立平面直角坐标系(注意:计算机中坐标系原点在左上角哦),如下图:

正方形的对角线将控件分成了4个三角形区域,也就是我们的4个方向键按钮。

据上图可知:
左上角到右下角对角线的方程为y=x;
    y>x的区域包含左和下三角形
    y<x的区域包含右和上三角形

右上角到左下角的对角线方程为y=-x+1;
    y>1-x的区域包含右和下三角形
    y<1-x的区域包含左和上三角形

综上可得:
    y>x 且 y<1-x 表示左三角
    y<x 且 y<1-x 表示上三角
    y<x 且 y>1-x 表示右三角
    y>x 且 y>1-x 表示下三角

以上是按照边长为1的正方形得到的结论,但实际中,我们的控件不一定是正方形,边长也不是1,而是一个不确定的矩形,这该怎么办呢?
这就需要经过一定的转换,将普通的矩形转换为边长为1的正方形。
这个转变也简单,如下:
设画布上被触摸到的点的坐标为(x,y),则:

    float relativeX = x / width;//0<=relativeX<=1
    float relativeY = y / height;//0<=relativeY<=1

我们将画布上被触摸的点的横纵坐标分别除以画布的宽和高,这样就得到了一个相对坐标,而这个相对坐标的取值一定在0到1之间。这样就相当于把一个不确定的矩形简化成了一个边长为1的正方形处理。

二、程序代码

有了上面的了解,下面就可以写代码了。由于有4个三角形按钮,而每个按钮又有两种状态,代码会稍微长点。但每个按钮的逻辑都是一样的,都是按哪个那个高亮,不按时都恢复正常状态。代码中的注释比较详细,相信大家如果看了前面的教程内容,看这个应该问题不大。唯一需要注意的是每次调用invalidate方法重绘界面时,是需要对整个画布都重绘的,而不能只重绘一个三角形。

这里我们需要先引入一个表示方向的枚举:Direction

package net.chengyujia.happysnake;

/**
 * 用来表示方向的枚举
 * Created by ChengYuJia on 2016/8/21.
 */
public enum Direction {
    //none表示没有方向键按下
    none,
    //
    left,
    //
    up,
    //
    right,
    //
    down;
}

 
下面是当前DirectionKeys的完整代码:

package net.chengyujia.happysnake;

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

/**
 * 屏幕上的虚拟方向键
 * Created by ChengYuJia on 2016/8/19.
 */
public class DirectionKeys extends View {
    //左三角形按压时的颜色(较亮)
    private int leftPressedColor = 0xFFFF0000;
    //左三角形正常显示的颜色(较暗)
    private int leftNormalColor = 0xFFAA0000;
    //上三角形按压时的颜色(较亮)
    private int upPressedColor = 0xFF00FF00;
    //上三角形正常显示的颜色(较暗)
    private int upNormalColor = 0xFF00AA00;
    //右三角形按压时的颜色(较亮)
    private int rightPressedColor = 0xFF0000FF;
    //右三角形正常显示的颜色(较暗)
    private int rightNormalColor = 0xFF0000AA;
    //下三角形按压时的颜色(较亮)
    private int downPressedColor = 0xFFFFFF00;
    //下三角形正常显示的颜色(较暗)
    private int downNormalColor = 0xFFAAAA00;

    //画笔
    private Paint paint = new Paint();
    //画左三角形的路径
    private Path pathLeft = new Path();
    //画上三角形的路径
    private Path pathUp = new Path();
    //画右三角形的路径
    private Path pathRight = new Path();
    //画下三角形的路径
    private Path pathDown = new Path();

    //画布的宽
    private int width;
    //画布的高
    private int height;
    //初始化方法是否执行过,确保初始化方法只执行一次。
    private boolean initDone = false;
    //记录当前哪个方向键被按下
    private Direction currentDirection = Direction.none;

    //只有一个参数的构造方法是我们在程序中通过“new”关键字创建实例时调用。
    public DirectionKeys(Context context) {
        super(context);
    }

    //有两个参数的构造方法是系统在XML布局文件中创建实例时调用。
    public DirectionKeys(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    //初始化方法
    private void init(Canvas canvas) {
        /*获取画布的长和宽*/
        width = canvas.getWidth();
        height = canvas.getHeight();
        /*
        (小提示:在计算机中一般都是将左上角作为坐标原点的)
        画布上四个顶点和中心点的坐标如下:
        左上点 0,0
        左下点 0,height
        右上点 width,0
        右下点 width,height
        中心点 width/2,height/2
        */

        /*设置左三角形的路径数据*/
        //从画布左上点开始
        pathLeft.moveTo(0, 0);
        //画直线到画布中心点
        pathLeft.lineTo(width / 2, height / 2);
        //再画直线到画布左下点
        pathLeft.lineTo(0, height);
        //自动闭合图形。从最后一个点(左下点)画直线到第一个点(左上点)。
        pathLeft.close();

        /*同理设置上三角形的路径数据*/
        pathUp.moveTo(0, 0);
        pathUp.lineTo(width / 2, height / 2);
        pathUp.lineTo(width, 0);
        pathUp.close();

        /*同理设置右三角形的路径数据*/
        pathRight.moveTo(width, 0);
        pathRight.lineTo(width / 2, height / 2);
        pathRight.lineTo(width, height);
        pathRight.close();

        /*同理设置下三角形的路径数据*/
        pathDown.moveTo(width, height);
        pathDown.lineTo(width / 2, height / 2);
        pathDown.lineTo(0, height);
        pathDown.close();
    }

    //画路径的共用方法
    private void drawPath(Path path, int color, Canvas canvas) {
        //设置画笔颜色
        paint.setColor(color);
        //用画笔在画布上按照路径数据画出图形
        canvas.drawPath(path, paint);
    }

    //画左三角正常颜色
    private void drawLeftNormal(Canvas canvas) {
        drawPath(pathLeft, leftNormalColor, canvas);
    }

    //画左三角按压颜色(高亮)
    private void drawLeftPressed(Canvas canvas) {
        drawPath(pathLeft, leftPressedColor, canvas);
    }

    //画上三角正常颜色
    private void drawUpNormal(Canvas canvas) {
        drawPath(pathUp, upNormalColor, canvas);
    }

    //画上三角按压颜色(高亮)
    private void drawUpPressed(Canvas canvas) {
        drawPath(pathUp, upPressedColor, canvas);
    }

    //画右三角正常颜色
    private void drawRightNormal(Canvas canvas) {
        drawPath(pathRight, rightNormalColor, canvas);
    }

    //画右三角按压颜色(高亮)
    private void drawRightPressed(Canvas canvas) {
        drawPath(pathRight, rightPressedColor, canvas);
    }

    //画下三角正常颜色
    private void drawDownNormal(Canvas canvas) {
        drawPath(pathDown, downNormalColor, canvas);
    }

    //画下三角按压颜色(高亮)
    private void drawDownPressed(Canvas canvas) {
        drawPath(pathDown, downPressedColor, canvas);
    }

    //所有按钮恢复正常颜色
    private void reset(Canvas canvas) {
        drawLeftNormal(canvas);
        drawUpNormal(canvas);
        drawRightNormal(canvas);
        drawDownNormal(canvas);
    }

    //当按左三角时,左三角高亮,其它正常。
    private void drawWhenLeftPressed(Canvas canvas) {
        drawLeftPressed(canvas);
        drawUpNormal(canvas);
        drawRightNormal(canvas);
        drawDownNormal(canvas);
    }

    //当按上三角时,上三角高亮,其它正常。
    private void drawWhenUpPressed(Canvas canvas) {
        drawLeftNormal(canvas);
        drawUpPressed(canvas);
        drawRightNormal(canvas);
        drawDownNormal(canvas);
    }

    //当按右三角时,右三角高亮,其它正常。
    private void drawWhenRightPressed(Canvas canvas) {
        drawLeftNormal(canvas);
        drawUpNormal(canvas);
        drawRightPressed(canvas);
        drawDownNormal(canvas);
    }

    //当按下三角时,下三角高亮,其它正常。
    private void drawWhenDownPressed(Canvas canvas) {
        drawLeftNormal(canvas);
        drawUpNormal(canvas);
        drawRightNormal(canvas);
        drawDownPressed(canvas);
    }

    /**
     * 通过重写父类的onDraw方法来绘制我们需要的图形
     * 该方法会在控件第一次显示时被系统调用,并在之后每次调用invalidate方法后被系统调用。
     *
     * @param canvas 这里的canvas是系统提供的一块矩形画布,我们要做的就是在这块画布上画我们想要的东西。
     */
    @Override
    protected void onDraw(Canvas canvas) {
        if (!initDone) {
            init(canvas);
            //确保初始化方法只执行一次
            initDone = true;
        }

        //按下不同的方向键,绘制不同的界面效果。
        switch (currentDirection) {
            case left://按下左键时
                drawWhenLeftPressed(canvas);
                break;
            case up://按下上键时
                drawWhenUpPressed(canvas);
                break;
            case right://按下右键时
                drawWhenRightPressed(canvas);
                break;
            case down://按下下键时
                drawWhenDownPressed(canvas);
                break;
            default://其它情况
                reset(canvas);
        }
    }

    /**
     * 当用户触摸到该控件时,系统通过该方法告诉控件“你被摸了,要不要有反应啊?有反应返回true,没反应返回false。”
     * 控件说“那要看是怎么摸的了。如果是按下,我就将对应的三角形按键高亮显示;如果是抬起,我就将所有的三角形按键恢复成正常的颜色。其它情况我就不反应了,摸就摸吧。”
     *
     * @param event 系统给我们传递的触摸事件参数
     * @return 如果该触摸事件被我们处理了返回true,反之返回false。
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //获取当前的触摸动作
        int action = event.getAction();
        if (action == MotionEvent.ACTION_DOWN) {//按下
            //获取触摸点的坐标
            float x = event.getX();
            float y = event.getY();
            currentDirection = getDirection(x, y);
            invalidate();//重绘
            return true;
        } else if (action == MotionEvent.ACTION_UP) {//抬起
            currentDirection = Direction.none;
            invalidate();//重绘
            return true;
        } else {//其它不处理
            return false;
        }
    }

    //根据坐标判断哪个三角形方向键被按下
    private Direction getDirection(float x, float y) {
        //经过坐标转换,统一成边长为1的正方形处理。对角线分割形成的4个区域,分别代表4个方向。
        float relativeX = x / width;//0<=relativeX<=1
        float relativeY = y / height;//0<=relativeY<=1
        /*
        注意:原点是左上角。
        左上角到右下角对角线方程为y=x;
            则:
            y>x的区域包含左和下三角形
            y<x的区域包含右和上三角形

        左下角到右上角对角线方程为y=-x+1;
            则:
            y>1-x的区域包含右和下三角形
            y<1-x的区域包含左和上三角形

        综上可得:
            y>x 且 y<1-x 表示左三角
            y<x 且 y<1-x 表示上三角
            y<x 且 y>1-x 表示右三角
            y>x 且 y>1-x 表示下三角
         */

        if (relativeY > relativeX) {//左和下
            if (relativeY < 1 - relativeX) {//
                return Direction.left;
            } else {//
                return Direction.down;
            }
        } else {//上和右
            if (relativeY < 1 - relativeX) {//
                return Direction.up;
            } else {//
                return Direction.right;
            }
        }
    }
}

 

三、运行效果

没有点击时的效果:

 

点击左键时的效果:

 

点击上建时的效果:

 

点击右键时的效果:

 

点击下键时的效果:

 

到这里,我们的自定义方向键的4个背景三角形已经做好了,而且实现了点击变色的按钮效果。

后面我们会在这4个三角形上都画上相应的箭头,同样也有点击变色的效果哦。敬请期待o(^▽^)o

posted @ 2016-08-22 15:28  成宇佳  阅读(2375)  评论(0编辑  收藏  举报