Android關於Paint你所知道的和不知道的一切

張風捷特烈發表於2018-11-07

零、前言:

1.曾經也算半個藝術家,深知筆的重要性與複雜性
2.Android裡的Paint設定項好多基本上都是setXXX,getXXX,很多文字相關的內容都在Paint裡
3.主要由畫筆常規配置,畫筆型別、畫筆特效(線效,著色,濾色)、畫筆文字
4.本文暫時還無法覆蓋Paint的所有API,能用的吾儘量都會涉及一下

最主要的還是set方法

paint的Set一覽.png


一、畫筆的常規配置

public void setColor(@ColorInt int color) //設定顏色
public void setAlpha(int a)//設定透明度
public void setARGB(int a, int r, int g, int b)//設定ARGB顏色
public void setStrokeWidth(float width)//設定寬度
public void setAntiAlias(boolean aa)//設定抗鋸齒
複製程式碼

基本設定.png


二、筆的樣式:Paint.Style.:[#FILL|STROKE|FILL_AND_STROKE]

/**
 * 樣式測試
 * @param canvas
 */
private void testStyle(Canvas canvas) {
    Rect rect = new Rect(0, 0, 100, 100);
    mRedPaint.setStrokeWidth(15);
    canvas.save();
    
    mRedPaint.setStyle(Paint.Style.FILL);
    canvas.translate(50, 450);
    canvas.drawRect(rect, mRedPaint);
    
    canvas.translate(150, 0);
    mRedPaint.setStyle(Paint.Style.STROKE);
    canvas.drawRect(rect, mRedPaint);
    
    canvas.translate(150 , 0);
    mRedPaint.setStyle(Paint.Style.FILL_AND_STROKE);
    canvas.drawRect(rect, mRedPaint);
    canvas.restore();
    mRedPaint.setStrokeWidth(40);
}
複製程式碼

畫筆樣式測試.png


三、線帽:Paint.Cap.:[#BUTT|ROUND|SQUARE]

1.繪製圓形的點:Paint.Cap.ROUND
/**
 * 繪製圓形的點
 *
 * @param canvas
 */
private void drawPos(Canvas canvas) {
    //設定畫筆圓帽
    mRedPaint.setStrokeCap(Paint.Cap.ROUND);
    mRedPaint.setStrokeWidth(20);
    //繪製點
    canvas.drawPoint(100, 100, mRedPaint);
    canvas.drawPoints(new float[]{
            400, 400, 500, 500,
            600, 400, 700, 350,
            800, 300, 900, 300
    }, mRedPaint);
}
複製程式碼

繪製圓點.png

2.三種線帽比較

/**
 * 線帽型狀測試:Paint.Cap.BUTT、Paint.Cap.ROUND、Paint.Cap.SQUARE
 *
 * @param canvas
 */
private void testOfCap(Canvas canvas) {
    canvas.save();
    canvas.translate(150, 200);
    //線帽測試:
    mRedPaint.setStrokeCap(Paint.Cap.BUTT);//無頭(預設)
    canvas.drawLine(0, 0, 0, 200, mRedPaint);
    canvas.translate(50, 0);
    mRedPaint.setStrokeCap(Paint.Cap.ROUND);//圓頭
    canvas.drawLine(0, 0, 0, 200, mRedPaint);
    canvas.translate(50, 0);
    mRedPaint.setStrokeCap(Paint.Cap.SQUARE);//方頭
    canvas.drawLine(0, 0, 0, 200, mRedPaint);
    canvas.restore();
}
複製程式碼

線帽型狀測試.png


四、線交角測試:Paint.Join.:[#BEVEL|ROUND|MITER]

注意:只有路徑繪製的線才有交角效果

/**
 * 角型測試:Paint.Join.BEVEL、Paint.Join.ROUND、Paint.Join.MITER
 *
 * @param canvas
 */
private void testOfJoin(Canvas canvas) {
    mRedPaint.setStyle(Paint.Style.STROKE);
    mRedPaint.setStrokeWidth(40);
    Path path = new Path();
    path.moveTo(30, 0);
    path.lineTo(0, 100);
    path.lineTo(100, 100);
    
    canvas.save();
    canvas.translate(600, 100);
    mRedPaint.setStrokeJoin(Paint.Join.BEVEL);//直線(預設)
    canvas.drawPath(path, mRedPaint);
    
    canvas.translate(150, 0);
    mRedPaint.setStrokeJoin(Paint.Join.ROUND);//圓角
    canvas.drawPath(path, mRedPaint);
    
    canvas.translate(150, 0);
    mRedPaint.setStrokeJoin(Paint.Join.MITER);//銳角
    canvas.drawPath(path, mRedPaint);
    canvas.restore();
}
複製程式碼

線交角測試.png


五、Paint的路徑效果

1.虛線:DashPathEffect(new float[]{a, b, a1, b1...}, offSet)

第一參為顯隱線段的長度,第二參為偏移值
下面會動的線是不斷改變偏移量:mDashOffSet的結果

虛線效果.gif

/**
 * 虛線測試
 *
 * @param canvas
 */
private void dashEffect(Canvas canvas) {
    mEffectPaint = new Paint(mRedPaint);
    mEffectPaint.setStrokeCap(Paint.Cap.BUTT);
    //顯示100,隱藏50,顯示50,隱藏50,的迴圈
    mEffectPaint.setPathEffect(new DashPathEffect(new float[]{100, 50, 50, 50}, 0));
    Path path = new Path();
    path.moveTo(100, 650);
    path.lineTo(1000, 650);
    canvas.drawPath(path, mEffectPaint);
    //顯示100,隱藏50,顯示60,隱藏50,的迴圈,偏移:mDashOffSet
    mEffectPaint.setPathEffect(new DashPathEffect(new float[]{100, 50, 50, 50}, mDashOffSet));
    Path pathOffset50 = new Path();
    pathOffset50.moveTo(100, 750);
    pathOffset50.lineTo(1000, 750);
    canvas.drawPath(pathOffset50, mEffectPaint);
}
複製程式碼

2.折角弧:CornerPathEffect(corner)

動畫是不斷改變圓角大小:mEffectCorner的結果

折角弧.gif

/**
 * 圓角折線
 *
 * @param canvas
 */
private void cornerEffect(Canvas canvas) {
    mEffectPaint.setPathEffect(new CornerPathEffect(mEffectCorner));
    mEffectPaint.setStyle(Paint.Style.STROKE);
    mEffectPaint.setStrokeWidth(40);
    Path path = new Path();
    path.moveTo(550, 550);
    path.lineTo(900, 300);
    path.lineTo(1000, 550);
    canvas.drawPath(path, mEffectPaint);
    //藍色輔助線
    Paint tempPaint = new Paint();
    tempPaint.setStyle(Paint.Style.STROKE);
    tempPaint.setColor(Color.BLUE);
    tempPaint.setStrokeWidth(2);
    tempPaint.setPathEffect(new DashPathEffect(new float[]{20, 20}, 0));
    Path helpPath = new Path();
    helpPath.moveTo(550, 550);
    helpPath.lineTo(900, 300);
    helpPath.lineTo(1000, 550);
    canvas.drawPath(helpPath, tempPaint);
}
複製程式碼

3.離散路徑:DiscretePathEffect(小段長,偏移量)

離散路徑.png

/**
 * 離散路徑
 *
 * @param canvas
 */
private void discreteEffect(Canvas canvas) {
    canvas.save();//儲存畫布狀態
    canvas.translate(0, 950);
    //第一個引數:將原來的路徑切成多長的線段,越小,所切成的小線段越多
    //第二引數:被切成的每個小線段的可偏移距離。越大,每個線段的可偏移距離就越大。
    Path path = new Path();
    // 定義路徑的起點
    path.moveTo(100, 0);
    path.lineTo(600, -100);
    path.lineTo(1000, 0);
    
    mEffectPaint.setPathEffect(new DiscretePathEffect(2, 5));
    mEffectPaint.setStrokeWidth(2);
    canvas.drawPath(path, mEffectPaint);
    canvas.translate(0, 100);
    mEffectPaint.setPathEffect(new DiscretePathEffect(20, 5));
    canvas.drawPath(path, mEffectPaint);
    canvas.restore();//重新儲存畫布狀態
}
複製程式碼

4.路徑點樣:PathDashPathEffect(路徑,間距,偏移,樣式)

路徑樣點.gif

通過動圖可以很清楚得看出三個樣式的區別 動圖是不斷改變偏移量:mDashOffSet的結果

//Path shape:表示[路徑點樣],這裡是一個五角星
//float advance:表示兩個[路徑點樣]間的距離
//float phase:路徑繪製偏移距離
//Style style:表示在遇到轉角時的過渡樣式
// ----Style.ROTATE表示通過旋轉[路徑點樣]來過渡轉角;
// ----Style.MORPH表示通過變形[路徑點樣]來過渡轉角;
// ----Style.TRANSLATE表示通過位移[路徑點樣]來過渡轉角。
複製程式碼
/**
 * 路徑點樣路徑樣式
 *
 * @param canvas
 */
private void PathDashEffect(Canvas canvas) {
    canvas.save();
    canvas.translate(0, 1100);
    Path path = new Path();
    // 定義路徑的起點
    path.moveTo(100, 80);
    path.lineTo(600, -100);
    path.lineTo(1000, 80);
    //變形過渡
    mEffectPaint.setPathEffect(new PathDashPathEffect(
            CommonPath.nStarPath(5, 16, 8), 40, mDashOffSet, PathDashPathEffect.Style.ROTATE));
    canvas.drawPath(path, mEffectPaint);
    canvas.restore();
    //旋轉過渡
    canvas.save();
    canvas.translate(0, 1200);
    mEffectPaint.setPathEffect(new PathDashPathEffect(
            CommonPath.nStarPath(5, 16, 8), 40, mDashOffSet, PathDashPathEffect.Style.MORPH));
    canvas.drawPath(path, mEffectPaint);
    canvas.restore();
    //移動過渡
    canvas.save();
    canvas.translate(0, 1300);
    mEffectPaint.setPathEffect(new PathDashPathEffect(
            CommonPath.nStarPath(5, 16, 8), 40, mDashOffSet, PathDashPathEffect.Style.TRANSLATE));
    canvas.drawPath(path, mEffectPaint);
    canvas.restore();
}
/**
 * 離散路徑
 *
 * @param canvas
 */
private void discreteEffect(Canvas canvas) {
    canvas.save();//儲存畫布狀態
    canvas.translate(0, 950);
    //第一個引數:將原來的路徑切成多長的線段,越小,所切成的小線段越多
    //第二引數:被切成的每個小線段的可偏移距離。越大,每個線段的可偏移距離就越大。
    Path path = new Path();
    // 定義路徑的起點
    path.moveTo(100, 0);
    path.lineTo(600, -100);
    path.lineTo(1000, 0);
    mEffectPaint.setPathEffect(new DiscretePathEffect(2, 5));
    mEffectPaint.setStrokeWidth(2);
    canvas.drawPath(path, mEffectPaint);
    canvas.translate(0, 100);
    mEffectPaint.setPathEffect(new DiscretePathEffect(20, 5));
    canvas.drawPath(path, mEffectPaint);
    canvas.restore();//重新儲存畫布狀態
}
複製程式碼
n角星路徑封裝:
    /**
     * n角星路徑
     *
     * @param num 幾角星
     * @param R   外接圓半徑
     * @param r   內接圓半徑
     * @return n角星路徑
     */
    public static Path nStarPath(int num, float R, float r) {
        Path path = new Path();
        float perDeg = 360 / num;
        float degA = perDeg / 2 / 2;
        float degB = 360 / (num - 1) / 2 - degA / 2 + degA;

        path.moveTo(
                (float) (Math.cos(rad(degA + perDeg * 0)) * R + R * Math.cos(rad(degA))),
                (float) (-Math.sin(rad(degA + perDeg * 0)) * R + R));
        for (int i = 0; i < num; i++) {
            path.lineTo(
                    (float) (Math.cos(rad(degA + perDeg * i)) * R + R * Math.cos(rad(degA))),
                    (float) (-Math.sin(rad(degA + perDeg * i)) * R + R));
            path.lineTo(
                    (float) (Math.cos(rad(degB + perDeg * i)) * r + R * Math.cos(rad(degA))),
                    (float) (-Math.sin(rad(degB + perDeg * i)) * r + R));
        }
        path.close();
        return path;
    }

    /**
     * 角度制化為弧度制
     *
     * @param deg 角度
     * @return 弧度
     */
    public static float rad(float deg) {
        return (float) (deg * Math.PI / 180);
    }
複製程式碼

5.疊加特效:PathDashPathEffect(e1,e2...)(此處演示:離散效果+樣點效果)

特效疊加.gif

/**
 * 疊加樣式
 *
 * @param canvas
 */
private void composeEffect(Canvas canvas) {
    mEffectPaint.setStyle(Paint.Style.STROKE);
    mEffectPaint.setStrokeWidth(40);
    canvas.save();
    canvas.translate(0, 1400);
    Path path = new Path();
    // 定義路徑的起點
    path.moveTo(100, 80);
    path.lineTo(600, -100);
    path.lineTo(1000, 80);
    PathDashPathEffect effect1 = new PathDashPathEffect(
            CommonPath.nStarPath(5, 16, 8), 40, mDashOffSet, PathDashPathEffect.Style.ROTATE);
    DiscretePathEffect effect2 = new DiscretePathEffect(20, 5);
    mEffectPaint.setPathEffect(new ComposePathEffect(effect1, effect2));//離散效果+樣點效果
    canvas.drawPath(path, mEffectPaint);
    canvas.restore();
}
複製程式碼

6.路徑疊加

路徑疊加.gif

/**
 * 路徑疊加
 *
 * @param canvas
 */
private void sumEffect(Canvas canvas) {
    canvas.save();
    canvas.translate(0, 1500);
    Path path = new Path();
    // 定義路徑的起點
    path.moveTo(100, 80);
    path.lineTo(600, -100);
    path.lineTo(1000, 80);
    PathDashPathEffect effect1 = new PathDashPathEffect(
            CommonPath.nStarPath(5, 16, 8), 40, mDashOffSet, PathDashPathEffect.Style.ROTATE);
    DiscretePathEffect effect2 = new DiscretePathEffect(20, 5);
    mEffectPaint.setPathEffect(new SumPathEffect(effect1, effect2));//離散效果+樣點效果
    canvas.drawPath(path, mEffectPaint);
    canvas.restore();
}
複製程式碼

六.著色器:Shader

一個很簡單的類,有5個子類:

Shader.png

1.線性漸變:
1).new LinearGradient(漸變起點x,y,漸變終點x,y,漸變色1,漸變色2,漸變模式)

漸變模式:Shader.TileMode.[MIRROR|CLAMP|REPEAT] (圖中很形象,就不解釋了)

        int colorStart = Color.parseColor("#84F125");
        int colorEnd = Color.parseColor("#5825F1");
        canvas.save();
        canvas.translate(mCoo.x, mCoo.y);
        mRedPaint.setStyle(Paint.Style.FILL);
        mRedPaint.setShader(
                new LinearGradient(
                        -200, 0, 200, 0,
                        colorStart, colorEnd,
                        Shader.TileMode.MIRROR

                ));
        canvas.drawRect(-400,-200,400,-100,mRedPaint);

        canvas.translate(0, 150);
        mRedPaint.setShader(
                new LinearGradient(
                        -100, 0, 100, 0,
                        colorStart, colorEnd,
                        Shader.TileMode.CLAMP
                ));
        canvas.drawRect(-400,-200,400,-100,mRedPaint);

        canvas.translate(0, 150);
        mRedPaint.setShader(
                new LinearGradient(
                        -100, 0, 100, 0,
                        colorStart, colorEnd,
                        Shader.TileMode.REPEAT
                ));
        canvas.drawRect(-400,-200,400,-100,mRedPaint);
複製程式碼

線性漸變.png


2).多色多點漸變:LinearGradient(漸變起點x,y,漸變終點x,y,顏色陣列,位置百分點陣列0~1,漸變模式)

多色漸變.png

int[] colors = new int[]{
        Color.parseColor("#F60C0C"),//紅
        Color.parseColor("#F3B913"),//橙
        Color.parseColor("#E7F716"),//黃
        Color.parseColor("#3DF30B"),//綠
        Color.parseColor("#0DF6EF"),//青
        Color.parseColor("#0829FB"),//藍
        Color.parseColor("#B709F4"),//紫
};
float[] pos = new float[]{
        1.f / 7, 2.f / 7, 3.f / 7, 4.f / 7, 5.f / 7, 6.f / 7, 1
};

canvas.translate(0, 150);
mRedPaint.setShader(
        new LinearGradient(
                -300, 0, 300, 0,
                colors, pos,
                Shader.TileMode.CLAMP

        ));
canvas.drawRect(-400, -200, 400, -100, mRedPaint);
複製程式碼

2.徑向漸變:RadialGradient
1).兩色漸變:RadialGradient(漸變中心,漸變半徑,顏色1,顏色2,漸變模式)
canvas.translate(mCoo.x, mCoo.y);
int colorStart = Color.parseColor("#84F125");
int colorEnd = Color.parseColor("#5825F1");
mRedPaint.setStyle(Paint.Style.FILL);
mRedPaint.setShader(
        new RadialGradient(
               0,0,50,
                colorStart, colorEnd,
                Shader.TileMode.MIRROR

        ));

canvas.drawCircle(0, 0, 150, mRedPaint);

canvas.translate(350, 0);
mRedPaint.setShader(
        new RadialGradient(
                0,0,50,
                colorStart, colorEnd,
                Shader.TileMode.CLAMP

        ));
canvas.drawCircle(0, 0, 150, mRedPaint);


canvas.translate(350, 0);
mRedPaint.setShader(
        new RadialGradient(
                0,0,50,
                colorStart, colorEnd,
                Shader.TileMode.REPEAT

        ));
canvas.drawCircle(0, 0, 150, mRedPaint);
複製程式碼

徑像漸變.png


2).多色多點徑向漸變:

RadialGradient(漸變中心,漸變半徑,漸變模式,顏色陣列,位置百分點陣列0~1,漸變模式)

多色徑向漸變.png

int[] colors = new int[]{
        Color.parseColor("#F60C0C"),//紅
        Color.parseColor("#F3B913"),//橙
        Color.parseColor("#E7F716"),//黃
        Color.parseColor("#3DF30B"),//綠
        Color.parseColor("#0DF6EF"),//青
        Color.parseColor("#0829FB"),//藍
        Color.parseColor("#B709F4"),//紫
};
float[] pos = new float[]{
        1.f / 7, 2.f / 7, 3.f / 7, 4.f / 7, 5.f / 7, 6.f / 7, 1
};
mRedPaint.setStyle(Paint.Style.FILL);
mRedPaint.setShader(
        new RadialGradient(
                0, 0, 200,
                colors, pos,
                Shader.TileMode.CLAMP
        ));
canvas.drawCircle(0, 0, 250, mRedPaint);
複製程式碼
3.掃描漸變:SweepGradient

這個要比上面的簡單一點,沒有漸變的模式 雙色掃描漸變:SweepGradient(中心點x,y,顏色1,顏色2) 多色掃描漸變:SweepGradient(中心點x,y,顏色陣列,位置百分點陣列0~1)

掃描漸變.png

int colorStart = Color.parseColor("#84F125");
int colorEnd = Color.parseColor("#5825F1");
mRedPaint.setStyle(Paint.Style.FILL);
mRedPaint.setShader(
        new SweepGradient(0, 0, colorStart, colorEnd));
canvas.drawCircle(0, 0, 150, mRedPaint);

canvas.translate(400, 0);
int[] colors = new int[]{
        Color.parseColor("#F60C0C"),//紅
        Color.parseColor("#F3B913"),//橙
        Color.parseColor("#E7F716"),//黃
        Color.parseColor("#3DF30B"),//綠
        Color.parseColor("#0DF6EF"),//青
        Color.parseColor("#0829FB"),//藍
        Color.parseColor("#B709F4"),//紫
};
float[] pos = new float[]{
        1.f / 7, 2.f / 7, 3.f / 7, 4.f / 7, 5.f / 7, 6.f / 7, 1
};
mRedPaint.setShader(
        new SweepGradient(0, 0, colors, pos));
canvas.drawCircle(0, 0, 150, mRedPaint);
複製程式碼
4.圖片著色器:BitmapShader(圖片,著色模式x,著色模式y)

用圖片的所有畫素點作為畫筆的顏色

1).文字的圖片底色:
//載入圖片,生成圖片著色器
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.menu_bg);
BitmapShader bs = new BitmapShader(bitmap, Shader.TileMode.CLAMP,Shader.TileMode.CLAMP);
mRedPaint.setShader(bs);
mRedPaint.setTextSize(150);
mRedPaint.setStrokeWidth(10);
mRedPaint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawText("張風捷特烈", 0, 500, mRedPaint);
複製程式碼

圖片著色.png

2)路徑+圖片著色器實現裁剪圖片:路徑Path相關知識見上一篇:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.menu_bg);
BitmapShader bs = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mRedPaint.setShader(bs);

mRedPaint.setStyle(Paint.Style.FILL);
Path path = CommonPath.nStarPath(8, 500, 250);
canvas.drawPath(path, mRedPaint);
複製程式碼

使用路徑裁剪圖片.png

還有一個ComposeShader比較複雜,以後有需求會專門寫一篇


七、顏色過濾器:(具體原理是顏色運算,這裡全當玩玩看看吧,暫不深究)

ColorFilter只有三個子類

顏色過濾器.png

1.LightingColorFilter(顏色1,顏色2):

看圖體會一下吧...我不說了

LightingColorFilter測試.png

        Bitmap mainBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.menu_bg);
        mRedPaint.setStyle(Paint.Style.FILL);
        mRedPaint.setColorFilter(new LightingColorFilter(
                Color.parseColor("#FF0000"),//紅
                Color.parseColor("#0000ff")//藍
        ));
        canvas.drawBitmap(mainBitmap, 0, 0, mRedPaint);

        canvas.translate(350, 0);
        mRedPaint.setColorFilter(new LightingColorFilter(
                Color.parseColor("#FF0000"),//紅
                Color.parseColor("#00ff00")//綠
        ));
        canvas.drawBitmap(mainBitmap, 0, 0, mRedPaint);

        canvas.translate(350, 0);
        mRedPaint.setColorFilter(new LightingColorFilter(
                Color.parseColor("#FF0000"),//紅
                Color.parseColor("#000000")//黑
        ));
        canvas.drawBitmap(mainBitmap, 0, 0, mRedPaint);
        canvas.restore();
複製程式碼
2.PorterDuffColorFilter(顏色,模式--PorterDuff.Mode):

PorterDuff.Mode一共18中,在Color專題會詳述,這裡舉幾個栗子看看

PorterDuffColorFilter測試.png

Bitmap mainBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.menu_bg);
mRedPaint.setStyle(Paint.Style.FILL);

mRedPaint.setColorFilter(new PorterDuffColorFilter(
        Color.parseColor("#0000ff"), PorterDuff.Mode.DARKEN));
canvas.drawBitmap(mainBitmap, 0, 0, mRedPaint);

canvas.translate(350, 0);
mRedPaint.setColorFilter(new PorterDuffColorFilter(
        Color.parseColor("#0000ff"),PorterDuff.Mode.LIGHTEN
));
canvas.drawBitmap(mainBitmap, 0, 0, mRedPaint);

canvas.translate(350, 0);
mRedPaint.setColorFilter(new PorterDuffColorFilter(
        Color.parseColor("#0000ff"),PorterDuff.Mode.SCREEN
));
canvas.drawBitmap(mainBitmap, 0, 0, mRedPaint);

canvas.translate(350, 0);
mRedPaint.setColorFilter(new PorterDuffColorFilter(
        Color.parseColor("#0000ff"),PorterDuff.Mode.OVERLAY
));
canvas.drawBitmap(mainBitmap, 0, 0, mRedPaint);
複製程式碼
3.ColorMatrixColorFilter(顏色變換矩陣或20個float數)

剛好前端時間對ColorMatrix有一點研究,這裡拿來用用

顏色矩陣.png

ColorMatrix.png

        Bitmap mainBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.menu_bg);
        mRedPaint.setStyle(Paint.Style.FILL);

        //關閉RGB顏色通道(變為黑色),後偏移紅色255
        float[] matrix = new float[]{
                -1, 0, 0, 0, 255,
                0, -1, 0, 0, 0,
                0, 0, -1, 0, 0,
                0, 0, 0, 1, 0
        };
        ColorMatrix colorMatrix = new ColorMatrix(matrix);
        mRedPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
        canvas.drawBitmap(mainBitmap, 0, 0, mRedPaint);


        canvas.translate(350, 0);
        //關閉RGB顏色通道(變為黑色),後偏移藍色255
        float[] matrix2 = new float[]{
                -1, 0, 0, 0, 0,
                0, -1, 0, 0, 0,
                0, 0, -1, 0, 255,
                0, 0, 0, 1, 0
        };
        ColorMatrix colorMatrix2 = new ColorMatrix(matrix2);
        mRedPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix2));
        canvas.drawBitmap(mainBitmap, 0, 0, mRedPaint);

        canvas.translate(350, 0);
        //關閉RGB顏色通道(變為黑色),後偏移三色255
        float[] matrix3 = new float[]{
                -1, 0, 0, 0, 255,
                0, -1, 0, 0, 255,
                0, 0, -1, 0, 255,
                0, 0, 0, 1, 0
        };
        ColorMatrix colorMatrix3 = new ColorMatrix(matrix3);
        mRedPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix3));
        canvas.drawBitmap(mainBitmap, 0, 0, mRedPaint);

        canvas.translate(350, 0);

        float[] matrix4 = new float[]{
                //只要把RGB三通道的色彩資訊設定成一樣:即:R=G=B,
                // 為了保證影像亮度不變,同一個通道中的R+G+B=1
                0.3086f, 0.6094f, 0.0820f, 0, 0,
                0.3086f, 0.6094f, 0.0820f, 0, 0,
                0.3086f, 0.6094f, 0.0820f, 0, 0,
                0, 0, 0, 1, 0
        };
        ColorMatrix colorMatrix4 = new ColorMatrix(matrix4);
        mRedPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix4));
        canvas.drawBitmap(mainBitmap, 0, 0, mRedPaint);
複製程式碼

七、文字相關

字型.setTypeface(Typeface.SANS_SERIF);
對齊方式.setTextAlign(Paint.Align.LEFT);
字型大小.setTextSize(100);
複製程式碼
1.對齊方式與內建字型:

對齊:Paint.Align.[#LEFT|RIGHT|CENTER]
內建字型:Typeface.[#DEFAULT|DEFAULT_BOLD|SANS_SERIF|SERIF|MONOSPACE]

canvas.save();
canvas.translate(550, 1600);
mTextPaint.setTypeface(Typeface.SANS_SERIF);
mTextPaint.setTextAlign(Paint.Align.LEFT);
mTextPaint.setTextSize(100);
canvas.drawText("SANS_SERIF", 0, 0, mTextPaint);
Paint tempPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
tempPaint.setStrokeWidth(4);
tempPaint.setColor(Color.RED);
tempPaint.setStyle(Paint.Style.STROKE);
canvas.drawRect(0, - 100, mWinSize.x, 0, tempPaint);

canvas.translate(0, 150);
mTextPaint.setTextAlign(Paint.Align.RIGHT);
mTextPaint.setTypeface(Typeface.SERIF);
canvas.drawText("SERIF", 0, 0, mTextPaint);
canvas.drawRect(0, - 100, mWinSize.x, 0, tempPaint);

canvas.translate(0, 150);
mTextPaint.setTypeface(Typeface.MONOSPACE);
mTextPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("MONOSPACE", 0, 0, mTextPaint);
canvas.drawRect(0, - 100, mWinSize.x, 0, tempPaint);

canvas.restore();
複製程式碼

文字相關.png


2.建立字型:外部字型放在assets目錄下
/**
 * 建立字型
 * @param canvas
 */
private void createTypeface(Canvas canvas) {
    mTextPaint.setTextSize(50);
    canvas.save();
    canvas.translate(50, 1600);
    mTextPaint.setTypeface(Typeface.MONOSPACE);
    canvas.drawText("MONOSPACE", 0, 0, mTextPaint);
    //粗體
    canvas.translate(0, 100);
    Typeface typeface = Typeface.create(Typeface.MONOSPACE, Typeface.BOLD);
    mTextPaint.setTypeface(typeface);
    canvas.drawText("MONOSPACE+BOLD", 0, 0, mTextPaint);
    //斜體
    canvas.translate(0, 100);
    Typeface typeface2 = Typeface.create(Typeface.MONOSPACE, Typeface.ITALIC);
    mTextPaint.setTypeface(typeface2);
    canvas.drawText("MONOSPACE+ITALIC", 0, 0, mTextPaint);
    //粗斜體
    canvas.translate(0, 100);
    Typeface typeface3 = Typeface.create(Typeface.MONOSPACE, Typeface.BOLD_ITALIC);
    mTextPaint.setTypeface(typeface3);
    canvas.drawText("MONOSPACE+BOLD_ITALIC", 0, 0, mTextPaint);
    //使用外部字型
    canvas.translate(0, 100);
    Typeface myFont = Typeface.createFromAsset(getContext().getAssets(), "ACHAFSEX.TTF");
    mTextPaint.setTypeface(myFont);
    canvas.drawText("Hello I am Toly", 0, 0, mTextPaint);
    canvas.restore();
}
複製程式碼

建立字型.png


3.文字的測量:FontMetrics
1).字型測量類:FontMetrics
    public static class FontMetrics {
        public float   top;//最頂
        public float   ascent;//
        public float   leading;
        public float   descent;
        public float   bottom;//最底
    }
複製程式碼
2).SERIF字型測試:(試了一下不同字型這幾個屬性都不同的,有的字型相差很大)

字型5線.png

    /**
     * 獲取字型尺寸資訊
     * @param canvas
     */
    private void fontMetricsTest(Canvas canvas) {
        canvas.save();
        canvas.translate(100, 500);
        mTextPaint.setTextSize(200);
        mTextPaint.setTypeface(Typeface.SERIF);
        canvas.drawText("I am Toly", 0, 0, mTextPaint);
        //獲取字型尺寸
        Paint.FontMetrics fm = mTextPaint.getFontMetrics();

        Log.e(TAG, "top: " + fm.top);
        Log.e(TAG, "ascent: " + fm.ascent);
        Log.e(TAG, "leading: " + fm.leading);
        Log.e(TAG, "descent: " + fm.descent);
        Log.e(TAG, "bottom: " + fm.bottom);

        Paint tempPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        tempPaint.setStrokeWidth(1);
        tempPaint.setColor(Color.RED);
        tempPaint.setStyle(Paint.Style.STROKE);

        canvas.drawLine(0, fm.top, mWinSize.x, fm.top, tempPaint);

        tempPaint.setColor(Color.MAGENTA);
        canvas.drawLine(0, fm.ascent, mWinSize.x, fm.ascent, tempPaint);

        tempPaint.setColor(Color.parseColor("#4C17F9"));
        canvas.drawLine(0, fm.leading, mWinSize.x, fm.leading, tempPaint);

        tempPaint.setColor(Color.GREEN);
        canvas.drawLine(0, fm.descent, mWinSize.x, fm.descent, tempPaint);

        tempPaint.setColor(Color.parseColor("#E74EDD"));
        canvas.drawLine(0, fm.bottom, mWinSize.x, fm.bottom, tempPaint);

        canvas.restore();
    }

複製程式碼
2018-11-05 21:17:35.264 28726-28726/? E/PaintView: top: -209.57031
2018-11-05 21:17:35.264 28726-28726/? E/PaintView: ascent: -185.54688
2018-11-05 21:17:35.264 28726-28726/? E/PaintView: leading: 0.0
2018-11-05 21:17:35.264 28726-28726/? E/PaintView: descent: 48.828125
2018-11-05 21:17:35.264 28726-28726/? E/PaintView: bottom: 50.0
複製程式碼

3).獲取文字矩形區
String text = "I am Toly";
Rect textRect = new Rect();
mTextPaint.getTextBounds(text, 0, text.length(), textRect);
Log.e(TAG, textRect.toShortString());// [7,-152][886,49]
//繪製矩形
tempPaint.setColor(Color.parseColor("#66F4F628"));
tempPaint.setStyle(Paint.Style.FILL);
canvas.drawRect(textRect, tempPaint);
複製程式碼

文字最小矩形區.png


4).文字的變形操作
mTextPaint.setTextScaleX(.5f);//水平伸縮
mTextPaint.setStrikeThruText(true);//刪除線
mTextPaint.setUnderlineText(true);//下劃線
mTextPaint.setTextSkewX(-.5f);//傾斜
複製程式碼

其他效果.png

就先這樣吧,還有很多不常用的,以後有需求遇到了,在加吧

續--更新:

1.設定過渡消除鋸齒:setFilterBitmap效果

這個很簡單,就是true/false,預設false

setFilterBitmap.png

        Bitmap mainBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.menu_bg);
        Matrix matrix = new Matrix();
        matrix.setScale(5, 5);//放大矩陣
        
        mRedPaint.setFilterBitmap(false);
        canvas.drawBitmap(mainBitmap, matrix, mRedPaint);
        
        canvas.translate(-500, -300);
        mRedPaint.setFilterBitmap(true);
        canvas.drawBitmap(mainBitmap, matrix, mRedPaint);

複製程式碼
2.新增文字陰影:setShadowLayer(模糊半徑,x偏移,y偏移,顏色)

注意:圖片試了一下,不可以

        mRedPaint.setShadowLayer(100, 20, 20, Color.parseColor("#eeF39729"));
        mRedPaint.setTextSize(400);
        mRedPaint.setStrokeWidth(10);
        mRedPaint.setStyle(Paint.Style.FILL);
        
        //使用外接字型放在assets目錄下
        Typeface myFont = Typeface.createFromAsset(getContext().getAssets(), "CHOPS.TTF");
        mRedPaint.setTypeface(myFont);
        canvas.drawText("Toly", 300, 300, mRedPaint);
複製程式碼

新增文字陰影.png

對Path試了一下陰影效果,貌似只有Alpha值起作用,陰影跟著畫筆顏色走

mRedPaint.setShadowLayer(20, 4, 4, Color.parseColor("#335064F8"));
mRedPaint.setColor(Color.BLUE);
canvas.drawPath(CommonPath.regularStarPath(8, 200),mRedPaint);

canvas.translate(600, 0);
mRedPaint.setShadowLayer(20, 4, 4, Color.parseColor("#885064F8"));
canvas.drawBitmap(mainBitmap, matrix, mRedPaint);
複製程式碼

path陰影.png

後記:捷文規範

1.本文成長記錄及勘誤表
專案原始碼 日期 備註
V0.1--無 2018-11-7 Android關於Paint你所知道的和不知道的一切
V0.2--無 2018-11-9 增加setFilterBitmap和陰影測試
2.更多關於我
筆名 QQ 微信 愛好
張風捷特烈 1981462002 zdl1994328 語言
我的github 我的簡書 我的CSDN 個人網站
3.宣告

1----本文由張風捷特烈原創,轉載請註明
2----歡迎廣大程式設計愛好者共同交流
3----個人能力有限,如有不正之處歡迎大家批評指證,必定虛心改正
4----看到這裡,我在此感謝你的喜歡與支援


icon_wx_200.png

相關文章