前幾天開發新程式的時候,選擇了jetpack最新的組件compose來構建應用程式的界面,但是因為compose面世不久,網上基本找不到太多相關的資料,想找一個新擬態風格的控制元件庫但是找不到,也就只能自己寫一個,
效果圖:

這里以輸入框為例,其他的空間做成新擬態的原理是一樣的,
一、原理
其實新擬態的原理是很簡單的,大致就是給控制元件加兩個陰影,顯示出光線明暗的變化,如上圖所示,左上加的是白色的陰影,右下加的是灰色的陰影,
二、面臨的困難
在compose里面,api實際上是進一步封裝的,改變控制元件的modifier與陰影相關的api只有:
@Stable
public fun Modifier.shadow(
elevation: Dp,
shape: Shape,
clip: Boolean
): Modifier
這樣一個,
看到elevation這個單詞,應該不會感到陌生,這種陰影效果就是在Android5.0,apiLevel 21引入material design時引入的效果,
這種陰影效果只會出現在控制元件的右下角,沒辦法在控制元件的任意位置添加陰影,而且這種陰影效果是沒辦法指定顏色的,比如下圖中按鍵的陰影:

三、實作的思路
可能谷歌官方也知道compose的功能比現有Android framework的功能還是稍顯羸弱,所以在繪圖方面,compose的Canvas是可以直接借助底層的Android framework繪圖工具來繪制的,也就是:
public inline fun DrawScope.drawIntoCanvas(
block: (Canvas) → Unit
): Unit
這個api來呼叫底層的canvas,然后使用畫筆,設定好陰影相關的底層屬性,然后在canvas上面繪制兩個陰影,
四、效果實作
修改控制元件的外觀需要用到Modifier,所以一般習慣把實作封裝成Modifier的擴展函式:
@RequiresApi(Build.VERSION_CODES.O)
fun Modifier.drawColoredShadow(
color: Color,
alpha: Float = 0.2f,
borderRadius: Dp = 0.dp,
shadowRadius: Dp = 20.dp,
offsetX: Dp = 0.dp,
offsetY: Dp = 0.dp,
roundedRect: Boolean = true
) = this.drawBehind {
}
其中引數的含義:
color:陰影的顏色;
alpha:陰影的不透明度;
borderRadius:繪制的圓角矩形陰影的圓角半徑;
shadowRadius:陰影所使用的高斯模糊演算法的模糊半徑,這里高斯模糊演算法,就是把一個點的色彩用周圍一定范圍內的影像的色彩來取均值(或者加權均值),這個范圍的大小就是半徑了,
offsetX:陰影在水平方向的偏移,大于零則向右偏移,小于零則向左,
offsetY:陰影在豎直方向的偏移,大于零則向下偏移,小于零則向上,
roundedRect:是否兩邊都是半圓的圓角矩形,
之所以這個地方呼叫了this.drawBehind,是因為底層Android framework里面的陰影是依附于圖形的,也就是說,設定好了陰影,你只有畫一個圖形,才能顯示出圖形的陰影,而我們并不想讓這個圖形顯示出來而且不讓它影響控制元件的觸發事件,所以這個時候就要呼叫drawBehind把陰影依附的圖形繪制到控制元件的下面(z軸方向,類似于前端css里面的after偽類),
首先初始化兩個顏色,一個是陰影依附的圖形的顏色,這個地方為了不顯示出來,這里直接把不透明度alpha值設定為0f,第二個是陰影的顏色:
val transparentColor = android.graphics.Color.toArgb(color.copy(alpha = .0f).value.toLong())
val shadowColor = android.graphics.Color.toArgb(color.copy(alpha = alpha).value.toLong())
然后呼叫drawIntoCanvas來呼叫底層android framework的canvas:
this.drawIntoCanvas {
}
在drawIntoCanvas里面,初始化一個畫筆物件:
val paint = Paint()
val frameworkPaint = paint.asFrameworkPaint()
設定好畫筆顏色和陰影效果:
frameworkPaint.color = transparentColor
frameworkPaint.setShadowLayer(
shadowRadius.toPx(),
offsetX.toPx(),
offsetY.toPx(),
shadowColor
)
上面這段代碼中的setShadowLayer就是Android 底層設定陰影的api,第一個引數是陰影的半徑,第二個是水平偏移,第三個是豎直偏移,第四個是陰影的顏色,
最后用畫筆在canvas上面畫一個跟控制元件尺寸一樣,而且透明的圖形,陰影就顯示出來了:
it.drawRoundRect(
0f,
0f,
this.size.width,
this.size.height,
if(roundedRect) this.size.height / 2 else borderRadius.toPx(),
if(roundedRect) this.size.height / 2 else borderRadius.toPx(),
paint
)
在需要新擬態風的控制元件Modifier上呼叫這個擴展函式即可:
TextField(value = password, onValueChange = { password = it }, modifier = Modifier
.fillMaxWidth()
.background(Color.Transparent)
.padding(horizontal = 30.dp)
.drawColoredShadow(
Color.Black,
0.1f,
borderRadius = 25.dp,
shadowRadius = 10.dp,
offsetX = passwordAnimation.value.dp,
offsetY = passwordAnimation.value.dp
)
.drawColoredShadow(
Color.White,
0.9f,
borderRadius = 25.dp,
shadowRadius = 10.dp,
offsetX = (-passwordAnimation.value).dp,
offsetY = (-passwordAnimation.value).dp
)
)
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/297868.html
標籤:其他
上一篇:Python報錯:PermissionError: [Errno 13] Permission denied
下一篇:【Java 虛擬機原理】Class 位元組碼二進制檔案分析 一 ( 位元組碼檔案附加資訊 | 魔數 | 次版本號 | 主版本號 | 常量池個數 )
