原文: Jetpack Compose學習(6)——關於Modifier的妙用 | Stars-One的雜貨小窩
之前學習記錄中也是陸陸續續地將常用的Modifier的方法穿插進去了,本期就來詳細的講解下關於modifier的使用
限於篇幅,我是以常用的屬性來講解,漏講了一些請見諒,畢竟方法真的太多了,之後可能有用的新的效果,會穿插地講些
本系列以往文章請檢視此分類連結Jetpack compose學習
基本使用
我們直接以個簡單的例子講解下使用,設定Box佈局的寬高各100dp,且內邊距為16dp,背景色為綠色,程式碼如下
Column {
Box(
Modifier
.size(100.dp)
.background(Color.Green)
.padding(16.dp)
)
Box(
Modifier
.size(100.dp)
.padding(16.dp)
.background(Color.Green)
)
}
由上面程式碼和效果可以看到,modifier中的順序不同會導致效果不同,這是因為Modifier的設計如此
如果我們先設定背景色,之後再設定padding,那麼padding也是在綠色背景的基礎上進行的,所以,就是圖中全是綠色的效果
如果是先設定padding,那麼我們設定背景色是針對裡面的佈局進行設定,而不會講padding也算到裡面去
寬高類和邊距
首先,先是講解常用的屬性,設定寬高和邊距
size
同時設定寬高
size(size: Dp)
size(height: Dp,width: Dp)
width
單獨設定寬度
width(intrinsicSize: IntrinsicSize)
這個引數是自定義佈局測量裡的,本篇暫時不講解width(width: Dp)
hegiht
單獨設定高度
hegiht(intrinsicSize: IntrinsicSize)
這個引數是自定義佈局測量裡的,本篇暫時不講解hegiht(hegiht: Dp)
defaultMinSize
設定寬高的預設最小值
defaultMinSize(
minWidth: Dp = Dp.Unspecified,
minHeight: Dp = Dp.Unspecified
):
sizeIn
設定寬高的最小值和最大值,寬度在minWidth~maxWidth
之間,高度在minHeight~maxHeight
之間
sizeIn(
minWidth: Dp = Dp.Unspecified,
minHeight: Dp = Dp.Unspecified,
maxWidth: Dp = Dp.Unspecified,
maxHeight: Dp = Dp.Unspecified
):
同理,也有單獨給寬度或高度設定的方法
widthIn(min: Dp = Dp.Unspecified, max: Dp = Dp.Unspecified)
heightIn(min: Dp = Dp.Unspecified, max: Dp = Dp.Unspecified)
fillMaxSize
寬高都填充滿父佈局(相當於原生xml中的match_parent
)
fillMaxSize(fraction: Float = 1f)
預設是1f,代表填充滿父佈局,如果設定為0.5f,則是填滿父佈局的0.5(即一半)
除此之外,也有單獨給寬度或高度方法
= fillMaxWidth(fraction: Float = 1f)
= fillMaxHegiht(fraction: Float = 1f)
wrapContentSize
元件的控制元件寬高若是小與定義的最小寬高,會將元件進行排列的設定
wrapContentSize(
align: Alignment = Alignment.Center,
unbounded: Boolean = false
)
上面說的可能不是太好懂,以一個例子來說明吧
Box(
Modifier.sizeIn(minWidth = 40.dp, minHeight = 40.dp)
.wrapContentSize(Alignment.TopCenter)
.size(20.dp)
.background(Color.Blue)
)
我們設定了Box的元件的最小寬高為40dp,但此Box的寬高實際設定成了20dp,如果沒有加上這個方法wrapContentSize
,那麼最終渲染出的Box寬高其實是40dp
x 40dp
,且是藍色背景
而由於我們加上了這個方法,最終渲染出的Box寬高就為20dp
x 20dp
,且排列對齊方式會按照wrapContentSize
中的align引數進行
同理,也有單獨設定寬度或高度的方法
wrapContentHeight(
align: Alignment.Vertical = Alignment.CenterVertically,
unbounded: Boolean = false
)
//使用例子
Box(
Modifier.size(50.dp)
.wrapContentHeight(Alignment.CenterVertically)
.height(20.dp)
.background(Color.Blue)
)
wrapContentWidth(
align: Alignment.Horizontal = Alignment.CenterHorizontally,
unbounded: Boolean = false
)
//使用例子
Box(
Modifier.size(50.dp)
.wrapContentWidth(Alignment.CenterHorizontally)
.width(20.dp)
.background(Color.Blue)
)
padding
有四種不同的引數,各位看著用就行,之前文章中也是有詳細講解,這裡不再贅述
padding(all: Dp)
點選事件類(點選 雙擊 長按)
clickable
給任意元件(包括佈局)設定點選事件,且自帶點選水波紋效果
clickable(
enabled: Boolean = true,
onClickLabel: String? = null,
role: Role? = null,
onClick: () -> Unit
)
onClickLabel
和role
主要是為殘疾人(應該是盲人)設定的屬性,可以不用設定
此外,還有另外的引數列表
clickable(
interactionSource: MutableInteractionSource,
indication: Indication?,
enabled: Boolean = true,
onClickLabel: String? = null,
role: Role? = null,
onClick: () -> Unit
)
interactionSource
之前也有講過,是用來判斷按鈕的點選狀態,具體可以看之前講解關於Button的使用的文章
indication
看文件說明是說用來繪製水波紋或者點選高亮的效果,具體使用沒有深究,下面給個例子:
val interactionSource = remember { MutableInteractionSource() }
Column {
Text(
text = "Click me and my neighbour will indicate as well!",
modifier = Modifier
// clickable will dispatch events using MutableInteractionSource and show ripple
.clickable(
interactionSource = interactionSource,
indication = rememberRipple()
) {
/**do something */
}
.padding(10.dp)
)
Spacer(Modifier.requiredHeight(10.dp))
Text(
text = "I'm neighbour and I indicate when you click the other one",
modifier = Modifier
// this element doesn't have a click, but will show default indication from the
// CompositionLocal as it accepts the same MutableInteractionSource
.indication(interactionSource, LocalIndication.current)
.padding(10.dp)
)
}
效果如下圖所示
combinedClickable
組合點選事件,可以給元件點選,雙擊,長按監聽操作
不過,需要注意的是,此方法是實驗性方法,也不知道後面版本更新會有所改變
combinedClickable(
enabled: Boolean = true,
onClickLabel: String? = null,
role: Role? = null,
onLongClickLabel: String? = null,
onLongClick: () -> Unit = null,
onDoubleClick: () -> Unit = null,
onClick: () -> Unit
)
看方法很好理解,onLongClick
是長按操作,onDoubleClick
是雙擊操作,onClick
是點選操作
val context = LocalContext.current
Box(
Modifier
.size(50.dp)
.background(Color.Blue)
.combinedClickable(onLongClick = {
Toast.makeText(context, "長按操作", Toast.LENGTH_SHORT).show()
}, onDoubleClick = {
Toast.makeText(context, "雙擊操作", Toast.LENGTH_SHORT).show()
}, onClick = {
Toast.makeText(context, "點選操作", Toast.LENGTH_SHORT).show()
})
)
形狀(shape) 邊框(border) 背景(background)
border
設定邊框寬度和形狀
border(border: BorderStroke, shape: Shape = RectangleShape)
border(width: Dp, color: Color, shape: Shape = RectangleShape))
border(width: Dp, brush: Brush, shape: Shape)
Brush是設定漸變,如下面的例子
//紅藍綠三色水平漸變
val gradientBrush = Brush.horizontalGradient(
colors = listOf(Color.Red, Color.Blue, Color.Green),
startX = 0.0f,
endX = 500.0f,
tileMode = TileMode.Repeated
)
Text(
"Text with gradient border",
modifier = Modifier.padding(10.dp).border(width = 2.dp, brush = gradientBrush, shape = CircleShape)
.padding(10.dp)
)
效果如下:
後期再出一篇講解Brush的使用
background
設定背景及背景形狀
background(color: Color, shape: Shape = RectangleShape)
background(brush: Brush, shape: Shape = RectangleShape,alpha: Float = 1.0f)
alpha是設定透明度
background有兩種引數列表,具體使用也是與上面的類似,這裡不過多贅述
陰影
shadow(elevation: Dp, shape: Shape = RectangleShape, clip: Boolean)
滾動效果
之前在講解佈局的時候有提及,Row和Column佈局裡面的子元件,寬高若是大於父元件就是導致子元件被隱藏,我們可以將其設定為滾動效果
但Compose沒有Scrollview
,要想Row或Column實現滾動效果,就得使用modifier來實現
verticalScroll
horizontalScroll(
state: ScrollState,
enabled: Boolean = true,
flingBehavior: FlingBehavior? = null,
reverseScrolling: Boolean = false
)
flingBehavior這個引數不是很理解做什麼用的..
Column(Modifier.verticalScroll(rememberScrollState())) {
repeat(10){
Box(
Modifier
.fillMaxWidth()
.height(200.dp)
){
Text(text = "測試$it")
}
}
}
效果如下:
如果不加上verticalScroll
,Column是無法向下滾動的
reverseScrolling設定為true的話,預設自動滾動到底部,效果如下所示
Column
一般和verticalScroll
連用實現垂直方向的滾動效果,而Row
則與horizontalScroll
連用
horizontalScroll
horizontalScroll(
state: ScrollState,
enabled: Boolean = true,
flingBehavior: FlingBehavior? = null,
reverseScrolling: Boolean = false
)
使用與上面的類似,這裡不再贅述
scrollable
scrollable(
state: ScrollableState,
orientation: Orientation,
enabled: Boolean = true,
reverseDirection: Boolean = false,
flingBehavior: FlingBehavior? = null,
interactionSource: MutableInteractionSource? = null
)
沒太搞懂這個主要是實現什麼效果的..
文件的示例程式碼:
// actual composable state that we will show on UI and update in `Scrollable`
val offset = remember { mutableStateOf(0f) }
Box(
Modifier
.size(150.dp)
.scrollable(
orientation = Orientation.Vertical,
// state for Scrollable, describes how consume scroll amount
state = rememberScrollableState { delta ->
offset.value = offset.value + delta // update the state
delta // indicate that we consumed all the pixels available
}
)
.background(Color.LightGray),
contentAlignment = Alignment.Center
) {
Text(offset.value.roundToInt().toString(), style = TextStyle(fontSize = 32.sp))
}
效果
選擇
selectable
可用來實現單選功能
val option1 = Color.Red
val option2 = Color.Blue
var selectedOption by remember { mutableStateOf(option1) }
Column {
Text("Selected: $selectedOption")
Row {
listOf(option1, option2).forEach { color ->
val selected = selectedOption == color
Box(
Modifier
.size(100.dp)
.background(color = color)
.selectable(
selected = selected,
onClick = { selectedOption = color }
)
){
if(selected) Text(text = "已選",color = Color.White)
}
}
}
}
效果:
toggleable
類似核取方塊的勾選及不勾選
var checked by remember { mutableStateOf(false) }
// content that you want to make toggleable
Text(
modifier = Modifier.toggleable(value = checked, onValueChange = { checked = it }),
text = checked.toString()
)
效果如下:
變化類(旋轉 縮放 放大)
aspectRatio
Box(Modifier.width(100.dp).aspectRatio(2f).background(Color.Green))
rotate
元件沿中心順時針旋轉,最高支援360°
//順時針旋轉45°
Box(
Modifier.rotate(45f)
.size(100.dp, 100.dp)
)
效果如下:
scale
縮放或放大
Box(
Modifier.scale(scaleX = 0.2f, scaleY = 0.5f)
.background(Color.Black)
.size(100.dp, 100.dp)
)
不過看實際效果,感覺是將寬和高都往中間縮放了