Android 2D繪圖(Canvas+paint)詳解
阿新 • • 發佈:2019-02-17
目錄:
1.重要類概述
2.重要類的常用方法
2.簡單View繪製(圓、圓弧、矩形、弧形、圓角矩形、橢圓、文字等)
3.setXfermode(Xfermode xfermode)的運用
1.重要類概述
在2D繪製中我們常用的類,也是兩個最重要的類就是Canvas(畫布)和Paint(畫筆),通過Canvas我們可以設定
繪製的形狀和路徑,當然僅僅形狀和路徑是不行的,我們還需要顏色啊,陰影啊,透明度等等的設定,這時候就是Paint的
事情了,Paint的作用主要就是設定繪圖的風格,下面我們就總結一下這兩個類常用的方法。
2.重要類的常用方法
(1)Canvas:
構造類方法:
裁切類方法:
圖形繪製類方法:
填充類方法:
其他操作類方法:
(2)Paint:
文字設定相關方法:
繪圖設定相關方法:
其他方法:
2.簡單View繪製(圓、圓弧、矩形、弧形、圓角矩形、橢圓、文字等)
我們通過自定義View的形式來展示我們繪畫的圖形,所以在此之前我們需要搭建一個自定義View的模型
1.新增自定義類繼承View類(待會我們將在onDraw()方法中繪製我們的圖形)
2.佈局xml檔案中應用(通過 包名.類名 的形式 (就像下面com.example.drawview.DrawTextView一樣)指定我們定義的類)
接下來我們就可以在onDraw()方法中繪製我們的圖形了
(1)圓的繪製
1.1 程式碼
1.2 效果圖
(2)矩形繪製
2.1 程式碼
2.2 效果圖
(3)圓角矩形的繪製
3.1 程式碼
3.2 效果圖
(4)橢圓的繪製
4.1 程式碼
4.2 效果圖
(5)點的繪製
5.1 程式碼
5.2 效果圖
(6)線段的繪製
6.1 程式碼
6.2 效果圖
(7)路徑的繪製
7.1 程式碼
7.2 效果圖
(8)路徑文字+簡單文字的繪製
8.1 程式碼
8.2 效果圖
(9)點文字的繪製
9.1 程式碼
9.2 效果圖
以此實現多樣的自定義View。
由於它的混合模式多達18種,下面我只是選取了其中幾種,然後分別對不在Canvas上建立新Layer且不限制使用軟體渲染與在Canvas上建立新
Layer並且個別模式採用軟體渲染模式進行對比,看看他們的區別。如果又需要了解的原理的可以看看下面這篇部落格:
http://blog.csdn.net/iispring/article/details/50472485
1)不在Canvas上建立新Layer且不限制使用軟體渲染
(1)程式碼
(2)效果截圖
(1)程式碼
(2)效果截圖
3)PorterDuff.Mode的模式分類
上面這段程式碼來之原始碼,看了半天也沒看出啥來,話說那些註釋是啥意思呢,啥Sa又Da,寶寶頭都大了?
網上看了一些文字解釋,原來這樣:
Sa->Source alpha->源圖的Alpha通道
Da->Destination alpha->目標圖的Alpha通道
Sc->Source color->源圖的顏色
Dc->Destination color ->目標圖的顏色
然後呢這樣混合過後就組成了最後的ARGB值,形成最終的效果
1.重要類概述
2.重要類的常用方法
2.簡單View繪製(圓、圓弧、矩形、弧形、圓角矩形、橢圓、文字等)
3.setXfermode(Xfermode xfermode)的運用
1.重要類概述
在2D繪製中我們常用的類,也是兩個最重要的類就是Canvas(畫布)和Paint(畫筆),通過Canvas我們可以設定
繪製的形狀和路徑,當然僅僅形狀和路徑是不行的,我們還需要顏色啊,陰影啊,透明度等等的設定,這時候就是Paint的
事情了,Paint的作用主要就是設定繪圖的風格,下面我們就總結一下這兩個類常用的方法。
2.重要類的常用方法
(1)Canvas:
構造類方法:
Canvas() //構造方法 Canvas(Bitmap bitmap) //帶參構造方法,建立一個以bitmap點陣圖為背景的Canvas
裁切類方法:
clipPath(Path path, Region.Op op) //根據特殊path組合裁切影象,Region.Op定義了Region支援的區域間運算種類。
clipRect(Rect rect, Region.Op op) //根據矩形組合裁切影象
clipRegion(Region region, Region.Op op)
concat(Matrix matrix) //通過matrix的設定可以對繪製的圖形進行繪製伸縮和位移
圖形繪製類方法:
drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint) //繪製弧形 drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) //繪製bitmap點陣圖 drawPicture(Picture picture, RectF dst) //繪製圖片 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) //繪製路徑 drawPoint(float x, float y, Paint paint) //繪製點 drawPoints(float[] pts, int offset, int count, Paint paint) //繪製多個點 drawPosText(String text, float[] pos, Paint paint) //繪製文字,float[] pos指定每個文字位置 drawRect(float left, float top, float right, float bottom, Paint paint) //繪製矩形 drawRoundRect(RectF rect, float rx, float ry, Paint paint) //繪製圓角矩形 drawText(String text, float x, float y, Paint paint) //繪製string文字 drawTextOnPath(char[] text, int index, int count, Path path, float hOffset, float vOffset, Paint paint) //路徑上繪製文字
填充類方法:
drawRGB(int r, int g, int b) //使用RGB指定顏色填充canvas的bitmap畫布
drawARGB(int a, int r, int g, int b) //使用ARGB指定顏色填充canvas的bitmap畫布
其他操作類方法:
save() //儲存Canvas狀態,save之後,可以呼叫Canvas的平移、放縮、旋轉、錯切、裁剪等操作 restore() //恢復Canvas之前儲存的狀態,防止save後對Canvas執行的操作對後續的繪製有影響 rotate(float degrees, float px, float py) //旋轉 scale(float sx, float sy) //縮放 skew(float sx, float sy) //扭曲 translate(float dx, float dy) //平移
(2)Paint:
文字設定相關方法:
isUnderlineText() //判斷是否有下劃線
setUnderlineText(boolean underlineText) //設定下劃線
getLetterSpacing() //獲取字元間的間距
setLetterSpacing(float letterSpacing) //設定字元間距
getFontSpacing() //獲取行間距
isStrikeThruText() //判斷文字是否有刪除線
setStrikeThruText(boolean strikeThruText) //設定文字刪除線
getTextSize() //獲取字型大小
setTextSize(float textSize) //設定字型大小
getTypeface() //獲取文字字型型別
setTypeface(Typeface typeface) //設定文字字型型別
getTextSkewX() //獲取斜體文字的值
setTextSkewX(float skewX) //設定斜體文字的值,負數為右傾斜,正數為左傾斜 官方推薦-0.25
getTextScaleX() //獲取文字水平縮放值
setTextScaleX(float scaleX) //設定文字水平縮放值
getTextAlign() //獲取文字對其方式
setTextAlign(Paint.Align align) //設定文字對其方式
ascent() //baseline之上至字元最高處的距離
descent() //baseline下面到字元最低處的距離
measureText(CharSequence text, int start, int end) //測繪文字的寬度
getTextBounds(char[] text, int index, int count, Rect bounds) //獲取文字寬高
getTextWidths(String text, int start, int end, float[] widths) //精確獲取文字寬度
getTextLocale() //獲取文字語言地理位置
setTextLocale(Locale locale) //設定文字地理位置,也就是設定對應的語言
繪圖設定相關方法:
//設定畫筆顏色
setARGB(int a, int r, int g, int b)
setAlpha(int a)
setColor(int color)
//獲取畫筆顏色
getAlpha()
getColor()
isAntiAlias() //判斷是否抗鋸齒
setAntiAlias(boolean aa) //設定抗鋸齒,雖然耗資源耗時間,但是一般會開啟
getStyle() //獲取畫筆樣式
setStyle(Paint.Style style) //設定畫筆樣式,FILL:實心; FILL_OR_STROKE:同時實心和空心; STROKE:空心
setStrokeCap(Cap cap) //設定畫筆樣式, 圓形(Cap.Round),方形(Cap.SQUARE)
getStrokeWidth() //獲取畫筆的粗細大小
setStrokeWidth(float width) //設定畫筆的粗細大小
clearShadowLayer() //清除陰影層
setShadowLayer(float radius, float dx, float dy, int shadowColor) //設定陰影
getXfermode() //獲取圖形繪製的畫素融合模式
setXfermode(Xfermode xfermode) //設定圖形繪製的畫素融合模式和疊加模式,就是新繪製的畫素與Canvas上對應位置已有的畫素按照混合規則進行顏色混合
getShader() //獲取圖形的渲染方式
setShader(Shader shader) //設定圖形的渲染方式,分別有線性渲染(LinearGradient) 環形渲染(RadialGradient) 組合渲染(ComposeShader) 掃描漸變渲染/梯度渲染(SweepGradient)
其他方法:
reset() //清除畫筆復位
2.簡單View繪製(圓、圓弧、矩形、弧形、圓角矩形、橢圓、文字等)
我們通過自定義View的形式來展示我們繪畫的圖形,所以在此之前我們需要搭建一個自定義View的模型
1.新增自定義類繼承View類(待會我們將在onDraw()方法中繪製我們的圖形)
package com.example.drawview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by elimy on 2016-10-11.
*/
public class DrawCircleView extends View {
Paint paint;
public DrawCircleView(Context context) {
super(context);
}
public DrawCircleView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
}
2.佈局xml檔案中應用(通過 包名.類名 的形式 (就像下面com.example.drawview.DrawTextView一樣)指定我們定義的類)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.drawview.MainActivity">
<com.example.drawview.DrawTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</RelativeLayout>
接下來我們就可以在onDraw()方法中繪製我們的圖形了
(1)圓的繪製
1.1 程式碼
package com.example.drawview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by elimy on 2016-10-11.
*/
public class DrawCircleView extends View {
Paint paint;
public DrawCircleView(Context context) {
super(context);
}
public DrawCircleView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//初始化畫筆
paint = new Paint();
//設定抗鋸齒
paint.setAntiAlias(true);
//設定畫筆顏色
paint.setColor(getResources().getColor(R.color.colorAccent));
//設定畫筆要是,圓形/方形/其他
paint.setStrokeCap(Paint.Cap.BUTT);
//設定實心圓
paint.setStyle(Paint.Style.FILL);
//設定畫筆大小
paint.setStrokeWidth(2);
//設定陰影
paint.setShadowLayer(5,5,5, Color.BLUE);
//繪製實心圓
canvas.drawCircle(300,200,100,paint);
//設定為空心
paint.setStyle(Paint.Style.STROKE);
//設定畫筆大小
paint.setStrokeWidth(3);
//繪製
canvas.drawCircle(500,200,100,paint);
}
}
1.2 效果圖
(2)矩形繪製
2.1 程式碼
package com.example.drawview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by elimy on 2016-10-15.
*/
public class DrawRectView extends View {
public DrawRectView(Context context) {
super(context);
}
public DrawRectView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
Paint paint = new Paint();
//設定抗鋸齒
paint.setAntiAlias(true);
//設定畫筆的樣式
paint.setStrokeCap(Paint.Cap.ROUND);
//設定畫筆粗細大小
paint.setStrokeWidth(3);
//設定畫筆的所畫圖形的樣式
paint.setStyle(Paint.Style.FILL);
//設定畫筆的顏色
paint.setColor(Color.GREEN);
//繪製矩形 drawRect(left頂點X座標, left頂點Y座標, 右底部X座標, 右底Y座標, @NonNull Paint paint)
canvas.drawRect(200,200,400,400,paint);
//從新設定畫筆所畫圖形樣式
paint.setStyle(Paint.Style.STROKE);
//重新設定畫筆大小
paint.setStrokeWidth(5);
//繪製
RectF rect = new RectF(200,420,400,620);
canvas.drawRect(rect,paint);
}
}
2.2 效果圖
(3)圓角矩形的繪製
3.1 程式碼
package com.example.drawview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by elimy on 2016-10-14.
*/
public class DrawRoundRectView extends View {
public DrawRoundRectView(Context context) {
super(context);
}
public DrawRoundRectView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
//宣告並初始化
Paint paint = new Paint();
//設定抗鋸齒
paint.setAntiAlias(true);
//設定畫出的圖形是實心還是空心
paint.setStyle(Paint.Style.FILL);
//設定畫筆粗細
paint.setStrokeWidth(2);
//設定畫筆的樣式
paint.setStrokeCap(Paint.Cap.ROUND);
//設定畫筆顏色
paint.setColor(getResources().getColor(R.color.colorPrimaryDark));
/* if (Build.VERSION.SDK_INT>=21){
//需要在API 21版本及其以後才能使用,和下面效果相同
canvas.drawRoundRect(100,100,200,200,20,20,paint);
}*/
RectF rect = new RectF(200,200,400,400);
canvas.drawRoundRect(rect,50,50,paint);
//從新設定畫筆大小
paint.setStrokeWidth(5);
//設定畫筆畫出的圖形未空心
paint.setStyle(Paint.Style.STROKE);
//繪製矩形
// RectF(left頂點x座標, left頂點Y座標, right邊底部x座標, right邊底部Y座標)
RectF rect2 = new RectF(200,420,400,620);
//繪製圓角矩形
//drawRoundRect(@NonNull RectF rect, x方向上的圓角半徑, Y方向上的圓角半徑, @NonNull Paint paint)
canvas.drawRoundRect(rect2,50,50,paint);
}
}
3.2 效果圖
(4)橢圓的繪製
4.1 程式碼
package com.example.drawview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by elimy on 2016-10-16.
*/
public class DrawOvalView extends View {
public DrawOvalView(Context context) {
super(context);
}
public DrawOvalView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
Paint paint = new Paint();
//設定抗鋸齒
paint.setAntiAlias(true);
//設定畫筆筆尖樣式
paint.setStrokeCap(Paint.Cap.ROUND);
//設定所畫圖形的填充樣式
paint.setStyle(Paint.Style.FILL);
//設定畫筆顏色
paint.setColor(getResources().getColor(R.color.colorAccent));
//例項化一個包裹橢圓的矩形,如果為正方形,則橢圓為圓
RectF rectF = new RectF(200,200,500,400);
//設定畫布顏色,方便與paint.setColor()對比,看看他們的區別
canvas.drawColor(Color.BLACK);
//繪製橢圓
canvas.drawOval(rectF,paint);
//設定畫筆的大小
paint.setStrokeWidth(5);
//設定繪畫圖形為空心
paint.setStyle(Paint.Style.STROKE);
//繪製
if (Build.VERSION.SDK_INT>=21){
//API 21以上才能用,與上面的繪畫方式效果一致
canvas.drawOval(200,450,500,650,paint);
}
}
}
4.2 效果圖
(5)點的繪製
5.1 程式碼
package com.example.drawview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by elimy on 2016-10-16.
*/
public class DrawPointView extends View {
public DrawPointView(Context context) {
super(context);
}
public DrawPointView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
Paint paint = new Paint();
paint.setAntiAlias(true);
//設定畫筆大小,我這邊設定的大點,方便觀察
paint.setStrokeWidth(50);
//設定畫筆筆尖樣式,預設為Paint.Cap.SQUARE,待會切換一下,你就會知道它的作用了
// 網上有說在設定paint.setStrokeCap(Paint.Cap.ROUND|SQUARE);之前需要同時設定
//paint.setStrokeJoin(Paint.Join.ROUND|MITER);我測試了好像不需要也能達到切換的目的
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setColor(getResources().getColor(R.color.colorAccent));
canvas.drawPoint(200,200,paint);
paint.setStrokeCap(Paint.Cap.BUTT);
paint.setColor(getResources().getColor(R.color.colorPrimary));
canvas.drawPoint(300,200,paint);
paint.setStrokeCap(Paint.Cap.SQUARE);
paint.setColor(getResources().getColor(R.color.colorAccent));
canvas.drawPoint(400,200,paint);
paint.setStrokeCap(Paint.Cap.BUTT);
paint.setColor(getResources().getColor(R.color.colorPrimary));
canvas.drawPoint(500,200,paint);
//設定畫筆樣式
paint.setStrokeCap(Paint.Cap.ROUND);
//設定畫筆顏色
paint.setColor(getResources().getColor(R.color.colorAccent));
//繪製多個點
canvas.drawPoints(new float[]{150,300,250,300,350,300,450,300,550,300},paint);
}
}
5.2 效果圖
(6)線段的繪製
6.1 程式碼
package com.example.drawview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by elimy on 2016-10-16.
*/
public class DrawLineView extends View {
public DrawLineView(Context context) {
super(context);
}
public DrawLineView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
Paint paint = new Paint();
//設定抗鋸齒
paint.setAntiAlias(true);
//設定畫筆樣式
paint.setStrokeCap(Paint.Cap.ROUND);
//設定畫筆顏色
paint.setColor(Color.RED);
//設定畫筆大小
paint.setStrokeWidth(20);
//畫線段,引數分別是線段起始點的座標和終點的座標,和預設的畫筆
canvas.drawLine(100,200,600,200,paint);
//LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[],
//TileMode tile),(x0,y0) (x1,y1)原始碼上說是梯度的座標,不是很懂,我個人理解為後面設定的每一種顏色渲染的長度,座標間隔越小,渲染的顏色長度越短
//int colors[]:指示需要渲染的顏色陣列 float positions[]:可為null,為null時顏色均勻分佈
//TileMode tile:指示平鋪模式,分別有REPEAT(重複) CLAMP(畫素擴散) MIRROR(鏡面投影)
Shader mShader=new LinearGradient(0,0,100,100,
new int[]{Color.RED,Color.GREEN,Color.BLUE,Color.YELLOW},
null,Shader.TileMode.REPEAT);
//設定畫筆渲染漸變
paint.setShader(mShader);
//繪製漸變線段
canvas.drawLine(100,300,600,300,paint);
}
}
6.2 效果圖
(7)路徑的繪製
7.1 程式碼
package com.example.drawview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by elimy on 2016-10-16.
*/
public class DrawPathView extends View {
public DrawPathView(Context context) {
super(context);
}
public DrawPathView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
//設定抗鋸齒
paint.setAntiAlias(true);
//設定畫筆筆尖樣式
paint.setStrokeCap(Paint.Cap.ROUND);
//設定填充模式,預設為FILL
paint.setStyle(Paint.Style.FILL);
//設定畫筆大小
paint.setStrokeWidth(3);
//設定顏色
paint.setColor(getResources().getColor(R.color.colorPrimary));
/*Path類的幾個...To()方法:
* path.moveTo(x,y)移動到某個點
* path.lineTo(x,y)從起始點,預設(0,0)點繪製直線到(x,y)點
* path.arcTo()用來繪製弧形
* path.cubicTo()用來繪製貝塞爾曲線
* path.quadTo()也是繪製貝塞爾曲線的
* */
//例項化路徑類
Path path = new Path();
//設定移動,不繪製
path.moveTo(200, 200);
//從(200,200)畫到(200,400)
path.lineTo(200, 400);
//從(200,400)畫到(400,400)
path.lineTo(400, 400);
//封閉曲線
path.close();
//繪製路徑
canvas.drawPath(path, paint);
//設定顏色
paint.setColor(getResources().getColor(R.color.colorAccent));
//設定移動,不繪製
Path path1 = new Path();
path1.moveTo(200, 200);
//從(200,200)畫到(200,400)
path1.lineTo(400, 200);
//從(200,400)畫到(400,400)
path1.lineTo(400, 400);
//封閉曲線
path1.close();
//繪製
canvas.drawPath(path1, paint);
//初始化路徑
Path path2 = new Path();
//設定矩形
RectF rectF = new RectF(420, 200, 620, 500);
//取矩形包裹橢圓0°到270°的弧
path2.arcTo(rectF, 0, 270);
//封閉弧形
path2.close();
//設定填充實心
paint.setStyle(Paint.Style.FILL);
//設定畫筆顏色
paint.setColor(getResources().getColor(R.color.colorAccent));
//繪製圖形
canvas.drawPath(path2, paint);
/*
*quadTo()繪製貝塞爾曲線
*/
Path path3 = new Path();
//移動到(200,620)點
path3.moveTo(200, 620);
//繪製貝塞爾曲線
path3.quadTo(300, 420, 400, 620);
//設定空心
paint.setStyle(Paint.Style.STROKE);
//繪製曲線
canvas.drawPath(path3, paint);
/*
* cubicTo()繪製貝塞爾曲線
* */
Path path4 = new Path();
//移動到(300,100)點
path4.moveTo(300, 100);
//繪製曲線
path4.cubicTo(300, 100, 5, 300, 300, 500);
//設定不填充
paint.setStyle(Paint.Style.STROKE);
//繪製
canvas.drawPath(path4, paint);
}
}
7.2 效果圖
(8)路徑文字+簡單文字的繪製
8.1 程式碼
package com.example.drawview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Typeface;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by elimy on 2016-10-16.
*/
public class DrawTextView extends View {
public DrawTextView(Context context) {
super(context);
}
public DrawTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
Paint paint = new Paint();
//設定抗鋸齒
paint.setAntiAlias(true);
//設定畫筆顏色
paint.setColor(getResources().getColor(R.color.colorPrimary));
//設定畫筆大小
paint.setStrokeWidth(3);
//設定字型大小
paint.setTextSize(30);
//設定文字樣式
paint.setTypeface(Typeface.DEFAULT_BOLD);
String str = "我知道,你要說我很帥,哈啊哈哈哈哈,字不夠長!";
//全部顯示,起點在(200,100)點
canvas.drawText(str,50,100,paint);
//擷取4~9的字串顯示
canvas.drawText(str,4,10,200,200,paint);
//例項化路徑
Path path = new Path();
//設定圓形狀路徑,Direction指示文字顯示是逆時針向外還是順時針向內 Path.Direction.CW|Path.Direction.CCW
path.addCircle(400,400,110, Path.Direction.CW);
//更具路徑繪製文字
canvas.drawTextOnPath(str,path,0,0,paint);
Path path1 = new Path();
if (Build.VERSION.SDK_INT>=21){
//API 21以上
path1.addOval(300,600,500,900, Path.Direction.CCW);
}
canvas.drawTextOnPath(str,path1,0,0,paint);
}
}
8.2 效果圖
(9)點文字的繪製
9.1 程式碼
package com.example.drawview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by elimy on 2016-10-16.
*/
public class DrawPosTextView extends View {
public DrawPosTextView(Context context) {
super(context);
}
public DrawPosTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
Paint paint = new Paint();
//設定抗鋸齒
paint.setAntiAlias(true);
//設定畫筆顏色
paint.setColor(getResources().getColor(R.color.colorAccent));
//設定畫筆到校
paint.setStrokeWidth(5);
//設定字型顏色
paint.setTextSize(30);
canvas.drawColor(Color.BLACK);
canvas.drawPosText("你看我帥嘛?",new float[]{100,200,200,100,300,200,300,400,200,500,100,400},paint);
}
}
9.2 效果圖
3.setXfermode(Xfermode xfermode)的運用
setXfermode()方法主要是設定圖形繪製的畫素融合模式和疊加模式,就是新繪製的畫素與Canvas上對應位置已有的畫素按照混合規則進行顏色混合以此實現多樣的自定義View。
由於它的混合模式多達18種,下面我只是選取了其中幾種,然後分別對不在Canvas上建立新Layer且不限制使用軟體渲染與在Canvas上建立新
Layer並且個別模式採用軟體渲染模式進行對比,看看他們的區別。如果又需要了解的原理的可以看看下面這篇部落格:
http://blog.csdn.net/iispring/article/details/50472485
1)不在Canvas上建立新Layer且不限制使用軟體渲染
(1)程式碼
package com.example.drawview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by elimy on 2016-10-17.
*/
public class XfermodeView extends View {
public XfermodeView(Context context) {
super(context);
}
public XfermodeView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/*
* 預設的Xfermode
* */
Paint paint = getPaint();
//設定繪製圓的畫筆顏色
paint.setColor(getResources().getColor(R.color.colorPrimary));
canvas.drawCircle(200, 200, 100, paint);
//設定繪製圓角矩形的畫筆顏色
paint.setColor(getResources().getColor(R.color.colorAccent));
if (Build.VERSION.SDK_INT >= 21)
//API 21以上,當然最好是些相容性好的,我這裡只是為了方便
canvas.drawRoundRect(200, 200, 300, 350, 10, 10, paint);
/*
* PorterDuff.Mode.ADD模式
* */
Paint paint1 = getPaint();
paint1.setColor(getResources().getColor(R.color.colorPrimary));
canvas.drawCircle(500, 200, 100, paint1);
//設定畫素融合模式
paint1.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
paint1.setColor(getResources().getColor(R.color.colorAccent));
if (Build.VERSION.SDK_INT >= 21)
//API 21以上
canvas.drawRoundRect(500, 200, 600, 350, 10, 10, paint1);
//取消畫素融合模式的設定
paint1.setXfermode(null);
/*
* PorterDuff.Mode.CLEAR模式
* */
Paint paint2 = getPaint();
paint2.setColor(getResources().getColor(R.color.colorPrimary));
canvas.drawCircle(200, 500, 100, paint2);
paint2.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
paint2.setColor(getResources().getColor(R.color.colorAccent));
if (Build.VERSION.SDK_INT >= 21)
//API 21以上
canvas.drawRoundRect(200, 500, 300, 650, 10, 10, paint2);
paint2.setXfermode(null);
/*
* PorterDuff.Mode.DARKEN模式
* */
Paint paint3 = getPaint();
paint3.setColor(getResources().getColor(R.color.colorPrimary));
canvas.drawCircle(500, 500, 100, paint3);
paint3.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DARKEN));
paint3.setColor(getResources().getColor(R.color.colorAccent));
if (Build.VERSION.SDK_INT >= 21)
//API 21以上
canvas.drawRoundRect(500, 500, 600, 650, 10, 10, paint3);
paint3.setXfermode(null);
/*
* PorterDuff.Mode.LIGHTEN模式
* */
Paint paint4 = getPaint();
paint4.setColor(getResources().getColor(R.color.colorPrimary));
canvas.drawCircle(200, 800, 100, paint4);
paint4.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN));
paint4.setColor(getResources().getColor(R.color.colorAccent));
if (Build.VERSION.SDK_INT >= 21)
//API 21以上
canvas.drawRoundRect(200, 800, 300, 950, 10, 10, paint4);
/*
* PorterDuff.Mode.MULTIPLY模式
* */
Paint paint5 = getPaint();
paint5.setColor(getResources().getColor(R.color.colorPrimary));
canvas.drawCircle(500, 800, 100, paint5);
paint5.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
paint5.setColor(getResources().getColor(R.color.colorAccent));
if (Build.VERSION.SDK_INT >= 21)
//API 21以上
canvas.drawRoundRect(500, 800, 600, 950, 10, 10, paint5);
paint5.setXfermode(null);
}
public Paint getPaint() {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(5);
return paint;
}
}
(2)效果截圖
(1)程式碼
package com.example.drawview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by elimy on 2016-10-17.
*/
public class XfermodeView extends View {
public XfermodeView(Context context) {
super(context);
}
public XfermodeView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/*
* 預設的Xfermode
* */
Paint paint = getPaint();
//設定繪製圓的畫筆顏色
paint.setColor(getResources().getColor(R.color.colorPrimary));
canvas.drawCircle(200, 200, 100, paint);
//設定繪製圓角矩形的畫筆顏色
paint.setColor(getResources().getColor(R.color.colorAccent));
if (Build.VERSION.SDK_INT >= 21)
//API 21以上,當然最好是些相容性好的,我這裡只是為了方便
canvas.drawRoundRect(200, 200, 300, 350, 10, 10, paint);
/*
* PorterDuff.Mode.ADD模式
* */
//canvas.saveLayer()在canvas原畫布上見一個透明的layer
int layerId = canvas.saveLayer(0, 0, canvas.getWidth(), canvas.getHeight(), null, Canvas.ALL_SAVE_FLAG);
Paint paint1 = getPaint();
paint1.setColor(getResources().getColor(R.color.colorPrimary));
canvas.drawCircle(500, 200, 100, paint1);
//設定畫素融合模式
paint1.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
paint1.setColor(getResources().getColor(R.color.colorAccent));
if (Build.VERSION.SDK_INT >= 21)
//API 21以上
canvas.drawRoundRect(500, 200, 600, 350, 10, 10, paint1);
//取消畫素融合模式的設定
paint1.setXfermode(null);
//將新圖層畫到canvas上
canvas.restoreToCount(layerId);
/*
* PorterDuff.Mode.CLEAR模式
* */
int layerId2 = canvas.saveLayer(0, 0, canvas.getWidth(), canvas.getHeight(), null, Canvas.ALL_SAVE_FLAG);
Paint paint2 = getPaint();
paint2.setColor(getResources().getColor(R.color.colorPrimary));
canvas.drawCircle(200, 500, 100, paint2);
paint2.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
paint2.setColor(getResources().getColor(R.color.colorAccent));
if (Build.VERSION.SDK_INT >= 21)
//API 21以上
canvas.drawRoundRect(200, 500, 300, 650, 10, 10, paint2);
paint2.setXfermode(null);
canvas.restoreToCount(layerId2);
/*
* PorterDuff.Mode.DARKEN模式
* */
int layerId3 = canvas.saveLayer(0, 0, canvas.getWidth(), canvas.getHeight(), null, Canvas.ALL_SAVE_FLAG);
Paint paint3 = getPaint();
paint3.setColor(getResources().getColor(R.color.colorPrimary));
canvas.drawCircle(500, 500, 100, paint3);
//禁用掉CPU硬體加速,採用軟體渲染模式,因為DARKEN、LIGHTEN、OVERLAY等幾種混合規則在GPU硬體加速效果展示不出來
this.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
paint3.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DARKEN));
paint3.setColor(getResources().getColor(R.color.colorAccent));
if (Build.VERSION.SDK_INT >= 21)
//API 21以上
canvas.drawRoundRect(500, 500, 600, 650, 10, 10, paint3);
paint3.setXfermode(null);
canvas.restoreToCount(layerId3);
/*
* PorterDuff.Mode.LIGHTEN模式
* */
int layerId4 = canvas.saveLayer(0, 0, canvas.getWidth(), canvas.getHeight(), null, Canvas.ALL_SAVE_FLAG);
Paint paint4 = getPaint();
paint4.setColor(getResources().getColor(R.color.colorPrimary));
canvas.drawCircle(200, 800, 100, paint4);
this.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
paint4.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN));
paint4.setColor(getResources().getColor(R.color.colorAccent));
if (Build.VERSION.SDK_INT >= 21)
//API 21以上
canvas.drawRoundRect(200, 800, 300, 950, 10, 10, paint4);
paint4.setXfermode(null);
canvas.restoreToCount(layerId4);
/*
* PorterDuff.Mode.MULTIPLY模式
* */
int layerId5 = canvas.saveLayer(0, 0, canvas.getWidth(), canvas.getHeight(), null, Canvas.ALL_SAVE_FLAG);
Paint paint5 = getPaint();
paint5.setColor(getResources().getColor(R.color.colorPrimary));
canvas.drawCircle(500, 800, 100, paint5);
paint5.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
paint5.setColor(getResources().getColor(R.color.colorAccent));
if (Build.VERSION.SDK_INT >= 21)
//API 21以上
canvas.drawRoundRect(500, 800, 600, 950, 10, 10, paint5);
paint5.setXfermode(null);
canvas.restoreToCount(layerId5);
}
public Paint getPaint() {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(5);
return paint;
}
}
(2)效果截圖
3)PorterDuff.Mode的模式分類
/** [0, 0] */
CLEAR (0),
/** [Sa, Sc] */
SRC (1),
/** [Da, Dc] */
DST (2),
/** [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] */
SRC_OVER (3),
/** [Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc] */
DST_OVER (4),
/** [Sa * Da, Sc * Da] */
SRC_IN (5),
/** [Sa * Da, Sa * Dc] */
DST_IN (6),
/** [Sa * (1 - Da), Sc * (1 - Da)] */
SRC_OUT (7),
/** [Da * (1 - Sa), Dc * (1 - Sa)] */
DST_OUT (8),
/** [Da, Sc * Da + (1 - Sa) * Dc] */
SRC_ATOP (9),
/** [Sa, Sa * Dc + Sc * (1 - Da)] */
DST_ATOP (10),
/** [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc] */
XOR (11),
/** [Sa + Da - Sa*Da,
Sc*(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)] */
DARKEN (12),
/** [Sa + Da - Sa*Da,
Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)] */
LIGHTEN (13),
/** [Sa * Da, Sc * Dc] */
MULTIPLY (14),
/** [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] */
SCREEN (15),
/** Saturate(S + D) */
ADD (16),
OVERLAY (17);
上面這段程式碼來之原始碼,看了半天也沒看出啥來,話說那些註釋是啥意思呢,啥Sa又Da,寶寶頭都大了?
網上看了一些文字解釋,原來這樣:
Sa->Source alpha->源圖的Alpha通道
Da->Destination alpha->目標圖的Alpha通道
Sc->Source color->源圖的顏色
Dc->Destination color ->目標圖的顏色
然後呢這樣混合過後就組成了最後的ARGB值,形成最終的效果