Canvas和Paint的關系
在對UI繪制流程的分析中,可以知道performCanvas()
方法最終會調用到View#onDraw()
方法,並且會傳遞Canvas
,那么Canvas
到底有什么用呢?
Canvas
在UI繪制中扮演的是會話角色
,我們通常情況下都能夠知道使用Canvas去 畫圓型,矩形圖片等,但是最終其真正的繪制並不是在我們的android 層面進行的。
通過源碼分析,Canvas
繼承了BaseCanvas
public class Canvas extends BaseCanvas {}
在BaseCanvas
中,我們可以發現有大量以nDraw
開頭的方法,比如drawCircle
。
public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint) {
throwIfHasHwBitmapInSwMode(paint);
nDrawCircle(mNativeCanvasWrapper, cx, cy, radius, paint.getNativeInstance());
}
private static native void nDrawCircle(long nativeCanvas, float cx, float cy, float radius, long nativePaint);
通過上述代碼塊我們可以知道,Canvas並不是具體的執行者,而是一個傳達着, 在Canvas當中我們會將所有的參數信息設置好,然后交由底層去繪制。
我們通過觀察Canvas中的繪制方法,可以發現每一個繪制方法中都有一個Paint
類型的參數,那么Paint的職責到底是什么呢?打開Paint的源碼,可以看到:
/**
* The Paint class holds the style and color information about how to draw
* geometries, text and bitmaps.
*/
public class Paint {}
注釋中描述了Paint 在繪制過程中保存了色彩信息和樣式信息。
Paint基礎
Paint方法主要可以抽象成2大類:
設置和獲取圖像繪制、路徑相關數據
設置畫筆樣式
// 設置畫筆填充樣式
paint.style = Paint.Style.STROKE
Paint.Style.FILL :填充內部
Paint.Style.FILL_AND_STROKE :填充內部和描邊
Paint.Style.STROKE :僅描邊
注意 STROKE、FILL_AND_STROKE與FILL模式相比 外輪廓的位置會擴大。
設置畫筆寬度
// 設置畫筆寬度
paint.strokeWidth = 200.0f
抗鋸齒功能
- 設置抗鋸齒
// 開啟抗鋸齒
paint.isAntiAlias = true
- 獲取是否開啟抗鋸齒
val isAntiAlias = paint.isAntiAlias
開啟抗鋸齒功能會消耗較大資源,繪制圖形速度會變慢,但開啟后繪制圖像會平滑一些。
設置線冒樣式
// 設置圓形線冒
paint.strokeCap = Paint.Cap.ROUND
Cap.ROUND(圓形線冒)、 Cap.SQUARE(方形線冒) 、Paint.Cap.BUTT(無線冒)
效果圖:
代碼如下:
val paint = Paint()
fun init() {
// 設置畫筆填充樣式
paint.style = Paint.Style.STROKE
// 開啟抗鋸齒
paint.isAntiAlias = true
// 設置畫筆顏色
paint.color = Color.RED
// 設置畫筆寬度
paint.strokeWidth = 200.0f
// 設置圓形線冒
paint.strokeCap = Paint.Cap.ROUND
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.drawLine(200f, 300f, 400f, 300f, paint)
}
設置連接處線段
// 設置線段鏈接處的樣式
paint.strokeJoin = Paint.Join.MITER
Join.MITER(結合處為銳角)、Join.Round(結合處為圓弧)、Join.BEVEL(結合處為直線)
效果圖:
代碼如下:
// 創建畫筆
private val paint: Paint = Paint()
// 創建矩形區域
private val rect: Rect = Rect()
init {
// 設置畫筆填充樣式
paint.style = Paint.Style.STROKE
// 開啟抗鋸齒
paint.isAntiAlias = true
// 設置畫筆顏色
paint.color = Color.RED
// 設置畫筆寬度
paint.strokeWidth = 200.0f
// 設置線段鏈接處於樣式
paint.strokeJoin = Paint.Join.ROUND
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
rect.set(200, 300, 400, 400)
canvas.drawRect(rect, paint)
}
設置畫筆傾斜度
// 設置畫筆傾斜度
paint.strokeMiter = 0.8f
清空畫筆復位
// 畫筆復位
paint.reset()
設置外來畫筆
// 設置一個新的畫筆
paint.set(Paint())
獲取與設置alpha值,顏色和ARGB
// 透明度 0 -100
// r、g、b 0-255
paint.setARGB(80,110,234, 125)
// 設置透明度 0(完全透明) - 100 (不透明)
paint.alpha = 100
// 獲取透明度
val alpha = paint.alpha
// 設置顏色
paint.color = Color.BLUE
// 獲取顏色
val color = paint.color
圖像抖動處理
- 開啟圖像抖動
// 開啟圖像抖動
paint.isDither = true
- 獲取圖像抖動是否開啟
// 開啟圖像抖動
val isDither = paint.isDither
會使繪制出來的圖像顏色更加平滑、飽滿和圖像更加清晰
設置繪制路徑效果
// 設置虛線
// intervals: 控制實線和實線之后空白線的寬度(數組長度必須為偶數)
// phase: 將View向”左“偏移phase
paint.pathEffect = DashPathEffect(intervals = floatArrayOf(20f,10f,50f,100f), phase = 15f)
CornerPathEffect ——圓形拐角效果
paint.setPathEffect(new CornerPathEffect(100))
利用半徑R=50的圓來代替原來兩條直線間的夾角
DashPathEffect ——虛線效果 畫同一條線段,偏移值為15
paint.setPathEffect(new DashPathEffect(new float[]{20,10,50,100},15))
intervals[]:表示組成虛線的各個線段的長度;整條虛線就是由intervals[]中這些基本線段循環組成的。比如,我們定義new float[] {20,10}; 那這個虛線段就是由兩段線段組成的,第一個可見的線段長為20,每二個線段不可見,長度為10;
phase:開始繪制的偏移值
設置圖形重疊時的處理方式
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.MULTIPLY)
通過設置xfermode 可以設置許多不同的效果,如 制作橡皮擦, 具體介紹請看 Paint的高級用法
設置MaskFilter
paint.maskFilter = MaskFilter()
可以用不同的MaskFilter實現濾鏡的效果,如濾化,立體等 。
/**
* Create a blur maskfilter.
*
* @param radius 陰影的半徑
* @param style NORMOL -- 整個圖像都被模糊掉
* SOLID -- 圖像邊界外產生一層與Paint顏色一致陰影效果,不影響圖像的本身
* OUTER -- 圖像邊界外產生一層陰影,並且將圖像變成透明效果
* INNER -- 在圖像內部邊沿產生模糊效果
* @return
*/
paint.setMaskFilter(new BlurMaskFilter(50, BlurMaskFilter.Blur.NORMAL));
/**
* Create an emboss maskfilter
*
* @param direction 指定光源的位置,長度為xxx的數組標量[x,y,z]
* @param ambient 環境光的因子 (0~1),越接近0,環境光越暗
* @param specular 鏡面反射系數 越接近0,鏡面反射越強
* @param blurRadius 模糊半徑 值越大,模糊效果越明顯
*/
paint.setMaskFilter(new EmbossMaskFilter(new float[]{1,1,1},0.2f,60,80));
設置顏色過濾器
paint.colorFilter = ColorFilter()
可以在繪制顏色時實現不用顏色的變換效果,可以用來設置圖像的黑白效果。
設置圖像效果
paint.shader = Shader()
使用Shader可以繪制出各種漸變效果。Shader有一些子類,如:LinearGradient。
在圖像下設置陰影層
// radius 為陰影的角度
// dx和dy為陰影在x軸和y軸上的距離
// color為陰影的顏色
paint.setShadowLayer(float radius ,float dx,float dy,int color)
設置獲取文字相關的
獲取字符的行間距
val spacing = paint.fontSpacing
設置和獲取字符的間距
// 獲取字符的間距 api >= 21
val letterSpacing = paint.letterSpacing
// 設置字符的間距 api >= 21
paint.letterSpacing = 20f
是否有下划線和設置下划線
// 獲取是否有下划線
val isUnderlineText = paint.isUnderlineText
// 設置下划線
paint.isUnderlineText = true
獲取與設置是否有文本刪除線
// 設置文本刪除線
paint.isStrikeThruText = true
// 獲取文本刪除線
val isStrikeThruText = paint.isStrikeThruText
獲取與設置文字大小
// 獲取文字大小
val textSize = paint.textSize
// 設置文字大小
paint.textSize = 14f
注意: Paint.setTextSize
單位是px
,TextView.setTextSize
單位是sp
。
獲取與設置字體類型
// 設置字體樣式
paint.typeface = Typeface.DEFAULT
val typeface = paint.typeface
默認有四種字體樣式: BOLD(加粗、BOLD_ITALIC(加粗並傾斜)、ITALIC(傾斜)、NORMAL(正常)
也可通過Typeface類來自定義個性化字體。
獲取與設置文字傾斜
// 設置文字傾斜度
paint.textSkewX = -0.25f
val textSkewX = paint.textSkewX
參數沒有具體范圍, 官方推薦值為-0.25,值為負則右傾,為正則左傾,默認值為0。
獲取與設置文本對齊方式
// 設置文本的對齊方式
paint.textAlign = Paint.Align.CENTER
val textAlign = paint.textAlign
取值為 CENTER、LEFT、RIGHT,即文字繪制是左邊對齊、右邊還是局中。
亞像素
// 設置亞像素
paint.isSubpixelText = true
固定的幾個范圍:320480,480800,7201280,10801920等,那么如何在同樣的分辨率的顯示器中增強顯示清晰度呢?
亞像素的概念就油然而生了,亞像素就是把兩個相鄰的兩個像素之間的距離再細分,再插入一些像素,這些通過程序加入的像素就是亞像素。在兩個像素間插入的像素個數是通過程序計算出來的,一般是插入兩個、三個或四個。 所以打開亞像素顯示,是可以在增強文本顯示清晰度的,但由於插入亞像素是通過程序計算而來的,所以會耗費一定的計算機性能。
文本折斷
paint.breakText(String text, boolean measureForwards, float maxWidth, float[] measuredWidth)
如文本閱讀器的翻頁效果,我們需要在翻頁的時候動態折斷或生成一行字符串。
ps: xiaowujiang.cn