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)文章
android多線程斷點(diǎn)下載-帶進(jìn)度條和百分比進(jìn)度顯示效果
下面小編就為大家?guī)硪黄猘ndroid多線程斷點(diǎn)下載-帶進(jìn)度條和百分比進(jìn)度顯示效果。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-06-06
Android學(xué)習(xí)筆記--使用剪切板在Activity中傳值示例代碼
相對于getText和setText而言,利用ClipData對象來傳遞數(shù)據(jù),更符合面向?qū)ο蟮乃枷?,而且所能傳遞的數(shù)據(jù)類型也多樣化了2013-06-06
詳解Android通過修改配置文件設(shè)置wifi密碼
這篇文章主要介紹了詳解Android通過修改配置文件設(shè)置wifi密碼的相關(guān)資料,需要的朋友可以參考下2017-07-07
Kotlin使用TransitionDrawable實(shí)現(xiàn)顏色漸變效果流程講解
這篇文章主要介紹了Kotlin使用TransitionDrawable實(shí)現(xiàn)顏色漸變效果,這里,我們通過TransitionDrawable顯示顏色漸變效果,包括背景顏色的變化,以及圖片與圖片的漸變效果2023-02-02
簡略分析Android的Retrofit應(yīng)用開發(fā)框架源碼
這篇文章主要介紹了Android的Retrofit應(yīng)用開發(fā)框架的源碼分析,作者對Volley和Retrofit兩個(gè)框架進(jìn)行了一些對比,比較精彩,需要的朋友可以參考下2016-02-02
Android Studio和Gradle使用不同位置JDK的問題解決
這篇文章主要介紹了Android Studio和Gradle使用不同位置JDK的問題解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03
利用kotlin實(shí)現(xiàn)統(tǒng)計(jì)文件字符個(gè)數(shù)的方法示例
最近在學(xué)習(xí)kotlin,發(fā)現(xiàn)了一些不錯(cuò)的小技巧,所以下面這篇文章主要給大家介紹了關(guān)于利用kotlin實(shí)現(xiàn)統(tǒng)計(jì)文件字符個(gè)數(shù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-12-12
Flutter使用stack實(shí)現(xiàn)懸浮UI的示例代碼
在Flutter中,你可以使用Stack和Positioned來創(chuàng)建懸浮 UI,這篇文章主要為大家詳細(xì)介紹了Flutter使用stack實(shí)現(xiàn)懸浮UI的具體代碼,希望對大家有所幫助2024-01-01
淺談Android硬件加速原理與實(shí)現(xiàn)簡介
這篇文章主要介紹了淺談Android硬件加速原理與實(shí)現(xiàn)簡介,本文嘗試從底層硬件原理,一直到上層代碼實(shí)現(xiàn),對硬件加速技術(shù)進(jìn)行簡單介紹,感興趣的小伙伴們可以參考一下2018-07-07
Android 開發(fā)仿簡書登錄框可刪除內(nèi)容或顯示密碼框的內(nèi)容
本文通過實(shí)例代碼給大家分享android開發(fā)中模仿簡書登錄框可刪除內(nèi)容或顯示密碼框的內(nèi)容,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧2016-12-12

