Android Canvas 和Paint的用法
阿新 • • 發佈:2019-01-10
首先,介紹的是Canvas的基本方法
方法簽名 | 簡要說明 |
---|---|
drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint) | 繪製弧 |
drawBitmap(Bitmap bitmap, Rect src, Rect dst,Paint paint) | 在指定點繪製從源點陣圖中"挖取"的一塊 |
drawBitmap(Bitmap bitmap, float left, flost top, Paint paint) | 在指定點繪製點陣圖 |
drawCircle(float cx, float cy, float radius, Paint paint) | 在指定點繪製一個圓形 |
drawLine(float startX, float startY, float stopX, float stopY, Paint paint) | 繪製一條線 |
drawLines(float[] pts, int offset, int count, Paint paint) | 繪製多條線 |
drawOval(RectF oval, Paint paint) | 繪製橢圓 |
drawPath(Path path, Paint paint) | 沿著指定Path繪製任意形狀 |
drawPoint(float x, float y, Paint paint) | 繪製一個點 |
drawPoints(float[] pts, int offset, int count, Paint paint) | 繪製多個點 |
drawRect(float left, float top, float right, float bottom, Paint paint) | 繪製矩形 |
drawRoundRect(RectF rect, float rx, float ry, Paint paint) | 繪製圓角矩形 |
drawText(String text, int start ,int end, Paint paint) | 繪製字串 |
drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) | 沿著路徑繪製字串 |
clipRect(float left, float top, float right, float bottom) | 剪下一個矩形區域 |
clipRegion(Region region) | 剪下指定區域 |
>rotate(float degress, float px, float py): 對Canvas執行旋轉變換.
>scale(float sx, float sy, float px, float py): 對Canvas執行縮放變換.
>skew(float sx, float sy): 對Canvas執行傾斜變換.
>translate(float dx, float dy): 移動Canvas.向右移動dx距離(dx為負數即向左移動);向下移動dy距離(dy為負數即向上移動).
Paint代表了Canvas上的畫筆,因此Paint類主要用於設定繪製風格,包括畫筆顏色.畫筆筆觸粗細,填充風格等.
Paint提供瞭如下所示的方法:
方法簽名 | 簡要說明 |
---|---|
setARGB(int a, int r, int g, int b)/setColor(int color) | 設定顏色 |
setAlpha(int a ) | 設定透明度 |
setAntiAlias(boolean aa) | 設定是否抗鋸齒 |
setColor(int color) | 設定顏色 |
setPahtEffect(PathEffect cffect) | 設定繪製路徑時的路徑效果 |
setShader(Shader shader) | 設定畫筆的填充效果 |
setShadowLayer(float radius, float dx, float dy, int color) | 設定陰影 |
setStrokeWidth(float width) | 設定畫筆的筆觸寬度 |
setStrokeJoin(Paint.Join join) | 設定畫筆轉彎處的連線風格 |
setStyle(Paint.Style style) | 設定Paint的填充風格 |
setTextAlign(Paint.Align align) | 設定繪製文字時的文字的對齊方式 |
setTextSize(float textSize) | 設定繪製文字時的文字大小 |
下面的MyView重寫了onDraw(Canvas)方法,繪製了基本的幾何圖形.
package com.example.canvastest.view;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;
public class MyView extends View {
public MyView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyView(Context context) {
super(context);
}
/**
* 重寫該方法,進行繪圖
*/
@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 把整張畫布繪製成白色
canvas.drawColor(Color.WHITE);
// 建立畫筆
Paint paint = new Paint();
// 去鋸齒
paint.setAntiAlias(true);
// 設定畫筆顏色
paint.setColor(Color.BLUE);
// 設定畫筆樣式
paint.setStyle(Paint.Style.STROKE);
// 設定畫筆的寬度
paint.setStrokeWidth(3);
// 繪製圓形
canvas.drawCircle(40, 40, 30, paint);
// 繪製正方形
canvas.drawRect(10, 80, 70, 140, paint);
// 繪製矩形
canvas.drawRect(10, 150, 70, 190, paint);
//
RectF rectF1 = new RectF(10, 200, 70, 230);
// 繪製圓角矩形
canvas.drawRoundRect(rectF1, 15, 15, paint);
//
RectF rectF2 = new RectF(10, 240, 70, 270);
// 繪製橢圓
canvas.drawOval(rectF2, paint);
// 定義一個Path物件,封閉成一個三角形
Path path1 = new Path();
// 設定起點
path1.moveTo(10, 340);
// 線路1
path1.lineTo(70, 340);
// 線路2
path1.lineTo(40, 290);
path1.close();
// 根據Path進行繪製,繪製三角形
canvas.drawPath(path1, paint);
// 定義一個Path物件,繪製一個五角形
Path path2 = new Path();
path2.moveTo(26, 360);
path2.lineTo(54, 360);
path2.lineTo(70, 392);
path2.lineTo(40, 420);
path2.lineTo(10, 392);
path2.close();
// 根據Path進行繪製,繪製三角形
canvas.drawPath(path2, paint);
// -------------設定填充風格後繪製---------------------------
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.RED);
// 繪製圓形
canvas.drawCircle(120, 40, 30, paint);
// 繪製正方形
canvas.drawRect(90, 80, 150, 140, paint);
// 繪製矩形
canvas.drawRect(90, 200, 150, 190, paint);
// 繪製圓角矩形
RectF rectF3 = new RectF(90, 200, 150, 230);
canvas.drawRoundRect(rectF3, 15, 15, paint);
// 繪製橢圓
RectF rectF4 = new RectF(90, 240, 150, 270);
canvas.drawOval(rectF4, paint);
// 繪製三角形
Path path3 = new Path();
path3.moveTo(90, 340);
path3.lineTo(150, 340);
path3.lineTo(120, 290);
path3.close();
canvas.drawPath(path3, paint);
// 繪製五角形
Path path4 = new Path();
path4.moveTo(106, 360);
path4.lineTo(134, 360);
path4.lineTo(150, 392);
path4.lineTo(120, 420);
path4.lineTo(90, 392);
path4.close();
canvas.drawPath(path4, paint);
// -------------設定漸變器後繪製------------------
// 為paint設定漸變器
Shader mShader = new LinearGradient(0, 0, 40, 60, new int[] {
Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW }, null,
Shader.TileMode.REPEAT);
paint.setShader(mShader);
//設定陰影
paint.setShadowLayer(45, 10, 10, Color.GRAY);
// 繪製圓形
canvas.drawCircle(200, 40, 30, paint);
// 繪製正方形
canvas.drawRect(170, 80, 230, 140, paint);
// 繪製矩形
canvas.drawRect(170, 150, 230, 190, paint);
// 繪製圓角矩形
RectF rectF5 = new RectF(170, 200, 230, 230);
canvas.drawRoundRect(rectF5, 15, 15, paint);
// 繪製橢圓
RectF rectF6 = new RectF(170, 240, 230, 270);
canvas.drawOval(rectF6, paint);
// 繪製三角形
Path path6 = new Path();
path6.moveTo(170, 340);
path6.lineTo(230, 340);
path6.lineTo(200, 290);
path6.close();
canvas.drawPath(path6, paint);
// 繪製五角形
Path path5 = new Path();
path5.moveTo(186, 360);
path5.lineTo(214, 360);
path5.lineTo(230, 392);
path5.lineTo(200, 420);
path5.lineTo(170, 392);
path5.close();
canvas.drawPath(path5, paint);
//----------設定字元大小後繪製-----------------
paint.setTextSize(24);
paint.setShader(null);
//繪製7個字串
canvas.drawText("圓形", 240, 50, paint);
canvas.drawText("正方形", 240, 120, paint);
canvas.drawText("矩形", 240, 175, paint);
canvas.drawText("圓角矩形", 240, 220, paint);
canvas.drawText("橢圓形", 240, 260, paint);
canvas.drawText("三角形", 240, 325, paint);
canvas.drawText("五角形", 240, 390, paint);
}
}
Path類: Android提供的Path是一個非常有用的類,它可以預先在View上將N個點連成一條"路徑",然後呼叫Canvas的drawPath(Path path, Paint paint)即可沿著路徑繪製圖形.實際上Android還為路徑繪製提供了PathEffect來定義繪製效果,PathEffect包含了如下子類(每個子類代表一種繪製效果):
Corner PathEffect
Discrete PathEffect
Dash PathEffect
Path Dash PathEffect
Compose PathEffect
Sum PathEffect
這些繪製效果使用語言來表述總顯得有些空洞,下面通過一個程式來了解一下繪製效果.接下來的程式繪製7條路徑,分別示範了不適用效果和使用上面6種效果的示意.
package com.example.canvastest.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ComposePathEffect;
import android.graphics.CornerPathEffect;
import android.graphics.DashPathEffect;
import android.graphics.DiscretePathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathDashPathEffect;
import android.graphics.PathEffect;
import android.graphics.SumPathEffect;
import android.util.AttributeSet;
import android.view.View;
/**
* 使用Path繪製路徑
* @author Administrator
*
*/
public class MyPathView extends View {
private float phase;
private PathEffect[] pathEffects = new PathEffect[7];
private int[] colors;
private Path path;
private Paint paint;
public MyPathView(Context context) {
this(context, null);
}
public MyPathView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyPathView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(4);
// 建立並初始化Path
path = new Path();
path.moveTo(0, 0);
for (int i = 0; i < 15; i++) {
// 生成15個點,隨機生成他們的Y座標,並將它們練成一條Path
path.lineTo(i * 20, (float) Math.random() * 60);
}
// 初始化7個顏色
colors = new int[] { Color.BLACK, Color.BLUE, Color.CYAN, Color.GREEN,
Color.MAGENTA, Color.RED, Color.YELLOW };
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
///將背景填充為白色
canvas.drawColor(Color.WHITE);
//-------下面開始初始化7種路徑效果--------
//不使用路徑效果
pathEffects[0] = null;
//使用CornerPathEffect路徑效果//角路徑效應
pathEffects[1] = new CornerPathEffect(10);
//使用DiscretePathEffect路徑效果//離散路徑效應
pathEffects[2] = new DiscretePathEffect(3.0f, 5.0f);
//DashPathEffect//短跑路徑效應
pathEffects[3] = new DashPathEffect(new float[]{20, 10, 5, 10}, phase);
//PathDashPathEffect//短跑的路徑路徑效應
Path p = new Path();
p.addRect(0, 0, 8, 8, Path.Direction.CCW);
pathEffects[4] = new PathDashPathEffect(p, 12, phase, PathDashPathEffect.Style.ROTATE);
//ComposePathEffect//組成的路徑效應
pathEffects[5] = new ComposePathEffect(pathEffects[2], pathEffects[4]);
//SumPathEffect//總和路徑效應
pathEffects[6] = new SumPathEffect(pathEffects[4], pathEffects[3]);
//將畫布移動到(8,8)出繪製
canvas.translate(8, 8);
//依次使用7種不同路徑效果,7種顏色來繪製路徑
for (int i = 0; i < pathEffects.length; i++) {
paint.setPathEffect(pathEffects[i]);
paint.setColor(colors[i]);
canvas.drawPath(path, paint);
canvas.translate(0, 60);
}
//改變phase值,形成動畫效果
phase += 1;
invalidate();
}
}
除此之外,Android的Canvas還提供了一個DrawTextOnPath(String text,Path path, float hOffset, float vOffset, Paint paint)方法,該方法可以沿著Path繪製文字,其中hOffset引數指定水平偏移,vOffset引數指定垂直偏移.
package com.example.canvastest.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
/**
* 使用drawTextOnPaht,沿著Path(路徑)繪製文字
*
* @author Administrator
*/
public class MyPathTextView extends View {
final String DRAW_STR = "繪製文字";
private Path[] paths = new Path[3];
Paint paint;
public MyPathTextView(Context context) {
this(context, null);
}
public MyPathTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyPathTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
paths[0] = new Path();
paths[0].moveTo(0, 0);
for (int i = 0; i < 7; i++) {
// 生成7個點,隨機生成它們的y座標,並將它們連成一條path
paths[0].lineTo(1 * 30, (float) Math.random() * 30);
}
paths[1] = new Path();
RectF rectf = new RectF(0, 0, 200, 120);
paths[1].addOval(rectf, Path.Direction.CCW);
paths[2] = new Path();
paths[2].addArc(rectf, 60, 180);
// 初始化畫筆
paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.CYAN);
paint.setStrokeWidth(1);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.WHITE);
canvas.translate(40, 40);
// 設定從右邊開始繪製(右對齊)
paint.setTextAlign(Paint.Align.RIGHT);
paint.setTextSize(20);
// 繪製路徑
paint.setStyle(Paint.Style.STROKE);
canvas.drawPath(paths[0], paint);
// 沿著路徑繪製一段文字
paint.setStyle(Paint.Style.FILL);
canvas.drawTextOnPath(DRAW_STR, paths[0], -8, 20, paint);
// 對canvas進行座標變換:畫布下移60
canvas.translate(0, 60);
// 繪製路徑
paint.setStyle(Paint.Style.STROKE);
canvas.drawPath(paths[1], paint);
// 沿著路徑繪製一段文字
paint.setStyle(Paint.Style.FILL);
canvas.drawTextOnPath(DRAW_STR, paths[1], -20, 20, paint);
// 對canvas進行座標變換:畫布下移120
canvas.translate(0, 120);
// 繪製路徑
paint.setStyle(Paint.Style.STROKE);
canvas.drawPath(paths[2], paint);
// 沿著路徑繪製一段文字
paint.setStyle(Paint.Style.FILL);
canvas.drawTextOnPath(DRAW_STR, paths[2], -10, 20, paint);
}
}
下面是一個使用雙緩衝機制實現的畫面板例子
package com.example.canvastest.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class DrawView extends View {
float preX;
float preY;
private Paint paint = null;
final int VIEW_WIDTH = 320;
final int VIEW_HEIGHT = 480;
// 定義一個記憶體中的圖片,該圖片將作為緩衝區
Bitmap cacheBitmap = null;
// 定義cachebitmap上的canvas物件
Canvas cacheCanvas = null;
Path path = null;
public DrawView(Context context) {
this(context, null);
}
public DrawView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DrawView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// 設定畫筆的顏色
paint = new Paint(Paint.DITHER_FLAG);
paint.setColor(Color.RED);
// 設定畫筆的風格
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(1);
// 抗鋸齒
paint.setAntiAlias(true);
paint.setDither(true);
// 建立一個與該View相同大小的緩衝區
cacheBitmap = Bitmap.createBitmap(VIEW_WIDTH, VIEW_HEIGHT,
Bitmap.Config.ARGB_8888);
// 設定cacheCanvas將會繪製到記憶體中的cacheBitmap上
cacheCanvas = new Canvas();
path = new Path();
cacheCanvas.setBitmap(cacheBitmap);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
path.moveTo(x, y);
preX = x;
preY = x;
break;
case MotionEvent.ACTION_MOVE:
path.quadTo(preX, preY, x, y);
preX = x;
preY = y;
break;
case MotionEvent.ACTION_UP:
cacheCanvas.drawPath(path, paint);
path.reset();
break;
}
invalidate();
// 返回true表明處理方法已經處理該事件
return true;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
// 將該bitmap繪製到該View元件上
canvas.drawBitmap(cacheBitmap, 0, 0, paint);
// 沿著path繪製
canvas.drawPath(path, paint);
}
}
一個彈球遊戲例子
package com.example.canvastest;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnKeyListener;
import android.view.Window;
import android.view.WindowManager;
public class PinBallActivity extends Activity {
/** 桌面的寬度 */
private int tableWidth;
/** 桌面的高度 */
private int tableHeight;
/** 隨機數生成器 **/
private Random random = new Random();
/** 球拍的垂直位置 */
private int racketY;
/** 球拍的水平位置 */
private int racketX = random.nextInt(200);
/** 球拍的寬度 */
private final int RACKET_WIDTH = 70;
/** 球拍的高度 */
private final int RACKET_HEIGHT = 20;
/** 小球的大小 */
private int BALL_SIZE = 12;
/** 小球的X座標 */
private int ballX = random.nextInt(200) + 20;
/** 小球的Y座標 */
private int ballY = random.nextInt(10) + 20;
/**返回一個-0.5~0.5的比率,用於控制小球的執行方向*/
private double xyRate = random.nextDouble()- 0.5;
/**小球的縱向執行速度*/
private int ySpeed = 10;
/**小球的橫向執行速度*/
private int xSpeed = (int) (ySpeed * xyRate * 2);
/*** 遊戲是否結束的遊標 */
private boolean isLose = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 去掉視窗標題
requestWindowFeature(Window.FEATURE_NO_TITLE);
// 全屏顯示
getWindow().setFlags(WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT);
// 建立FameView元件
final GameView gameView = new GameView(this);
setContentView(gameView);
// 獲取視窗管理器
WindowManager manager = getWindowManager();
// 獲取密度
Display display = manager.getDefaultDisplay();
DisplayMetrics displayMetrics = new DisplayMetrics();
display.getMetrics(displayMetrics);
// 獲得螢幕的寬和高
tableWidth = displayMetrics.widthPixels;
tableHeight = displayMetrics.heightPixels;
// 初始化球拍的垂直位置
racketY = tableHeight - 80;
final Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
if (msg.what == 0x123) {
gameView.invalidate();
}
};
};
gameView.setOnKeyListener(new OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
// 獲取由那個鍵觸發的事件
switch (event.getAction()) {
// 控制擋板左移
case KeyEvent.KEYCODE_A:
if (racketX > 0)
racketX -= 10;
break;
// 控制擋板右移
case KeyEvent.KEYCODE_D:
if (racketX < tableWidth - tableWidth)
racketX += 10;
break;
}
// 通知GameView重繪
gameView.invalidate();
return true;
}
});
final Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
//如果小球碰到左邊邊框
if(ballX <= 0 || ballX >= tableWidth - BALL_SIZE){
xSpeed = -xSpeed;
}
//如果小球高度超出了球拍的位置,且橫向不在球拍的範圍之內,遊戲結束
if(ballY >= racketY - BALL_SIZE && (ballX < racketX || ballX > racketX + RACKET_WIDTH)){
timer.cancel();
//設定遊戲是否結束的旗標為true
isLose = true;
}
//如果小球位於球拍之內,且到達球拍位置,小球反彈
else if(ballY <= 0 || (ballY >= racketY - BALL_SIZE && ballX >racketX && ballX <= racketX + RACKET_WIDTH)){
ySpeed = -ySpeed;
}
//小球的座標增加
ballX += xSpeed;
ballY += ySpeed;
//傳送訊息,通知系統重繪元件
handler.sendEmptyMessage(0x123);
}
}, 0, 100);
}
class GameView extends View {
Paint paint = new Paint();
public GameView(Context context) {
this(context, null);
}
public GameView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public GameView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// 接收焦點
setFocusable(true);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 設定樣式
paint.setStyle(Paint.Style.FILL);
// 抗鋸齒
paint.setAntiAlias(true);
// 如果遊戲已經結束
if (isLose) {
// 顏色
paint.setColor(Color.RED);
;
// 字型大小
paint.setTextSize(40);
canvas.drawText("遊戲結束", 50, 200, paint);
}
// 如果遊戲還沒有結束
else {
// 設定顏色,繪製小球
paint.setColor(Color.rgb(240, 240, 80));
canvas.drawCircle(ballX, ballY, BALL_SIZE, paint);
// 設定顏色,繪製球拍
paint.setColor(Color.rgb(80, 80, 200));
canvas.drawRect(racketX, racketY, racketX + RACKET_WIDTH,
racketY + RACKET_HEIGHT, paint);
}
}
}
}