Jetpack Compose中的基本组件及组件提取

前言

除了布局组件外,Jetpack Compose 还提供了一系列其他常用的 UI 组件。

https://developer.android.google.cn/jetpack/compose/components?hl=zh-cn

https://developer.android.google.cn/courses/pathways/compose?hl=zh-cn

占位

1
Spacer(modifier = Modifier.height(16.dp))

分割线

这个写法已经废弃

1
2
3
4
Divider(
color = Color.Black,
modifier = Modifier.fillMaxHeight().width(1.dp)
)

新写法

横线

1
2
3
4
HorizontalDivider(
modifier = Modifier.height(1.dp),
thickness = DividerDefaults.Thickness, color = Color.White
)

渐变色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
HorizontalDivider(
modifier = Modifier
.height(0.5.dp)
.background(
Brush.horizontalGradient(
listOf(
Color.Red,
Color.Blue,
Color.Magenta,
Color.Green,
)
)
),
thickness = DividerDefaults.Thickness,
color = Color.Transparent
)

纯色

1
2
3
4
5
6
HorizontalDivider(
modifier = Modifier
.height(0.5.dp)
.background(Color.White),
thickness = DividerDefaults.Thickness, color = Color.Transparent
)

文本

Text(文本):

1
Text("Hello, World!")

文本居中

1
2
3
4
5
6
7
8
Text(
text = "A",
modifier = Modifier
.background(Color.Red)
.width(30.dp).height(30.dp)
.wrapContentSize(Alignment.Center),
textAlign = TextAlign.Center,
)

设置行数

1
2
3
4
5
6
7
8
9
Text(
text = "ABC",
modifier = Modifier
.background(Color.Red)
.width(30.dp).height(30.dp)
.wrapContentSize(Alignment.Center),
textAlign = TextAlign.Center,
maxLines = 1
)

按钮

https://www.psvmc.cn/article/2024-03-13-jetpack-compose-button.html

文本输入

https://www.psvmc.cn/article/2024-03-13-jetpack-compose-textfield.html

图片

https://www.psvmc.cn/article/2024-03-13-jetpack-compose-image.html

加载中动画

1
CircularProgressIndicator(modifier = Modifier.size(30.dp))

消息框

Toast

组件化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import android.annotation.SuppressLint
import android.widget.Toast
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

@SuppressLint("CoroutineCreationDuringComposition")
@Composable
fun ShowToast(message: String) {
val context = LocalContext.current
// 启动一个协程来显示 Toast
CoroutineScope(Dispatchers.Main).launch {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}
}

调用

1
2
3
4
5
var showToast by remember { mutableStateOf(false) }

if (showToast) {
ShowToast("哈哈");
}

组件内方法

1
2
3
4
5
6
7
8
val context = LocalContext.current

// 显示 Toast 的函数
fun showToast(message: String) {
CoroutineScope(Dispatchers.Main).launch {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}
}

Snackbar

基本示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Composable
fun SnackbarExample() {
var snackbarVisible by remember { mutableStateOf(false) }

Box {
Button(onClick = { snackbarVisible = true }) {
Text("显示 Snackbar")
}
if (snackbarVisible) {
Snackbar(
content = { Text(text = "文本") },
action = {
Button(onClick = { snackbarVisible = false }) {
Text(text = "关闭")
}
}
)
}
}
}

使用snackbarHost

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
val scope = rememberCoroutineScope()
val snackbarHostState = remember { SnackbarHostState() }
Scaffold(
snackbarHost = {
SnackbarHost(hostState = snackbarHostState)
},
floatingActionButton = {

}
) { contentPadding ->
Box {
Button(
modifier = Modifier.padding(10.dp).width(100.dp).height(40.dp),
shape = MaterialTheme.shapes.medium,
onClick = {
scope.launch {
snackbarHostState.showSnackbar("我是提示消息")
}
}
){
Text(
text = "点击",
fontSize = 16.sp,
color = Color.White
)
}
}
}

对话框

1
2
3
4
5
6
7
8
9
Dialog(onDismissRequest = { /* 关闭对话框事件 */ }) {
Surface(
modifier = Modifier.size(300.dp),
shape = RoundedCornerShape(16.dp),
color = MaterialTheme.colorScheme.background
) {
Text("文字")
}
}

隐藏键盘

1
2
3
4
5
6
val keyboardController = LocalSoftwareKeyboardController.current

// 在需要隐藏键盘的地方调用这个函数
fun hideKeyboard() {
keyboardController?.hide()
}

加载网页

1
2
3
4
5
6
7
8
9
10
11
12
13
@Composable
fun WebViewContainer(url: String) {
AndroidView(factory = { context ->
WebView(context).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
webViewClient = WebViewClient()
loadUrl(url)
}
})
}

视频播放器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import android.net.Uri
import androidx.compose.runtime.Composable
import androidx.compose.ui.viewinterop.AndroidView
import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.SimpleExoPlayer
import com.google.android.exoplayer2.ui.PlayerView
import com.google.android.exoplayer2.source.ProgressiveMediaSource
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
import com.google.android.exoplayer2.util.Util

@Composable
fun VideoPlayer(videoUrl: String) {
val context = LocalContext.current
val exoPlayer = SimpleExoPlayer.Builder(context).build().apply {
val dataSourceFactory = DefaultDataSourceFactory(
context,
Util.getUserAgent(context, context.getString(R.string.app_name))
)
val videoSource = ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(Uri.parse(videoUrl))
prepare(videoSource)
}
AndroidView(factory = { ctx ->
PlayerView(ctx).apply {
player = exoPlayer
useController = true
}
}, update = { playerView ->
playerView.player = exoPlayer
})
}

实战-登录页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LoginScreen(onLoginClicked: (String, String) -> Unit) {
var username by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
Log.i("LoginScreen", "Username: $username, Password: $password")
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
OutlinedTextField(
value = username,
onValueChange = { username = it },
label = { Text("用户名") },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
OutlinedTextField(
value = password,
onValueChange = { password = it },
label = { Text("密码") },
visualTransformation = PasswordVisualTransformation(),
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
Button(
onClick = {
Log.i("登录点击", "Username: $username, Password: $password")
onLoginClicked(username, password)
},
modifier = Modifier.fillMaxWidth()
) {
Text(text = "登录")
}
}
}

调用

1
2
3
LoginScreen { name, pwd ->
Log.i("LoginScreen", "Username: $name, Password: $pwd")
};

组件提取

因为Compose组件本质都是方法,所以可以使用提取方法来提取组件

快捷键:Ctrl+Alt+M