綜述
關於顏色濾鏡的內容,將在另一篇文章記錄
文章內容多爲轉載,感謝相關作者的分享。具體參考文章在文章末尾已一一列出
一、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,SumPathEffectRasterizer,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 講解》