Android 2D 繪圖

綜述

關於顏色濾鏡的內容,將在另一篇文章記錄
文章內容多爲轉載,感謝相關作者的分享。具體參考文章在文章末尾已一一列出

一、2D 畫圖

View

只是把Graphic 資源(images,shapes,colors,pre-defined animation等等這些Android已經實現的一些畫圖操作)放入View體系,由系統 來將這些Graphic畫出來。
在這裏沒有一筆一畫地構造出一個圖形出來,即並沒有自己去定義畫圖操作,而是將這些內容放入View中,由系統來將這些內容畫出來。這種方式只能畫靜態或者極爲簡單的2D圖畫,對於實時性很強的動畫,高品質的遊戲都是沒法實現的

Canvas

這個 Canvas 是一個 2D 的概念,是在 Skia 中定義的。可以把這個 Canvas 理解成系統提供給我們的一塊內存區域(但實際上它只是一套畫圖的 API,真正的內存是下面的 Bitmap)。這種方式下我們能一筆一劃或者使用Graphic來畫我們所需要的東西了,要畫什麼要顯示什麼都由我們自己控制。
Canvas對象的獲取方式有兩種:一種我們通過重寫View.onDraw方法,View中的Canvas對象會被當做參數傳遞過來,我們操作這個Canvas,效果會直接反應在View中。另一種就是當你想創建一個Canvas對象時使用的方法:

Bitmap b = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);   
Canvas c =newCanvas(b);

上 面代碼創建了一個尺寸是100*100的Bitmap,使用它作爲Canvas操作的對象,這時候的Canvas就是使用創建的方式。當你使用創建的 Canvas在bitmap上執行繪製方法後,你還可以將繪製的結果提交給另外一個Canvas,這樣就可以達到兩個Canvas協作完成的效果,簡化邏 輯。

View canvas — 使用普通 View 的 Canvas 畫圖

(1)定義一個自己的View :class your_view extends View{} ;
(2)重載View的onDraw方法:protected void onDraw(Canvas canvas){} ;
(3)在onDraw方法中定義你自己的畫圖操作 ;
除此之外:可以定義自己的Btimap:
// 當自己新建 Canvas 時,需要自己創建一個 bitmap 來存儲和顯示繪圖結果
Bitmap b = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);// 必須將這個Bitmap放入View的canvas中,畫的 圖才能顯示出來

Surface View Canvas — 使用專門的 SurfaceView 的 Canvas 來畫圖

(1)定義一個自己的 SurfaceView : class your_surfaceview extends SurfaceView implements SurfaceHolder.Callback() {}
(2)實現 SurfaceHolder.Callback 的3個方法: surfaceCreated()、surfaceChanged() 、surfaceDestroyed() ;
(3)定義自己的專注於畫圖的線程: class your_thread extends Thread()
(4)重載線程的 run() 函數。一般在 run 中定義畫圖操作,在 surfaceCreated 中啓動這個線程

2D 繪圖相關 API

要掌握Android 2D Graphics必須要熟悉這三個包的各種API。

  • 繪圖基本要素:
    Canvas
    Paint
    Bitmap,BitmapFactory,BitmapRegionDecoder,ImageFormat,Movie,NinePatch,YuvImage

  • 過渡模式:
    Xfermode,AvoidXfermode,PixelXorXfermode,PorterDuffXfermode
    PorterDuff

  • 過濾:
    1、rgb過濾 ColorFilter ,ColorMatrixFilter,PorterDuffColorFilter,LightingColorFilter,PorterDuffColorFilter
    2、alpha過濾 MaskFilter,BlurMaskFilter,EmbossMaskFilter
    3、DrawFilter,PaintFlagsDrawFilter
    變換:
    Matrix,Camera,ColorMatrix

  • 顏色:
    Color

  • 漸變:
    Shader
    BitmapShader,ComposeShader,LinearGradient,RadialGradient,SweepGradient

  • 路徑
    Path:
    PathEffect,ComposePathEffect,CornerPathEffect,DashPathEffect,DiscretePathEffect,PathDashPathEffect,PathMeasure,SumPathEffect

  • Rasterizer,LayerRasterizer

  • Interpolator,

  • Picture

  • PixelFormat

  • Point,PointF,Rect,RectF

  • SurfaceTexture

  • Typeface

  • Region,RegionIterator

  • Drawable系列

  • Shape系列

總結 2D 畫圖用到的包

android.view //畫圖是在View中進行的
android.view.animation //定義了一些簡單的動畫效果Tween Animation 和 Frame. Animation 等
android.graphics //定義了畫圖比較通用的API,比如canvas,paint,bitmap等
android.graphics.drawable //定義了相應的Drawable(可畫的東西),比如說BitmapDrawable,PictureDrawable等
android.graphics.drawable.shapes //定義了一些shape

二、3D畫圖

針對 3D 畫圖 SDK 上講得很簡單,就是繼承一個 View,然後在這個 View 裏面獲得 Opengl 的句柄進行畫圖,道理和 2D 一樣的,差別就是一個是使用 2D 的 API 畫圖,一個是使用 3D 的。
因爲 3D openGl ES 具有一套本身的運行機制,比如渲染的過程控制等,因此 Android 提供了一個專門的畫圖類 GLSurfaceView。這個類被放在一個單獨的包 android.opengl 裏面,其實現了其他 View 所不具備的操作:
(1) 畫圖是在一個專門的 Surface 上進行, 這個 Surface 可以最後被組合到 android 的 View 體系中;
(2) Opengl 的運行週期可以與 Activity 的生命週期協調
(3) 具有 OpenGL ES 調用過程中的錯誤跟蹤,檢查工具,方便了 Opengl 編程過程的 debug
(4) 可以根據 EGL 的配置來選擇自己的 buffer 類型,比如 RGB565,depth=16
(5) 通過 render 來畫圖,而且 render 對 Opengl 的調用是在一個單獨的線程中
GLSurfaceView 是 Android 提供的一個非常值得學習的類,它實際上是一個如何在 View 中添加畫圖線程的例子,如何在 Java 中使用線程的例子,如何添加事件隊列的例子。具體的源碼在:/framworks/base/opengl/java/android/opengl/GLSurfaceView.java
GLSurface 畫 3D 圖形的步驟:
(1)選擇畫圖需要的buffer類型即:EGL配置 setEGLConfigChooser();
(2)選擇是否需要Debug信息
setDebugFlags(int)
setGLWrapper(GLSurfaceView.GLWrapper)
(3)爲 GLSurfaceView 註冊一個畫圖的 renderer
setRenderer(GLSurfaceView.Renderer)
(4) 設置 render mode:持續渲染或根據命令渲染, setRenderMode(int)
(5) Opengl 的運行和 Activity 的生命週期綁定在一起, 也就是說 Activity pause 的時候,opengl 的渲染也必須 pause。除此之外,GLSurfaceView 提供了線程間交互的函數 queueEvent(Runnable),可以用在主線程和 render 線程之間的交互

class MyGLSurfaceView extends GLSurfaceView {
    private MyRenderer mMyRenderer;
    public void start() {
        mMyRenderer = ...;
        setRenderer(mMyRenderer);
    }
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
            queueEvent(new Runnable() {
                 // This method will be called on the rendering
                 // thread:
                public void run() {
                    mMyRenderer.handleDpadCenter();
                }});
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }
}

Canvas Sample 及常用函數

常用方法

  • drawARGB / drawRGB / drawColor 使用單一的顏色填充畫布。
 canvas.drawColor(Color.BLUE);

這裏寫圖片描述

  • drawArc 在一個矩形區域的兩個角之間繪製一個弧。
// 參數依次爲:弧線所使用的矩形區域大小、開始角度、掃過的角度、是否使用中心
canvas.drawArc(rect, 0, 90, false, paint); 

這裏寫圖片描述

// 參數依次爲:弧線所使用的矩形區域大小、開始角度、掃過的角度、是否使用中心
canvas.drawArc(rect, 0, 90, true, paint); 

這裏寫圖片描述

  • drawBitmap 在畫布上繪製一個位圖。可以通過指定目標大小或者使用一個矩陣來改變目標位圖的外觀。

  • drawBitmapMesh 使用一個mesh(網)來繪製一個位圖,它可以通過移動網中的點來操作目標的外觀。

  • drawCircle 以給定的點爲圓心,繪製一個指定半徑的圓。
canvas.drawCircle(100, 100, 90, paint);

這裏寫圖片描述

  • drawLine(s) 在兩個點之間畫一條(多條)直線。
 canvas.drawLine(10, 10, 100, 100, paint);

這裏寫圖片描述

閉合區域

Paint paint=new Paint();
paint.setColor(Color.RED);  //設置畫筆顏色
paint.setStyle(Style.STROKE);//填充樣式改爲描邊
paint.setStrokeWidth(5);//設置畫筆寬度

Path path = new Path();

path.moveTo(10, 10); //設定起始點
path.lineTo(10, 100);//第一條直線的終點,也是第二條直線的起點
path.lineTo(300, 100);//畫第二條直線
path.lineTo(500, 100);//第三條直線
path.close();//閉環

canvas.drawPath(path, paint);

這裏寫圖片描述

矩形路徑

//先創建兩個大小一樣的路徑  
//第一個逆向生成  
Path CCWRectpath = new Path();  
RectF rect1 =  new RectF(50, 50, 240, 200);  
CCWRectpath.addRect(rect1, Direction.CCW);  

//第二個順向生成  
Path CWRectpath = new Path();  
RectF rect2 =  new RectF(290, 50, 480, 200);  
CWRectpath.addRect(rect2, Direction.CW);  

//先畫出這兩個路徑   
canvas.drawPath(CCWRectpath, paint);  
canvas.drawPath(CWRectpath, paint);  

//依據路徑寫出文字  
String text="風蕭蕭兮易水寒,壯士一去兮不復返";  
paint.setColor(Color.GRAY);  
paint.setTextSize(35);  
canvas.drawTextOnPath(text, CCWRectpath, 0, 18, paint);//逆時針生成  
canvas.drawTextOnPath(text, CWRectpath, 0, 18, paint);//順時針生成

兩個矩形一個是順時針,一個是逆時針。在繪製上沒有區別,主要區別在文字等基於路徑的繪製上
繪製其他形狀路徑時,情況類似,Paht 都有對應的 add 方法
這裏寫圖片描述
* drawOval 以指定的矩形爲邊界,畫一個橢圓。

//定義一個矩形區域
RectF oval =newRectF(0,0,200,300);
//矩形區域內切橢圓
canvas.drawOval(oval, paint);

這裏寫圖片描述

  • drawPaint 使用指定的Paint填充整個Canvas
  • drawPath 繪製指定的Path。Path對象經常用來保存一個對象中基本圖形的集合。
Path path =newPath();//定義一條路徑
path.moveTo(10, 10);//移動到 座標10,10
path.lineTo(50, 60);
path.lineTo(200,80);
path.lineTo(10, 10);
canvas.drawPath(path, paint);

這裏寫圖片描述

  • drawPicture 在指定的矩形中繪製一個Picture對象。
  • drawPosText 繪製指定了每一個字符的偏移量的文本字符串。
//按照既定點 繪製文本內容   
canvas.drawPosText("Android777",newfloat[]{   
        10,10,//第一個字母在座標10,10   
        20,20,//第二個字母在座標20,20   
        30,30,//....   
        40,40,   
        50,50,   
        60,60,   
        70,70,   
        80,80,   
        90,90,   
        100,100   
}, paint);

這裏寫圖片描述

  • drawRect 繪製一個矩形。
RectF rect =newRectF(50, 50, 200, 200);
canvas.drawRect(rect, paint);   

這裏寫圖片描述

  • drawRoundRect 繪製一個圓角矩形。
 RectF rect =newRectF(50, 50, 200, 200);
canvas.drawRoundRect(rect,   
                        30,//x軸的半徑   
                        30,//y軸的半徑   
                        paint);   

這裏寫圖片描述

  • drawText 在Canvas上繪製一個文本串。文本的字體、大小和渲染屬性都設置在用來渲染文本的Paint對象中。如:Paint.setTextSize(Size)
  • drawTextOnPath 在一個指定的path上繪製文本。
Path path =newPath();//定義一條路徑
path.moveTo(10, 10);//移動到 座標10,10
path.lineTo(50, 60);
path.lineTo(200,80);
path.lineTo(10, 10);
canvas.drawTextOnPath("Android777開發者博客", path, 10, 10, paint);  

這裏寫圖片描述

  • drawVertices 繪製一系列三角形面片,通過一系列頂點來指定它們。
  • drawPoint
    單個點
Paint paint=new Paint();  
paint.setColor(Color.RED);  //設置畫筆顏色      
paint.setStyle(Style.FILL);//設置填充樣式   
paint.setStrokeWidth(15);//設置畫筆寬度    
canvas.drawPoint(100, 100, paint);
![這裏寫圖片描述](https://img-blog.csdn.net/20151223160420685)

多個點

void drawPoints (float[] pts, Paint paint)
void drawPoints (float[] pts, int offset, int count, Paint paint)
Paint paint=new Paint();  
paint.setColor(Color.RED);  //設置畫筆顏色      
paint.setStyle(Style.FILL);//設置填充樣式   
paint.setStrokeWidth(15);//設置畫筆寬度  

float []pts={10,10,100,100,200,200,400,400};  
canvas.drawPoints(pts, 2, 4, paint);

drawPoints里路過前兩個數值,即第一個點橫縱座標,畫出後面四個數值代表的點,即第二,第三個點,第四個點沒畫
這裏寫圖片描述

sample

paint.setAntiAlias(true);
paint.setStyle(Style.STROKE);
canvas.translate(canvas.getWidth()/2, 200);//將位置移動畫紙的座標點:150,150
canvas.drawCircle(0, 0, 100, paint);//畫圓圈

//使用path繪製路徑文字
canvas.save();
canvas.translate(-75, -75);
Path path =newPath();
path.addArc(newRectF(0,0,150,150), -180, 180);
Paint citePaint =newPaint(paint);
citePaint.setTextSize(14);
citePaint.setStrokeWidth(1);
canvas.drawTextOnPath("http://www.android777.com", path, 28, 0, citePaint);
canvas.restore();

Paint tmpPaint =newPaint(paint);//小刻度畫筆對象
tmpPaint.setStrokeWidth(1);

float y=100;
int count = 60;//總刻度數

for(int i=0 ; i <count ; i++){
    if(i%5 == 0){
        canvas.drawLine(0f, y, 0, y+12f, paint);
        canvas.drawText(String.valueOf(i/5+1), -4f, y+25f, tmpPaint);
    }else{
        canvas.drawLine(0f, y, 0f, y +5f, tmpPaint);
    }
    canvas.rotate(360/count,0f,0f);//旋轉畫紙
}   

    //繪製指針   
    tmpPaint.setColor(Color.GRAY);   
    tmpPaint.setStrokeWidth(4);   
    canvas.drawCircle(0, 0, 7, tmpPaint);   
    tmpPaint.setStyle(Style.FILL);   
    tmpPaint.setColor(Color.YELLOW);   
    canvas.drawCircle(0, 0, 5, tmpPaint);   
    canvas.drawLine(0, 10, 0, -65, paint);   
![這裏寫圖片描述](https://img-blog.csdn.net/20151223152341925)

Canvas 變換

一個Canvas對象有四大基本要素: 1、一個用來保存像素的Bitmap 2、一個Canvas在Bitmap上進行繪製操作 3、繪製的內容 4、繪製的畫筆Paint Canvas還提供了一系列位置轉換的方法:rorate、scale、translate、skew(扭曲)等。 這些變換影響的是繪圖時相對於承載像素的 bitmap 的相對位置
canvas.drawColor(Color.BLUE);

canvas.save();
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
canvas.translate(20, 20);
canvas.drawRect(0, 0, 20, 20, paint);
canvas.restore();

canvas.save();
canvas.translate(60, 0);
canvas.scale(0.5f, 1);
canvas.drawRect(0, 0, 20, 20, paint);
canvas.restore();

canvas.drawRect(0, 0, 20, 20, paint);
![這裏寫圖片描述](https://img-blog.csdn.net/20151223231950748)

Canvas的保存和回滾

爲了方便一些轉換操作,Canvas還提供了保存和回滾屬性的方法(save和restore),比如你可以先保存目前畫紙的位置(save),然後旋轉90度,向下移動100像素後畫一些圖形,畫完後調用restore方法返回到剛纔保存的位置。 ![這裏寫圖片描述](https://img-blog.csdn.net/20151223232144723)

Canvas Layer

Canvas 在一般的情況下可以看作是一張畫布,所有的繪圖操作如drawBitmap, drawCircle都發生在這張畫布上,這張畫板還定義了一些屬性比如Matrix,顏色等等。但是如果需要實現一些相對複雜的繪圖操作,比如多層動畫,地圖(地圖可以有多個地圖層疊加而成,比如:政區層,道路層,興趣點層)。Canvas提供了圖層(Layer)支持,缺省情況可以看作是隻有一個圖層Layer。如果需要按層次來繪圖,Android的Canvas可以使用SaveLayerXXX, Restore 來創建一些中間層,對於這些Layer是按照“棧結構“來管理的: ![這裏寫圖片描述](https://img-blog.csdn.net/20151223232320929) 創建一個新的Layer到“棧”中,可以使用saveLayer, savaLayerAlpha, 從“棧”中推出一個Layer,可以使用restore,restoreToCount。但Layer入棧時,後續的DrawXXX操作都發生在這個Layer上,而Layer退棧時,就會把本層繪製的圖像“繪製”到上層或是Canvas上,在複製Layer到Canvas上時,可以指定Layer的透明度(Layer),這是在創建Layer時指定的:public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags)本例Layers 介紹了圖層的基本用法:Canvas可以看做是由兩個圖層(Layer)構成的,爲了更好的說明問題,我們將代碼稍微修改一下,缺省圖層繪製一個紅色的圓,在新的圖層畫一個藍色的圓,新圖層的透明度爲0×88。
 // private static final int LAYER_FLAGS = Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG
            | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.FULL_COLOR_LAYER_SAVE_FLAG
            | Canvas.CLIP_TO_LAYER_SAVE_FLAG;
 canvas.drawColor(Color.BLUE);
 Paint paint = new Paint();
canvas.translate(10, 10);
paint.setColor(Color.WHITE);
canvas.drawCircle(75, 75, 75, paint);
canvas.saveLayerAlpha(0, 0, 400, 400, 0x88, LAYER_FLAGS);
paint.setColor(Color.RED);
canvas.drawCircle(75, 125, 75, paint);
canvas.restore();
![這裏寫圖片描述](https://img-blog.csdn.net/20151223232818021)

Canvas 剪裁

裁剪功能由Canvas提供的一系列的clip…方法 和quickReject方法來完成。 前面已經提到,真正提供可繪製區域的是Canvas內部的mutable bitmap。 Canvas更像是一個圖層,我們只能在這上面的圖層來繪製東西。 裁剪Clip,即裁剪Canvas圖層,我們繪製的東西,只能在裁剪區域的範圍能才能顯示出來。 canvas.save()和canvas.restore()不僅對matrix有效,同樣對clip有類似的效果。 剪裁常用參數如下:
public enum Op {  
    DIFFERENCE(0), //最終區域爲region1 與 region2不同的區域  
    INTERSECT(1),  // 最終區域爲region1 與 region2相交的區域  
    UNION(2),      //最終區域爲region1 與 region2組合一起的區域  
    XOR(3),        //最終區域爲region1 與 region2相交之外的區域  
    REVERSE_DIFFERENCE(4), //最終區域爲region2 與 region1不同的區域  
    REPLACE(5);    //最終區域爲爲region2的區域  
 }
Paint paint=new Paint();
paint.setColor(Color.RED);
paint.setStrokeWidth(2);
paint.setStrokeMiter(100);
canvas.save();
canvas.clipRect(new Rect(100,100,300,300));
canvas.drawColor(Color.BLUE);//裁剪區域的rect變爲藍色
canvas.drawRect(new Rect(0,0,100,100), paint);//在裁剪的區域之外,不能顯示
canvas.drawCircle(150,150, 50, paint);//在裁剪區域之內,能顯示
canvas.restore();
canvas.drawCircle(100, 100, 100, paint);
![這裏寫圖片描述](https://img-blog.csdn.net/20151225173932524) clipRect和clipPath的最大區別在於 Canvas 的 matrix 對 clipRegion 沒有影響
Paint paint=new Paint();  
canvas.scale(0.5f, 0.5f);  
canvas.save();  
canvas.clipRect(new Rect(100,100,200,200));//裁剪區域實際大小爲50*50  
canvas.drawColor(Color.RED);  
canvas.restore();  

canvas.drawRect(new Rect(0,0,100,100), paint);//矩形實際大小爲50*50  

canvas.clipRegion(new Region(new Rect(300,300,400,400)));//裁剪區域實際大小爲100*100  
canvas.drawColor(Color.BLACK);  
![這裏寫圖片描述](https://img-blog.csdn.net/20151225174314693)

Sample

package com.example.jori.canvastest;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Region;
import android.view.View;

public class CustomView extends View {

    private Paint mPaint;
    private Path mPath;

    public CustomView(Context context) {
        super(context);

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStrokeWidth(6);
        mPaint.setTextSize(16);
        mPaint.setTextAlign(Paint.Align.RIGHT);

        mPath = new Path();
    }

    private void drawScene(Canvas canvas) {
        canvas.clipRect(0, 0, 100, 100);
        canvas.drawColor(Color.YELLOW);

        mPaint.setColor(Color.RED);
        canvas.drawLine(0, 0, 100, 100, mPaint);

        mPaint.setColor(Color.GREEN);
        canvas.drawCircle(30, 70, 30, mPaint);

        mPaint.setColor(Color.BLUE);
        canvas.drawText("Hello", 100, 30, mPaint);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.GRAY);

        canvas.save();
        canvas.translate(10, 10);
        drawScene(canvas);
        canvas.restore();

        canvas.save();
        canvas.translate(160, 10);
        canvas.clipRect(10, 10, 90, 90);
        canvas.clipRect(30, 30, 70, 70, Region.Op.DIFFERENCE);
        drawScene(canvas);
        canvas.restore();

        canvas.save();
        canvas.translate(10, 120);
        mPath.reset();
        canvas.clipPath(mPath);
        mPath.addCircle(50, 50, 50, Path.Direction.CCW);
        canvas.clipPath(mPath, Region.Op.REPLACE);
        drawScene(canvas);
        canvas.restore();

        canvas.save();
        canvas.translate(160, 120);
        canvas.clipRect(0, 0, 60, 60);
        canvas.clipRect(40, 40, 100, 100, Region.Op.UNION);
        drawScene(canvas);
        canvas.restore();

        canvas.save();
        canvas.translate(10, 240);
        canvas.clipRect(0, 0, 60, 60);
        canvas.clipRect(40, 40, 100, 100, Region.Op.XOR);
        drawScene(canvas);
        canvas.restore();

        canvas.save();
        canvas.translate(160, 240);
        canvas.clipRect(0, 0, 60, 60);
        canvas.clipRect(40, 40, 100, 100, Region.Op.REVERSE_DIFFERENCE);
        drawScene(canvas);
        canvas.restore();
    }
}
![這裏寫圖片描述](https://img-blog.csdn.net/20151225232338857)

Region

clipRegion(Region region) 方法已被棄用,原因不知。 由於 clipRegion 會忽略 Matrix 變換,所以下面的代碼可能不是你想的樣子
// 此 View 已設置 Padding 爲 16p
canvas.drawColor(Color.BLUE);
Region reginLeft = new Region();
reginLeft.set(10, 10, 300, 300);
reginLeft.op(100, 100, 400, 400, Region.Op.UNION);
canvas.clipRegion(reginLeft);
canvas.drawColor(Color.RED);
![這裏寫圖片描述](https://img-blog.csdn.net/20151225235127033)

Paint 常用屬性

  • setColor()
Paint.setColor(Color.BLUE);
  • setARGB(a, r, g, b)

  • setStrokeWidth() // 線寬

  • setAlpha() // 設置透明度

  • setAntiAlias() // 抗鋸齒

  • setStyle() // 填充方式

    Paint.Style.FILL :填充內部,會忽略任何和 stroke 相關的參數
    Paint.Style.FILL_AND_STROKE :填充內部和描邊
    Paint.Style.STROKE :僅描邊

這裏寫圖片描述

  • setShadowLayer (float radius, float dx, float dy, int color) 添加陰影
paint.setShadowLayer(10, 15, 15, Color.GREEN);//設置陰影

這裏寫圖片描述

文字相關

  • paint.setTextAlign(Align.CENTER)
    設置文字對齊方式,取值:align.CENTER、align.LEFT或align.RIGHT
  • paint.setTextSize(12)
    設置文字大小
  • paint.setFakeBoldText(true)
    設置是否爲粗體文字
  • paint.setUnderlineText(true)
    設置下劃線
  • paint.setTextSkewX((float) -0.25)
    設置字體水平傾斜度,普通斜體字是-0.25
  • paint.setStrikeThruText(true)
    設置帶有刪除線效果
  • paint.setTextScaleX(2)
    只會將水平方向拉伸,高度不會變

效果如下所示

這裏寫圖片描述

這裏寫圖片描述

這裏寫圖片描述

第一行,水平未拉伸的字體;第二行,水平拉伸兩倍的字體;第三行,水平未拉伸和水平拉伸兩部的字體寫在一起,可以發現,僅是水平方向拉伸,高度並未改變

字體相關

Typeface是專門用來設置字體樣式的,通過paint.setTypeface()來指定。可以指定系統中的字體樣式,也可以指定自定義的樣式文件中獲取。要構建Typeface時,可以指定所用樣式的正常體、斜體、粗體等,如果指定樣式中,沒有相關文字的樣式就會用系統默認的樣式來顯示,一般默認是宋體。

Typeface font = Typeface.create(familyName,Typeface.NORMAL);  
paint.setTypeface(font); 
  • Typeface create(String familyName, int style)
    直接通過指定字體名來加載系統中自帶的文字樣式
  • Typeface create(Typeface family, int style)
    通過其它Typeface變量來構建文字樣式
  • Typeface createFromAsset(AssetManager mgr, String path)
    通過從Asset中獲取外部字體來顯示字體樣式
  • Typeface createFromFile(String path)
    直接從路徑創建
  • Typeface createFromFile(File path)
    從外部路徑來創建字體樣式
  • Typeface defaultFromStyle(int style)
    創建默認字體

上面的各個參數都會用到Style變量,Style的枚舉值如下:

  • Typeface.NORMAL //正常體
  • Typeface.BOLD //粗體
  • Typeface.ITALIC //斜體
  • Typeface.BOLD_ITALIC //粗斜體

Xfermode

AvoidXfermode 指定了一個顏色和容差,強制Paint避免在它上面繪圖(或者只在它上面繪圖)。

PixelXorXfermode 當覆蓋已有的顏色時,應用一個簡單的像素XOR操作。

PorterDuffXfermode 這是一個非常強大的轉換模式,使用它,可以使用圖像合成的16條Porter-Duff規則的任意一條來控制Paint如何與已有的Canvas圖像進行交互。

要應用轉換模式,可以使用setXferMode方法,如下所示:

AvoidXfermode avoid = new AvoidXfermode(Color.BLUE, 10, AvoidXfermode.Mode. AVOID);    borderPen.setXfermode(avoid);

Porter-Duff 效果圖:
這裏寫圖片描述

參數及效果

PorterDuff.Mode 中不同 mode 算法中符號的意義爲:

  • Sa 代表source alpha ,即源 alpha 值
  • Da 代表 Destination alpha ,即 目標alpha值
  • Sc 代表 source color ,即源色值
  • Dc 代表 Destination color ,即目標色值

這所有的計算都以像素爲單位,在某一種混合模式下,對每一個像素的alpha 和 color 通過對應算法進行運算,得出新的像素值,進行展示

int saveCount = canvas.saveLayer(0, 0, mTotalWidth, mTotalHeight, mPaint, Canvas.ALL_SAVE_FLAG);

canvas.drawBitmap(mBottomBitmap, mBottomSrcRect, mBottomDestRect, mPaint);

mPaint.setXfermode(mPorterDuffXfermode);
canvas.drawBitmap(mTopBitmap, mTopSrcRect, mTopDestRect, mPaint);

mPaint.setXfermode(null);
canvas.restoreToCount(saveCount);

下面爲原圖效果:
這裏寫圖片描述
這裏寫圖片描述

下面所用代碼默認繪製效果:
這裏寫圖片描述

  • PorterDuff.Mode.CLEAR
    所繪製不會提交到畫布上。清除模式[0,0],即最終所有點的像素的alpha 和color 都爲 0,所以畫出來的效果只有白色背景
    這裏寫圖片描述
  • PorterDuff.Mode.SRC
    顯示上層繪製圖片。只保留源圖像的 alpha 和 color ,所以繪製出來只有源圖
    這裏寫圖片描述
  • PorterDuff.Mode.DST
    顯示下層繪製圖片。同上類比,只保留目標圖像的 alpha 和 color,所以繪製出來的只有目標圖
    這裏寫圖片描述
  • PorterDuff.Mode.SRC_OVER
    正常繪製顯示,上下層繪製疊蓋
    這裏寫圖片描述
  • PorterDuff.Mode.DST_OVER
    上下層都顯示。下層居上顯示
    這裏寫圖片描述
  • PorterDuff.Mode.SRC_IN
    取兩層繪製交集。顯示上層。在兩者相交的地方繪製源圖像,並且繪製的效果會受到目標圖像對應地方透明度的影響
    這裏寫圖片描述
  • PorterDuff.Mode.DST_IN
    取兩層繪製交集。顯示下層。原理類比上面
    這裏寫圖片描述
  • PorterDuff.Mode.SRC_OUT
    取上層繪製非交集部分。在不相交的地方繪製 源圖像,效果受兩者 alpha 值影響。(對於效果的疑問可以參考源碼中對每個像素的計算公式)
    這裏寫圖片描述
  • PorterDuff.Mode.DST_OUT
    取下層繪製非交集部分
    這裏寫圖片描述
  • PorterDuff.Mode.SRC_ATOP
    取下層非交集部分與上層交集部分
    這裏寫圖片描述
  • PorterDuff.Mode.DST_ATOP
    取上層非交集部分與下層交集部分
    這裏寫圖片描述
  • PorterDuff.Mode.XOR
    公式:[ Sa + Da - 2 * Sa * Da, Sc * ( 1 - Da ) + ( 1 - Sa ) * Dc ]
    在不相交的地方按原樣繪製源圖像和目標圖像,相交的地方受到對應alpha和色值影響,按上面公式進行計算,如果都完全不透明則相交處完全不繪製
    這裏寫圖片描述
  • PorterDuff.Mode.DARKEN
    公式:[ Sa + Da - Sa * Da , Sc * ( 1 - Da ) + Dc * ( 1 - Sa ) + min(Sc , Dc) ]
    該模式處理過後,會感覺效果變暗,即進行對應像素的比較,取較暗值,如果色值相同則進行混合;
    從算法上看,alpha值變大,色值上如果都不透明則取較暗值,非完全不透明情況下使用上面算法進行計算,受到源圖和目標圖對應色值和alpha值影響
    這裏寫圖片描述
  • PorterDuff.Mode.LIGHTEN
    公式:[ Sa + Da - Sa * Da , Sc * ( 1 -Da ) + Dc * ( 1 - Sa ) + max ( Sc , Dc ) ]
    可以和 DARKEN 對比起來看,DARKEN 的目的是變暗,LIGHTEN 的目的則是變亮,如果在均完全不透明的情況下 ,色值取源色值和目標色值中的較大值,否則按上面算法進行計算
    這裏寫圖片描述
  • PorterDuff.Mode.MULTIPLY
    正片疊底,即查看每個通道中的顏色信息,並將基色與混合色複合。結果色總是較暗的顏色。任何顏色與黑色複合產生黑色。任何顏色與白色複合保持不變。當用黑色或白色以外的顏色繪畫時,繪畫工具繪製的連續描邊產生逐漸變暗的顏色。
    這裏寫圖片描述
  • PorterDuff.Mode.SCREEN
    濾色,濾色模式與我們所用的顯示屏原理相同,所以也有版本把它翻譯成“屏幕”;
    簡單的說就是保留兩個圖層中較白的部分,較暗的部分被遮蓋;
    當一層使用了濾色(屏幕)模式時,圖層中純黑的部分變成完全透明,純白部分完全不透明,其他的顏色根據顏色級別產生半透明的效果
    這裏寫圖片描述

    注:
    canvas原有的圖片可以理解爲背景,就是dst;新畫上去的圖片可以理解爲前景,就是src。

當有多個繪製時,先繪製的是目標圖

參考文章

《android Graphics(一):概述及基本幾何圖形繪製》
《 Android 2D Graphics學習(一)、android.graphics介紹》
《Android 2D Graphics學習(二)、Canvas篇1、Canvas基本使用》
《Android 2D Graphics學習(二)、Canvas篇2、Canvas裁剪和Region、RegionIterator》
《android之Paint屬性介紹》
《Android Paint之 setXfermode PorterDuffXfermode 講解》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章