Jetpack?Compose?Canvas繪制超詳細(xì)介紹
1. Canvas
@Composable fun Canvas( modifier: Modifier, onDraw: DrawScope.() -> Unit ) = Spacer(modifier.drawBehind(onDraw))
modifier:這里主要作用是指定畫布的大小。onDraw就是執(zhí)行具體的繪制??梢钥吹剿峁┝艘粋€(gè)繪圖環(huán)境的作用域DrawScope,這里提供有我們經(jīng)常使用的繪圖api和屬性,比如drawLine、size等。
先來一個(gè)簡單的例子看看如何使用:
Canvas(modifier = Modifier.fillMaxSize()) {
val canvasWidth = size.width
val canvasHeight = size.height
drawLine(
start = Offset(x = canvasWidth, y = 0f),
end = Offset(x = 0f, y = canvasHeight),
color = Color.Blue
)
}畫一條線,開始和結(jié)束位置分別是畫布的右上角和左下角。效果如下:

2. 繪制方法
1. drawLine
drawLine在上面的例子中簡單說明了,當(dāng)然它不止這些功能。
fun drawLine(
color: Color, //或 brush: Brush,
start: Offset,
end: Offset,
strokeWidth: Float = Stroke.HairlineWidth,
cap: StrokeCap = Stroke.DefaultCap,
pathEffect: PathEffect? = null,
/*FloatRange(from = 0.0, to = 1.0)*/
alpha: Float = 1.0f,
colorFilter: ColorFilter? = null,
blendMode: BlendMode = DefaultBlendMode
)- 指定線的顏色用
color - 漸變色可以使用
brush,本系列第三篇有說明。 strokeWidth是線的寬度,默認(rèn)是1px。cap是線頭的形狀,默認(rèn)是StrokeCap.Butt平頭。還有StrokeCap.Round圓頭,StrokeCap.Square方頭。這部分和Android中的Paint是一樣的。平頭和方頭的不同在于是否延伸出來的部分。

pathEffect是線段的效果,比如虛線這種就是使用PathEffect.dashPathEffect(intervals: FloatArray, phase: Float = 0f) ,舉一個(gè)例子:
PathEffect.dashPathEffect(floatArrayOf(20f, 10f), 10f)
intervals中的20f表示虛線的寬度,10f是間隔寬度。phase的10f表示初始的偏移距離。所以一開始偏移10f,就會導(dǎo)致第一段的線段被"裁剪"10f,具體效果如下圖:

alpha是線段的透明度。colorFilter是顏色過濾器,本系列第四篇有說明,這里就不重復(fù)介紹了。blendMode:混合模式。這個(gè)不在本篇的范圍內(nèi),后面有機(jī)會我會詳細(xì)說一下。有興趣可以先看看文末的參考文章。
2. drawRect
繪制矩形方法,屬性與drawLine大同小異,下面說一些不同點(diǎn)。
fun drawRect(
color: Color,
topLeft: Offset = Offset.Zero,
size: Size = this.size.offsetSize(topLeft),
/*@FloatRange(from = 0.0, to = 1.0)*/
alpha: Float = 1.0f,
style: DrawStyle = Fill,
colorFilter: ColorFilter? = null,
blendMode: BlendMode = DefaultBlendMode
)
topLeft是用來指定左上角的偏移量,如果沒有指定那么默認(rèn)從當(dāng)前畫布左上角開始。size用來指定矩形大小,如果沒有指定那么默認(rèn)就是當(dāng)前畫布的大小。style是指實(shí)心還是空心。默認(rèn)Fill實(shí)心,Stroke空心。
3. drawRoundRect
繪制圓角矩形基本與矩形一致,只是多了一個(gè)設(shè)置圓角大小的參數(shù)drawRoundRect,這里就不多說明了。
4. drawImage
繪制圖片方法
fun drawImage(
image: ImageBitmap,
srcOffset: IntOffset = IntOffset.Zero,
srcSize: IntSize = IntSize(image.width, image.height),
dstOffset: IntOffset = IntOffset.Zero,
dstSize: IntSize = srcSize,
/*@FloatRange(from = 0.0, to = 1.0)*/
alpha: Float = 1.0f,
style: DrawStyle = Fill,
colorFilter: ColorFilter? = null,
blendMode: BlendMode = DefaultBlendMode
)
image:需要繪制的圖片,具體可以使用ImageBitmap.imageResource(id = R.drawable.xxx)方法獲取。srcOffset:需要繪制圖片的左上角偏移量,默認(rèn)為圖像的原點(diǎn)。srcSize: 圖片相對于srcOffset的尺寸,默認(rèn)為圖像的寬高。dstOffset: 繪制圖片的相對左上角的偏移量,這默認(rèn)為圖像的原點(diǎn)。dstSize:繪制圖片的大小,默認(rèn)為srcSize。
下面的代碼是繪制一張圖片的右下角區(qū)域,相對畫布偏移50 * 50,繪制的大小是200 * 200。
val imageBitmap = ImageBitmap.imageResource(id = R.mipmap.ic_launcher)
Canvas(modifier = Modifier.fillMaxSize()) {
drawImage(
image = imageBitmap,
srcOffset = IntOffset(imageBitmap.width / 2,imageBitmap.height / 2),
srcSize = IntSize(imageBitmap.width, imageBitmap.height),
dstOffset = IntOffset(50,50),
dstSize = IntSize(200,200)
)
}效果如下:

5. drawCircle
繪制圓形方法
fun drawCircle(
color: Color,
radius: Float = size.minDimension / 2.0f,
center: Offset = this.center,
/*@FloatRange(from = 0.0, to = 1.0)*/
alpha: Float = 1.0f,
style: DrawStyle = Fill,
colorFilter: ColorFilter? = null,
blendMode: BlendMode = DefaultBlendMode
)radius:圓的半徑大小。center:圓心位置。
6. drawArc
drawArc可以用來繪制弧形或是扇形
fun drawArc(
color: Color,
startAngle: Float,
sweepAngle: Float,
useCenter: Boolean,
topLeft: Offset = Offset.Zero,
size: Size = this.size.offsetSize(topLeft),
/*@FloatRange(from = 0.0, to = 1.0)*/
alpha: Float = 1.0f,
style: DrawStyle = Fill,
colorFilter: ColorFilter? = null,
blendMode: BlendMode = DefaultBlendMode
)
startAngle: 開始角度sweepAngle: 弧線掃過的角度useCenter: 弧線是否過圓心
這里就不舉例說明了,可以用style、useCenter屬性自行組合嘗試。
7. drawPath
繪制路徑方法
fun drawPath(
path: Path,
color: Color,
/*@FloatRange(from = 0.0, to = 1.0)*/
alpha: Float = 1.0f,
style: DrawStyle = Fill,
colorFilter: ColorFilter? = null,
blendMode: BlendMode = DefaultBlendMode
)
其實(shí)和Android中的path使用一樣,圍繞著moveTo、lineTo、close這些方法,也就不詳細(xì)說明了。
8. drawPoints
繪制點(diǎn)的方法
fun drawPoints(
points: List<Offset>,
pointMode: PointMode,
color: Color,
strokeWidth: Float = Stroke.HairlineWidth,
cap: StrokeCap = StrokeCap.Butt,
pathEffect: PathEffect? = null,
/*@FloatRange(from = 0.0, to = 1.0)*/
alpha: Float = 1.0f,
colorFilter: ColorFilter? = null,
blendMode: BlendMode = DefaultBlendMode
)
points:點(diǎn)的偏移位置pointMode:點(diǎn)的繪制模式,PointMode.Points分別畫出每個(gè)點(diǎn)。PointMode.Lines每兩個(gè)點(diǎn)畫成一條線段。 如果點(diǎn)數(shù)是奇數(shù),則忽略最后一個(gè)點(diǎn)。PointMode.Polygon連接所有的點(diǎn)。
最后還有一個(gè)繪制橢圓方法drawOval,用法大同小異,就不說明了。
3. DrawScope拓展方法
1. inset
同時(shí)從左到上轉(zhuǎn)換DrawScope坐標(biāo)空間,并修改當(dāng)前繪制區(qū)域的尺寸。
inline fun DrawScope.inset(
left: Float,
top: Float,
right: Float,
bottom: Float,
block: DrawScope.() -> Unit
) {...}
inset有點(diǎn)像是在原有的畫布上,嵌入了一個(gè)"新"的畫布,設(shè)置的left,top就是相應(yīng)的padding。
Canvas(modifier = Modifier.fillMaxSize()){
drawRect(
color = Color.Blue,
)
inset(100f, 100f, 100f, 100f) {
drawRect(
color = Color.Red,
)
}
}
2. translate
平移繪制區(qū)域
inline fun DrawScope.translate(
left: Float = 0.0f,
top: Float = 0.0f,
block: DrawScope.() -> Unit
) {...}
只需要設(shè)置left、top方向移動的距離即可。
3. rotate與rotateRad
旋轉(zhuǎn)繪制區(qū)域
inline fun DrawScope.rotate(
degrees: Float,
pivot: Offset = center,
block: DrawScope.() -> Unit
) {...}
inline fun DrawScope.rotateRad(
radians: Float,
pivot: Offset = center,
block: DrawScope.() -> Unit
) {...}
degrees是旋轉(zhuǎn)了多少角度。radians是旋轉(zhuǎn)了多少弧度。pivot是旋轉(zhuǎn)的中心點(diǎn),默認(rèn)是中心。
4. scale
縮放繪制區(qū)域。
inline fun DrawScope.scale(
scaleX: Float,
scaleY: Float,
pivot: Offset = center,
block: DrawScope.() -> Unit
) {...}
指定x、y方向上的縮放倍數(shù)即可。
5. clipRect
裁剪給定的矩形區(qū)域
inline fun DrawScope.clipRect(
left: Float = 0.0f,
top: Float = 0.0f,
right: Float = size.width,
bottom: Float = size.height,
clipOp: ClipOp = ClipOp.Intersect,
block: DrawScope.() -> Unit
) {...}
clipOp:ClipOp.Intersect是裁剪矩形的里面,ClipOp.Difference是裁剪矩形的外面。
看個(gè)簡單的例子,便于你的理解:
Canvas(modifier = Modifier.fillMaxSize()){
drawRect(
color = Color.Blue,
)
clipRect(200f, 200f, clipOp = ClipOp.Intersect) {
drawRect(
color = Color.Yellow,
)
}
}
左邊是ClipOp.Intersect,右邊是ClipOp.Difference

clipPath同理。
6. drawIntoCanvas
可以直接調(diào)用底層Canvas繪制的方法。我們用它實(shí)現(xiàn)一開始的drawLine例子,畫一條對角線:
Canvas(modifier = Modifier.fillMaxSize()) {
val canvasWidth = size.width
val canvasHeight = size.height
drawIntoCanvas {
val paint = Paint()
paint.color = Color.Blue
paint.strokeWidth = 1f
it.drawLine(
p1 = Offset(canvasWidth,0f),
p2 = Offset(0f,canvasHeight),
paint = paint
)
}
}
其中drawLine方法,并不是一開始DrawScope中的drawLine:
actual typealias NativeCanvas = android.graphics.Canvas
private val EmptyCanvas = android.graphics.Canvas()
@PublishedApi internal class AndroidCanvas() : Canvas {
@PublishedApi internal var internalCanvas: NativeCanvas = EmptyCanvas
override fun drawLine(p1: Offset, p2: Offset, paint: Paint) {
internalCanvas.drawLine(
p1.x,
p1.y,
p2.x,
p2.y,
paint.asFrameworkPaint()
)
}
}可以看到最終調(diào)用了Android的Canvas api。
7. withTransform
執(zhí)行1個(gè)或多個(gè)轉(zhuǎn)換。也就是上面平移旋轉(zhuǎn)這些可以一塊執(zhí)行。
inline fun DrawScope.withTransform(
transformBlock: DrawTransform.() -> Unit,
drawBlock: DrawScope.() -> Unit
) {...}
4.參考
到此這篇關(guān)于Jetpack Compose Canvas繪制超詳細(xì)介紹的文章就介紹到這了,更多相關(guān)Jetpack Compose Canvas內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解如何在Flutter中用小部件創(chuàng)建響應(yīng)式布局
由于Flutter的跨平臺、單一代碼庫的能力,了解屏幕管理以防止像柔性溢出錯(cuò)誤或糟糕的用戶界面設(shè)計(jì)這樣的問題是至關(guān)重要的。本文將探討如何用靈活和擴(kuò)展的小部件創(chuàng)建響應(yīng)式布局,需要的可以參考一下2022-02-02
Kotlin HttpURLConnection與服務(wù)器交互實(shí)現(xiàn)方法詳解
簡單來說,HttpURLConnection 是發(fā)起HTTP請求的基礎(chǔ)類庫,提供了HTTP請求的基本功能,不過封裝的比較少,在使用時(shí)很多內(nèi)容都需要自己設(shè)置,也需要自己處理請求流和響應(yīng)流2022-09-09
Android studio實(shí)現(xiàn)刮刮樂的方法
這篇文章主要為大家詳細(xì)介紹了Android studio實(shí)現(xiàn)刮刮樂的兩種方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10
android中寫一個(gè)內(nèi)部類來選擇文件夾中指定的圖片類型實(shí)例說明
選擇文件夾中指定的圖片類型,本類是用來選擇文件夾中是.jpg類型的圖片具體實(shí)現(xiàn)如下,感興趣的朋友可以參考下哈2013-06-06
Android Studio 中aidl的自定義類的使用詳解
這篇文章主要介紹了Android Studio 中aidl的自定義類的使用詳解,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-03-03
Android DaggerActivityComponent錯(cuò)誤解決辦法詳解
這篇文章主要介紹了Android DaggerActivityComponent錯(cuò)誤解決的相關(guān)資料,需要的朋友可以參考下2017-05-05

