一、如何判斷點(diǎn)擊的是哪個(gè)方向鍵按鈕

在上篇教程中我們實(shí)現(xiàn)了左邊的三角形按鈕效果,本篇教程我們將左、上、右、下四個(gè)三角形按鈕都一起實(shí)現(xiàn)了。
能做出一個(gè)來(lái),另外三個(gè)應(yīng)該不難了吧?但實(shí)際并非怎么簡(jiǎn)單哦。
首先我們來(lái)解決一下上節(jié)課遺留的一個(gè)問(wèn)題,如何判斷當(dāng)前手指點(diǎn)擊的是哪個(gè)三角形按鈕?


這個(gè)需要用解析幾何大法來(lái)解決。
假設(shè)我們的控件是邊長(zhǎng)為1的正方形,建立平面直角坐標(biāo)系(注意:計(jì)算機(jī)中坐標(biāo)系原點(diǎn)在左上角哦),如下圖:
photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動(dòng)軟件開(kāi)發(fā)培訓(xùn),網(wǎng)站設(shè)計(jì)培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)
正方形的對(duì)角線將控件分成了4個(gè)三角形區(qū)域,也就是我們的4個(gè)方向鍵按鈕。

據(jù)上圖可知:
左上角到右下角對(duì)角線的方程為y=x;
    y>x的區(qū)域包含左和下三角形
    y<x的區(qū)域包含右和上三角形

右上角到左下角的對(duì)角線方程為y=-x+1;
    y>1-x的區(qū)域包含右和下三角形
    y<1-x的區(qū)域包含左和上三角形

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

以上是按照邊長(zhǎng)為1的正方形得到的結(jié)論,但實(shí)際中,我們的控件不一定是正方形,邊長(zhǎng)也不是1,而是一個(gè)不確定的矩形,這該怎么辦呢?
這就需要經(jīng)過(guò)一定的轉(zhuǎn)換,將普通的矩形轉(zhuǎn)換為邊長(zhǎng)為1的正方形。
這個(gè)轉(zhuǎn)變也簡(jiǎn)單,如下:
設(shè)畫(huà)布上被觸摸到的點(diǎn)的坐標(biāo)為(x,y),則:

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

我們將畫(huà)布上被觸摸的點(diǎn)的橫縱坐標(biāo)分別除以畫(huà)布的寬和高,這樣就得到了一個(gè)相對(duì)坐標(biāo),而這個(gè)相對(duì)坐標(biāo)的取值一定在0到1之間。這樣就相當(dāng)于把一個(gè)不確定的矩形簡(jiǎn)化成了一個(gè)邊長(zhǎng)為1的正方形處理。

二、程序代碼

有了上面的了解,下面就可以寫(xiě)代碼了。由于有4個(gè)三角形按鈕,而每個(gè)按鈕又有兩種狀態(tài),代碼會(huì)稍微長(zhǎng)點(diǎn)。但每個(gè)按鈕的邏輯都是一樣的,都是按哪個(gè)那個(gè)高亮,不按時(shí)都恢復(fù)正常狀態(tài)。代碼中的注釋比較詳細(xì),相信大家如果看了前面的教程內(nèi)容,看這個(gè)應(yīng)該問(wèn)題不大。唯一需要注意的是每次調(diào)用invalidate方法重繪界面時(shí),是需要對(duì)整個(gè)畫(huà)布都重繪的,而不能只重繪一個(gè)三角形。

這里我們需要先引入一個(gè)表示方向的枚舉:Direction

photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動(dòng)軟件開(kāi)發(fā)培訓(xùn),網(wǎng)站設(shè)計(jì)培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)

package net.chengyujia.happysnake;/**
 * 用來(lái)表示方向的枚舉
 * Created by ChengYuJia on 2016/8/21. */public enum Direction {    //none表示沒(méi)有方向鍵按下    none,    //左    left,    //上    up,    //右    right,    //下    down;
}

photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動(dòng)軟件開(kāi)發(fā)培訓(xùn),網(wǎng)站設(shè)計(jì)培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)

 
下面是當(dāng)前DirectionKeys的完整代碼:

photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動(dòng)軟件開(kāi)發(fā)培訓(xùn),網(wǎng)站設(shè)計(jì)培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)

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 {    //左三角形按壓時(shí)的顏色(較亮)
    private int leftPressedColor = 0xFFFF0000;    //左三角形正常顯示的顏色(較暗)
    private int leftNormalColor = 0xFFAA0000;    //上三角形按壓時(shí)的顏色(較亮)
    private int upPressedColor = 0xFF00FF00;    //上三角形正常顯示的顏色(較暗)
    private int upNormalColor = 0xFF00AA00;    //右三角形按壓時(shí)的顏色(較亮)
    private int rightPressedColor = 0xFF0000FF;    //右三角形正常顯示的顏色(較暗)
    private int rightNormalColor = 0xFF0000AA;    //下三角形按壓時(shí)的顏色(較亮)
    private int downPressedColor = 0xFFFFFF00;    //下三角形正常顯示的顏色(較暗)
    private int downNormalColor = 0xFFAAAA00;    //畫(huà)筆
    private Paint paint = new Paint();    //畫(huà)左三角形的路徑
    private Path pathLeft = new Path();    //畫(huà)上三角形的路徑
    private Path pathUp = new Path();    //畫(huà)右三角形的路徑
    private Path pathRight = new Path();    //畫(huà)下三角形的路徑
    private Path pathDown = new Path();    //畫(huà)布的寬
    private int width;    //畫(huà)布的高
    private int height;    //初始化方法是否執(zhí)行過(guò),確保初始化方法只執(zhí)行一次。
    private boolean initDone = false;    //記錄當(dāng)前哪個(gè)方向鍵被按下
    private Direction currentDirection = Direction.none;    //只有一個(gè)參數(shù)的構(gòu)造方法是我們?cè)诔绦蛑型ㄟ^(guò)“new”關(guān)鍵字創(chuàng)建實(shí)例時(shí)調(diào)用。
    public DirectionKeys(Context context) {        super(context);
    }    //有兩個(gè)參數(shù)的構(gòu)造方法是系統(tǒng)在XML布局文件中創(chuàng)建實(shí)例時(shí)調(diào)用。
    public DirectionKeys(Context context, AttributeSet attrs) {        super(context, attrs);
    }    //初始化方法
    private void init(Canvas canvas) {        /*獲取畫(huà)布的長(zhǎng)和寬*/
        width = canvas.getWidth();
        height = canvas.getHeight();        /*
        (小提示:在計(jì)算機(jī)中一般都是將左上角作為坐標(biāo)原點(diǎn)的)
        畫(huà)布上四個(gè)頂點(diǎn)和中心點(diǎn)的坐標(biāo)如下:
        左上點(diǎn) 0,0
        左下點(diǎn) 0,height
        右上點(diǎn) width,0
        右下點(diǎn) width,height
        中心點(diǎn) width/2,height/2        */

        /*設(shè)置左三角形的路徑數(shù)據(jù)*/
        //從畫(huà)布左上點(diǎn)開(kāi)始
        pathLeft.moveTo(0, 0);        //畫(huà)直線到畫(huà)布中心點(diǎn)
        pathLeft.lineTo(width / 2, height / 2);        //再畫(huà)直線到畫(huà)布左下點(diǎn)
        pathLeft.lineTo(0, height);        //自動(dòng)閉合圖形。從最后一個(gè)點(diǎn)(左下點(diǎn))畫(huà)直線到第一個(gè)點(diǎn)(左上點(diǎn))。        pathLeft.close();        /*同理設(shè)置上三角形的路徑數(shù)據(jù)*/
        pathUp.moveTo(0, 0);
        pathUp.lineTo(width / 2, height / 2);
        pathUp.lineTo(width, 0);
        pathUp.close();        /*同理設(shè)置右三角形的路徑數(shù)據(jù)*/
        pathRight.moveTo(width, 0);
        pathRight.lineTo(width / 2, height / 2);
        pathRight.lineTo(width, height);
        pathRight.close();        /*同理設(shè)置下三角形的路徑數(shù)據(jù)*/
        pathDown.moveTo(width, height);
        pathDown.lineTo(width / 2, height / 2);
        pathDown.lineTo(0, height);
        pathDown.close();
    }    //畫(huà)路徑的共用方法
    private void drawPath(Path path, int color, Canvas canvas) {        //設(shè)置畫(huà)筆顏色        paint.setColor(color);        //用畫(huà)筆在畫(huà)布上按照路徑數(shù)據(jù)畫(huà)出圖形        canvas.drawPath(path, paint);
    }    //畫(huà)左三角正常顏色
    private void drawLeftNormal(Canvas canvas) {
        drawPath(pathLeft, leftNormalColor, canvas);
    }    //畫(huà)左三角按壓顏色(高亮)
    private void drawLeftPressed(Canvas canvas) {
        drawPath(pathLeft, leftPressedColor, canvas);
    }    //畫(huà)上三角正常顏色
    private void drawUpNormal(Canvas canvas) {
        drawPath(pathUp, upNormalColor, canvas);
    }    //畫(huà)上三角按壓顏色(高亮)
    private void drawUpPressed(Canvas canvas) {
        drawPath(pathUp, upPressedColor, canvas);
    }    //畫(huà)右三角正常顏色
    private void drawRightNormal(Canvas canvas) {
        drawPath(pathRight, rightNormalColor, canvas);
    }    //畫(huà)右三角按壓顏色(高亮)
    private void drawRightPressed(Canvas canvas) {
        drawPath(pathRight, rightPressedColor, canvas);
    }    //畫(huà)下三角正常顏色
    private void drawDownNormal(Canvas canvas) {
        drawPath(pathDown, downNormalColor, canvas);
    }    //畫(huà)下三角按壓顏色(高亮)
    private void drawDownPressed(Canvas canvas) {
        drawPath(pathDown, downPressedColor, canvas);
    }    //所有按鈕恢復(fù)正常顏色
    private void reset(Canvas canvas) {
        drawLeftNormal(canvas);
        drawUpNormal(canvas);
        drawRightNormal(canvas);
        drawDownNormal(canvas);
    }    //當(dāng)按左三角時(shí),左三角高亮,其它正常。
    private void drawWhenLeftPressed(Canvas canvas) {
        drawLeftPressed(canvas);
        drawUpNormal(canvas);
        drawRightNormal(canvas);
        drawDownNormal(canvas);
    }    //當(dāng)按上三角時(shí),上三角高亮,其它正常。
    private void drawWhenUpPressed(Canvas canvas) {
        drawLeftNormal(canvas);
        drawUpPressed(canvas);
        drawRightNormal(canvas);
        drawDownNormal(canvas);
    }    //當(dāng)按右三角時(shí),右三角高亮,其它正常。
    private void drawWhenRightPressed(Canvas canvas) {
        drawLeftNormal(canvas);
        drawUpNormal(canvas);
        drawRightPressed(canvas);
        drawDownNormal(canvas);
    }    //當(dāng)按下三角時(shí),下三角高亮,其它正常。
    private void drawWhenDownPressed(Canvas canvas) {
        drawLeftNormal(canvas);
        drawUpNormal(canvas);
        drawRightNormal(canvas);
        drawDownPressed(canvas);
    }    /**
     * 通過(guò)重寫(xiě)父類的onDraw方法來(lái)繪制我們需要的圖形
     * 該方法會(huì)在控件第一次顯示時(shí)被系統(tǒng)調(diào)用,并在之后每次調(diào)用invalidate方法后被系統(tǒng)調(diào)用。
     *
     * @param canvas 這里的canvas是系統(tǒng)提供的一塊矩形畫(huà)布,我們要做的就是在這塊畫(huà)布上畫(huà)我們想要的東西。     */
    @Override    protected void onDraw(Canvas canvas) {        if (!initDone) {
            init(canvas);            //確保初始化方法只執(zhí)行一次
            initDone = true;
        }        //按下不同的方向鍵,繪制不同的界面效果。
        switch (currentDirection) {            case left://按下左鍵時(shí)                drawWhenLeftPressed(canvas);                break;            case up://按下上鍵時(shí)                drawWhenUpPressed(canvas);                break;            case right://按下右鍵時(shí)                drawWhenRightPressed(canvas);                break;            case down://按下下鍵時(shí)                drawWhenDownPressed(canvas);                break;            default://其它情況                reset(canvas);
        }
    }    /**
     * 當(dāng)用戶觸摸到該控件時(shí),系統(tǒng)通過(guò)該方法告訴控件“你被摸了,要不要有反應(yīng)???有反應(yīng)返回true,沒(méi)反應(yīng)返回false。”
     * 控件說(shuō)“那要看是怎么摸的了。如果是按下,我就將對(duì)應(yīng)的三角形按鍵高亮顯示;如果是抬起,我就將所有的三角形按鍵恢復(fù)成正常的顏色。其它情況我就不反應(yīng)了,摸就摸吧。”
     *
     * @param event 系統(tǒng)給我們傳遞的觸摸事件參數(shù)
     * @return 如果該觸摸事件被我們處理了返回true,反之返回false。     */
    @Override    public boolean onTouchEvent(MotionEvent event) {        //獲取當(dāng)前的觸摸動(dòng)作
        int action = event.getAction();        if (action == MotionEvent.ACTION_DOWN) {//按下            //獲取觸摸點(diǎn)的坐標(biāo)
            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;
        }
    }    //根據(jù)坐標(biāo)判斷哪個(gè)三角形方向鍵被按下
    private Direction getDirection(float x, float y) {        //經(jīng)過(guò)坐標(biāo)轉(zhuǎn)換,統(tǒng)一成邊長(zhǎng)為1的正方形處理。對(duì)角線分割形成的4個(gè)區(qū)域,分別代表4個(gè)方向。
        float relativeX = x / width;//0<=relativeX<=1
        float relativeY = y / height;//0<=relativeY<=1
        /*
        注意:原點(diǎn)是左上角。
        左上角到右下角對(duì)角線方程為y=x;
            則:
            y>x的區(qū)域包含左和下三角形
            y<x的區(qū)域包含右和上三角形

        左下角到右上角對(duì)角線方程為y=-x+1;
            則:
            y>1-x的區(qū)域包含右和下三角形
            y<1-x的區(qū)域包含左和上三角形

        綜上可得:
            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;
            }
        }
    }
}

photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動(dòng)軟件開(kāi)發(fā)培訓(xùn),網(wǎng)站設(shè)計(jì)培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)

 

三、運(yùn)行效果

沒(méi)有點(diǎn)擊時(shí)的效果:

photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動(dòng)軟件開(kāi)發(fā)培訓(xùn),網(wǎng)站設(shè)計(jì)培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)

 

點(diǎn)擊左鍵時(shí)的效果:

photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動(dòng)軟件開(kāi)發(fā)培訓(xùn),網(wǎng)站設(shè)計(jì)培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)

 

點(diǎn)擊上建時(shí)的效果:

photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動(dòng)軟件開(kāi)發(fā)培訓(xùn),網(wǎng)站設(shè)計(jì)培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)

 

點(diǎn)擊右鍵時(shí)的效果:

photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動(dòng)軟件開(kāi)發(fā)培訓(xùn),網(wǎng)站設(shè)計(jì)培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)

 

點(diǎn)擊下鍵時(shí)的效果:

photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動(dòng)軟件開(kāi)發(fā)培訓(xùn),網(wǎng)站設(shè)計(jì)培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)

 

到這里,我們的自定義方向鍵的4個(gè)背景三角形已經(jīng)做好了,而且實(shí)現(xiàn)了點(diǎn)擊變色的按鈕效果。

后面我們會(huì)在這4個(gè)三角形上都畫(huà)上相應(yīng)的箭頭,同樣也有點(diǎn)擊變色的效果哦。敬請(qǐng)期待o(^▽^)o