編輯:關於android開發
本系列教程概述與目錄:http://www.cnblogs.com/chengyujia/p/5787111.html
本系列教程項目源碼GitHub地址:https://github.com/jackchengyujia/HappySnake
在上篇教程中,我們畫了4個背景三角形,並且實現了點擊變色的按鈕效果。
在本篇教程中,我們將在這4個三角形上分別繪制表示方向的箭頭,並且讓箭頭也有點擊變色的效果。
我們先看一下運行效果,有一個直觀的了解,然後再從代碼的角度分析和講解。
沒有觸摸時的效果:

按左鍵時的效果:

按上鍵時的效果:

按右鍵時的效果:

按下鍵時的效果:

我們前面畫三角形是用Path對象來告訴程序我們要畫的圖形,這裡畫箭頭也是這樣的。
先定義4個Path對象,分別用來表示4個箭頭的路徑數據。
//畫左箭頭的路徑
private Path pathLeftArrow = new Path();
//畫上箭頭的路徑
private Path pathUpArrow = new Path();
//畫右箭頭的路徑
private Path pathRightArrow = new Path();
//畫下箭頭的路徑
private Path pathDownArrow = new Path();
然後在初始化方法中設置各自的路徑數據。
從上面的運行截圖上可以看到,每個箭頭由一個三角形和一個矩形組成。
同樣,在程序中也是先設計一個三角形,然後拼接一個矩形,這樣一個箭頭的path數據就設置好了。
這裡有兩點需要注意:
1.箭頭上每個轉折點的坐標要計算准確。
2.我們只需要設置左箭頭和上箭頭的詳細數據,右箭頭和下箭頭可以通過旋轉來得到。
箭頭相關的初始化程序我都放在了initArrow()方法中了,該方法代碼如下:
//初始化與箭頭相關的數據。
private void initArrow() {
/*
這裡我們規定:
1.每個箭頭由一個三角形和一個矩形組成;
2.每個箭頭整體的寬和高分別為畫布寬和高的1/4;
3.每個箭頭的三角形部分和矩形部分在寬(左右箭頭)或高(上下箭頭)上各占一半。
4.每個三角形矩形部分的高(左右箭頭)或寬(上下箭頭)為箭頭整體高或寬的一半。
*/
//每個箭頭整體的寬和高。
final int arrowWidth = width / 4;
final int arrowHeight = height / 4;
/*
設計左箭頭
先設計三角形部分,再設計矩形部分。
*/
//設置箭頭尖的坐標。這裡我們規定讓左鍵頭尖的橫坐標為畫布寬的1/16;縱坐標為畫布高度的1/2,也就是垂直方向居中。
int arrowStartX = width / 16;
int arrowStartY = height / 2;
//設計箭頭的三角形部分
int arrowX = arrowStartX;
int arrowY = arrowStartY;
//從箭頭尖開始
pathLeftArrow.moveTo(arrowX, arrowY);
//直線移動到三角形的上頂點
pathLeftArrow.lineTo(arrowX += arrowWidth / 2, arrowY -= arrowHeight / 2);
//然後直線移動到三角形的下頂點
pathLeftArrow.lineTo(arrowX, arrowY += arrowHeight);
//閉合三角形
pathLeftArrow.close();
//重置坐標,准備設計矩形部分。在計算機中畫矩形是最容易的,只要知道左上角和右下角兩點的坐標即可。
arrowX = arrowStartX;
arrowY = arrowStartY;
//矩形左邊界到畫布左邊界的距離(左上角橫坐標)
float left = arrowX += arrowWidth / 2;
//矩形上邊界到畫布上邊界的距離(左上角縱坐標)
float top = arrowY -= arrowHeight / 4;
//矩形右邊界到畫布左邊界的距離(右下角橫坐標)
float right = arrowX += arrowWidth / 2;
//矩形下邊界到畫布上邊界的距離(右下角縱坐標)
float bottom = arrowY += arrowHeight / 2;
//在已有三角形的基礎上增加一個矩形。最後一個參數是一個枚舉,只有兩個值,Direction.CW表示順時針,Direction.CCW表示逆時針。在我們這裡選那個都行,沒有影響。
pathLeftArrow.addRect(left, top, right, bottom, Path.Direction.CW);
/*
設計右箭頭
由於右箭頭與左箭頭是中心對稱圖形,只要把左箭頭旋轉180度即可。這裡我們使用矩陣來做旋轉。
*/
Matrix matrix = new Matrix();
matrix.setRotate(180, width / 2, height / 2);
pathLeftArrow.transform(matrix, pathRightArrow);
/*
設計上箭頭
和左箭頭一樣,先設計三角形部分,再設計矩形部分。
*/
//設置箭頭尖的坐標。這裡我們規定讓上鍵頭尖的橫坐標為畫布寬的1/2,也就是水平方向居中;縱坐標為畫布高度的1/16。
arrowStartX = width / 2;
arrowStartY = height / 16;
//設計三角形部分
arrowX = arrowStartX;
arrowY = arrowStartY;
pathUpArrow.moveTo(arrowX, arrowY);
pathUpArrow.lineTo(arrowX += arrowWidth / 2, arrowY += arrowHeight / 2);
pathUpArrow.lineTo(arrowX - +arrowWidth, arrowY);
pathUpArrow.close();
//設計矩形部分
arrowX = arrowStartX;
arrowY = arrowStartY;
pathUpArrow.addRect(arrowX -= arrowWidth / 4, arrowY += arrowHeight / 2, arrowX += arrowWidth / 2, arrowY += arrowHeight / 2, Path.Direction.CW);
/*
設計下箭頭
同理,將上箭頭旋轉180度得到下箭頭。
*/
pathUpArrow.transform(matrix, pathDownArrow);
}
注釋比較詳細,看過之章節的朋友,看這個應該問題不大。
下面是目前為止DirectionKeys類的全部代碼:
package net.chengyujia.happysnake;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Matrix;
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 int arrowPressedColor = 0xFFFFFFFF;
//箭頭正常顯示的顏色(較暗)
private int arrowNormalColor = 0xFFAAAAAA;
//畫筆
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 Path pathLeftArrow = new Path();
//畫上箭頭的路徑
private Path pathUpArrow = new Path();
//畫右箭頭的路徑
private Path pathRightArrow = new Path();
//畫下箭頭的路徑
private Path pathDownArrow = 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) {
//抗鋸齒。讓圖形邊界的鋸齒模糊,看起來圖形邊界將更加光滑。
paint.setAntiAlias(true);
/*獲取畫布的長和寬*/
width = canvas.getWidth();
height = canvas.getHeight();
/*
(小提示:在計算機中一般都是將左上角作為坐標原點的)
畫布上四個頂點和中心點的坐標如下:
左上點 0,0
左下點 0,height
右上點 width,0
右下點 width,height
中心點 width/2,height/2
*/
initBackgroundTriangle();
initArrow();
}
//初始化與背景三角形相關的數據。
private void initBackgroundTriangle() {
/*設置左三角形的路徑數據*/
//從畫布左上點開始
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 initArrow() {
/*
這裡我們規定:
1.每個箭頭由一個三角形和一個矩形組成;
2.每個箭頭整體的寬和高分別為畫布寬和高的1/4;
3.每個箭頭的三角形部分和矩形部分在寬(左右箭頭)或高(上下箭頭)上各占一半。
4.每個三角形矩形部分的高(左右箭頭)或寬(上下箭頭)為箭頭整體高或寬的一半。
*/
//每個箭頭整體的寬和高。
final int arrowWidth = width / 4;
final int arrowHeight = height / 4;
/*
設計左箭頭
先設計三角形部分,再設計矩形部分。
*/
//設置箭頭尖的坐標。這裡我們規定讓左鍵頭尖的橫坐標為畫布寬的1/16;縱坐標為畫布高度的1/2,也就是垂直方向居中。
int arrowStartX = width / 16;
int arrowStartY = height / 2;
//設計箭頭的三角形部分
int arrowX = arrowStartX;
int arrowY = arrowStartY;
//從箭頭尖開始
pathLeftArrow.moveTo(arrowX, arrowY);
//直線移動到三角形的上頂點
pathLeftArrow.lineTo(arrowX += arrowWidth / 2, arrowY -= arrowHeight / 2);
//然後直線移動到三角形的下頂點
pathLeftArrow.lineTo(arrowX, arrowY += arrowHeight);
//閉合三角形
pathLeftArrow.close();
//重置坐標,准備設計矩形部分。在計算機中畫矩形是最容易的,只要知道左上角和右下角兩點的坐標即可。
arrowX = arrowStartX;
arrowY = arrowStartY;
//矩形左邊界到畫布左邊界的距離(左上角橫坐標)
float left = arrowX += arrowWidth / 2;
//矩形上邊界到畫布上邊界的距離(左上角縱坐標)
float top = arrowY -= arrowHeight / 4;
//矩形右邊界到畫布左邊界的距離(右下角橫坐標)
float right = arrowX += arrowWidth / 2;
//矩形下邊界到畫布上邊界的距離(右下角縱坐標)
float bottom = arrowY += arrowHeight / 2;
//在已有三角形的基礎上增加一個矩形。最後一個參數是一個枚舉,只有兩個值,Direction.CW表示順時針,Direction.CCW表示逆時針。在我們這裡選那個都行,沒有影響。
pathLeftArrow.addRect(left, top, right, bottom, Path.Direction.CW);
/*
設計右箭頭
由於右箭頭與左箭頭是中心對稱圖形,只要把左箭頭旋轉180度即可。這裡我們使用矩陣來做旋轉。
*/
Matrix matrix = new Matrix();
matrix.setRotate(180, width / 2, height / 2);
pathLeftArrow.transform(matrix, pathRightArrow);
/*
設計上箭頭
和左箭頭一樣,先設計三角形部分,再設計矩形部分。
*/
//設置箭頭尖的坐標。這裡我們規定讓上鍵頭尖的橫坐標為畫布寬的1/2,也就是水平方向居中;縱坐標為畫布高度的1/16。
arrowStartX = width / 2;
arrowStartY = height / 16;
//設計三角形部分
arrowX = arrowStartX;
arrowY = arrowStartY;
pathUpArrow.moveTo(arrowX, arrowY);
pathUpArrow.lineTo(arrowX += arrowWidth / 2, arrowY += arrowHeight / 2);
pathUpArrow.lineTo(arrowX - +arrowWidth, arrowY);
pathUpArrow.close();
//設計矩形部分
arrowX = arrowStartX;
arrowY = arrowStartY;
pathUpArrow.addRect(arrowX -= arrowWidth / 4, arrowY += arrowHeight / 2, arrowX += arrowWidth / 2, arrowY += arrowHeight / 2, Path.Direction.CW);
/*
設計下箭頭
同理,將上箭頭旋轉180度得到下箭頭。
*/
pathUpArrow.transform(matrix, pathDownArrow);
}
//畫路徑的共用方法
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);
drawPath(pathLeftArrow, arrowNormalColor, canvas);
}
//畫左方向鍵按壓狀態(高亮)
private void drawLeftPressed(Canvas canvas) {
drawPath(pathLeft, leftPressedColor, canvas);
drawPath(pathLeftArrow, arrowPressedColor, canvas);
}
//畫上方向鍵正常狀態
private void drawUpNormal(Canvas canvas) {
drawPath(pathUp, upNormalColor, canvas);
drawPath(pathUpArrow, arrowNormalColor, canvas);
}
//畫上方向鍵按壓狀態(高亮)
private void drawUpPressed(Canvas canvas) {
drawPath(pathUp, upPressedColor, canvas);
drawPath(pathUpArrow, arrowPressedColor, canvas);
}
//畫右方向鍵正常狀態
private void drawRightNormal(Canvas canvas) {
drawPath(pathRight, rightNormalColor, canvas);
drawPath(pathRightArrow, arrowNormalColor, canvas);
}
//畫右方向鍵按壓狀態(高亮)
private void drawRightPressed(Canvas canvas) {
drawPath(pathRight, rightPressedColor, canvas);
drawPath(pathRightArrow, arrowPressedColor, canvas);
}
//畫下方向鍵正常狀態
private void drawDownNormal(Canvas canvas) {
drawPath(pathDown, downNormalColor, canvas);
drawPath(pathDownArrow, arrowNormalColor, canvas);
}
//畫下方向鍵按壓狀態(高亮)
private void drawDownPressed(Canvas canvas) {
drawPath(pathDown, downPressedColor, canvas);
drawPath(pathDownArrow, arrowPressedColor, 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;
}
}
}
}
相比上節的代碼,這裡主要是增加了initArrow方法,另外把背景三角形相關的初始化代碼放到了initBackgroundTriangle方法裡,這樣看起來會更有條理些。
好了本節先到這裡,我們下節繼續。:)
Android自定義控件5--輪播圖廣告ViewPager基本實現,androidviewpager
Android自定義控件5--輪播圖廣告ViewPager基本實現,androidviewpager本文地址:http://www.cnblogs.com/wuyudon
git版本控制工具(二)----本地版本庫的常用操作,git----
git版本控制工具(二)----本地版本庫的常用操作,git----【正文】 在上一章節中,我們學習了關於Git最基本的用法,包括安裝Git、創建版本庫,以及提交本地代碼
Intent屬性詳解二 Action、Category,intentcategory
Intent屬性詳解二 Action、Category,intentcategory先看效果圖: 1、Action:該activity可以執行的動作 該標識用來說明這
Android開發實踐:Android.mk模板
Android開發實踐:Android.mk模板 關於Android NDK開發的文章已經比較多了,我的博客中也分享了很多NDK開發相關經驗和技巧,今天簡單寫了一個 An