【Android-JetpackCompose】8、实战 Compose 滑动列表、动画、样式

在这里插入图片描述

一、调整界面

首先,新建名为 BasicComposeTest 的项目,项目类型选择为 Empty Compose Project,创建后初始代码如下:

package com.bignerdranch.android.basiccomposetest

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.bignerdranch.android.basiccomposetest.ui.theme.BasicComposeTestTheme

class MainActivity : ComponentActivity() {
    
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContent {
    
    
            BasicComposeTestTheme {
    
    
                // A surface container using the 'background' color from the theme
                Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colors.background) {
    
    
                    Greeting("Android")
                }
            }
        }
    }
}

@Composable
fun Greeting(name: String) {
    
    
    Text(text = "Hello $name!")
}

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
    
    
    BasicComposeTestTheme {
    
    
        Greeting("Android")
    }
}

将 Text 用 Surface() 封装后,代码如下:

@Composable
fun Greeting(name: String) {
    
    
    Surface(color = MaterialTheme.colors.primary) {
    
    
        Text(text = "Hello $name!")
    }
}

效果如下:

在这里插入图片描述

给文本设置 padding,代码如下:

@Composable
fun Greeting(name: String) {
    
    
    Surface(color = MaterialTheme.colors.primary) {
    
    
        Text(text = "Hello $name!", modifier = Modifier.padding(24.dp))
    }
}

效果如下:

在这里插入图片描述

二、重复使用@Composable

package com.bignerdranch.android.basiccomposetest

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.padding
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.bignerdranch.android.basiccomposetest.ui.theme.BasicComposeTestTheme

class MainActivity : ComponentActivity() {
    
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContent {
    
    
            BasicComposeTestTheme {
    
    
                MyApp()
            }
        }
    }
}

@Composable
private fun MyApp() {
    
    
    Surface(
        color = MaterialTheme.colors.background
    ) {
    
    
        Greeting("Android")
    }
}

@Composable
fun Greeting(name: String) {
    
    
    Surface(color = MaterialTheme.colors.primary) {
    
    
        Text(text = "Hello $name!", modifier = Modifier.padding(24.dp))
    }
}

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
    
    
    BasicComposeTestTheme {
    
    
        MyApp()
    }
}

在这里插入图片描述

三、创建 Row 和 Column

@Composable
fun Greeting(name: String) {
    
    
    Surface(color = MaterialTheme.colors.primary) {
    
    
        Column(modifier = Modifier.padding(24.dp)) {
    
    
            Text(text = "Hello,")
            Text(text = name)
        }
    }
}

在这里插入图片描述

使用 for 循环,代码如下:

@Composable
fun MyApp(names: List<String> = listOf("World", "Compose")) {
    
    
    Column {
    
    
        for (name in names) {
    
    
            Greeting(name = name)
        }
    }
}

在这里插入图片描述

设置 padding 和 fillMaxWidth,代码如下:

@Composable
fun MyApp(names: List<String> = listOf("World", "Compose")) {
    
    
    Column(modifier = Modifier.padding(vertical = 4.dp)) {
    
    
        for (name in names) {
    
    
            Greeting(name = name)
        }
    }
}

@Composable
private fun Greeting(name: String) {
    
    
    Surface(
        color = MaterialTheme.colors.primary,
        modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)
    ) {
    
    
        Column(modifier = Modifier.fillMaxWidth().padding(24.dp)) {
    
    
            Text(text = "Hello, ")
            Text(text = name)
        }
    }
}

效果如下:

在这里插入图片描述

用 weight 修饰符会让元素填满所有可用空间,使其“具有弹性”,也就是会推开其他没有权重的元素(即“无弹性”元素)。该修饰符还会使 fillMaxWidth 修饰符变得多余。代码如下:

@Composable
private fun Greeting(name: String) {
    
    
    Surface(
        color = MaterialTheme.colors.primary,
        modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)
    ) {
    
    
        Row(modifier = Modifier.padding(24.dp)) {
    
    
            Column(
                modifier = Modifier.weight(1f)
            ) {
    
    
                Text(text = "Hello, ")
                Text(text = name)
            }
            OutlinedButton(onClick = {
    
     /*TODO*/ }) {
    
    
                Text(text = "Show less")
            }
        }

    }
}

效果如下:

在这里插入图片描述

四、Compose 的 state

通过 remember 和 mutableStateOf,可以使用 state,代码如下:

@Composable
private fun Greeting(name: String) {
    
    

    val expanded = remember {
    
     mutableStateOf(false) }

    val extraPadding = if (expanded.value) 48.dp else 0.dp

    Surface(
        color = MaterialTheme.colors.primary,
        modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)
    ) {
    
    
        Row(modifier = Modifier.padding(24.dp)) {
    
    
            Column(modifier = Modifier
                .weight(1f)
                .padding(bottom = extraPadding)
            ) {
    
    
                Text(text = "Hello, ")
                Text(text = name)
            }
            OutlinedButton(
                onClick = {
    
     expanded.value = !expanded.value }
            ) {
    
    
                Text(if (expanded.value) "Show less" else "Show more")
            }
        }
    }
}

效果如下:

在这里插入图片描述

五、state 提升

state 提升前,代码如下:

@Composable
fun OnBoardingScreen() {
    
    
    var shouldShowOnBoarding by remember {
    
     mutableStateOf(true) }
    Surface {
    
    
        Column(
            modifier = Modifier.fillMaxSize(),
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
    
    
            Text("Welcome to the Basics Test!")
            Button(
                modifier = Modifier.padding(vertical = 24.dp),
                onClick = {
    
     shouldShowOnBoarding = false }
            ) {
    
    
                Text("Continue")
            }
        }
    }
}

@Preview(showBackground = true, widthDp = 320, heightDp = 320)
@Composable
fun OnBoardingPreview() {
    
    
    BasicComposeTestTheme {
    
    
        OnBoardingScreen()
    }
}

预览效果如下:

在这里插入图片描述

state 提升到上层函数后,代码如下:


class MainActivity : ComponentActivity() {
    
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContent {
    
    
            BasicComposeTestTheme {
    
    
                MyApp()
            }
        }
    }
}

@Composable
fun MyApp() {
    
    
    var shouldShowOnBoarding by remember {
    
     mutableStateOf(true) }
    if (shouldShowOnBoarding)
        OnBoardingScreen {
    
     shouldShowOnBoarding = false }
    else
        Greetings()
}

@Composable
fun Greetings(names: List<String> = listOf("World", "Compose")) {
    
    
    Column(modifier = Modifier.padding(vertical = 4.dp)) {
    
    
        for (name in names) {
    
    
            Greeting(name = name)
        }
    }
}

@Composable
private fun Greeting(name: String) {
    
    
    val expanded = remember {
    
     mutableStateOf(false) }
    val extraPadding = if (expanded.value) 48.dp else 0.dp
    Surface(
        color = MaterialTheme.colors.primary,
        modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)
    ) {
    
    
        Row(modifier = Modifier.padding(24.dp)) {
    
    
            Column(
                modifier = Modifier
                    .weight(1f)
                    .padding(bottom = extraPadding)
            ) {
    
    
                Text(text = "Hello, ")
                Text(text = name)
            }
            OutlinedButton(onClick = {
    
     expanded.value = !expanded.value }) {
    
    
                Text(if (expanded.value) "Show less" else "Show more")
            }
        }
    }
}

@Preview(showBackground = true, widthDp = 320)
@Composable
fun DefaultPreview() {
    
    
    BasicComposeTestTheme {
    
    
        MyApp()
    }
}

@Composable
fun OnBoardingScreen(onContinueClicked: () -> Unit) {
    
    
    Surface {
    
    
        Column(
            modifier = Modifier.fillMaxSize(),
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
    
    
            Text("Welcome to the Basics Test!")
            Button(
                modifier = Modifier.padding(vertical = 24.dp),
                onClick = onContinueClicked,
            ) {
    
    
                Text("Continue")
            }
        }
    }
}

@Preview(showBackground = true, widthDp = 320, heightDp = 320)
@Composable
fun OnBoardingPreview() {
    
    
    BasicComposeTestTheme {
    
    
        OnBoardingScreen(onContinueClicked = {
    
    })
    }
}

预览后,实现了布局切换,效果如下:

在这里插入图片描述

六、用 LazyColumn() 渲染滚动列表

到目前为止,已经在 Column 中显示了两条问候语。如果要千上万条问候语,就要用LazyColumn 和 LazyRow。他们相当于 Android View 中的 RecyclerView(比后者性能更优),用于显示可滚动列,他们只会渲染屏幕上可见的内容(而不是全部列表数据),从而提升性能。代码如下:

@Composable
fun Greetings(names: List<String> = List(1000) {
    
     "$it" }) {
    
    
    LazyColumn(modifier = Modifier.padding(vertical = 4.dp)) {
    
    
        items(items = names) {
    
     name ->
            Greeting(name = name)
        }
    }
}

预览后,列表可滚动,效果如下:

在这里插入图片描述

七、用 rememberSaveable 保留 state

用 remember 的话,每次配置更改(如旋转屏幕)、进程终止时都会重置,因为其只在 @Composable 被 composition 时起作用,因为旋转屏幕后整个 Activity 都丢失了,所有 state 也就都丢失了。

而用 rememberSaveable 的话,会让配置更改、进程终止时保留 state,代码如下:

@Composable
fun MyApp() {
    
    
    var shouldShowOnBoarding by rememberSaveable {
    
     mutableStateOf(true) }
    if (shouldShowOnBoarding)
        OnBoardingScreen {
    
     shouldShowOnBoarding = false }
    else
        Greetings()
}

运行后,旋转屏幕时,state 仍被保留,效果如下:

在这里插入图片描述

八、用 animateDpAsState 添加动画

代码如下:

@Composable
private fun Greeting(name: String) {
    
    
    var expanded by remember {
    
     mutableStateOf(false) }

    val extraPadding by animateDpAsState(
        if (expanded) 48.dp else 0.dp,
        animationSpec = spring(
            dampingRatio = Spring.DampingRatioMediumBouncy,
            stiffness = Spring.StiffnessLow
        )
    )
    Surface(
        color = MaterialTheme.colors.primary,
        modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)
    ) {
    
    
        Row(modifier = Modifier.padding(24.dp)) {
    
    
            Column(
                modifier = Modifier
                    .weight(1f)
                    .padding(bottom = extraPadding.coerceAtLeast(0.dp))
            ) {
    
    
                Text(text = "Hello, ")
                Text(text = name)
            }
            OutlinedButton(
                onClick = {
    
     expanded = !expanded }
            ) {
    
    
                Text(if (expanded) "Show less" else "Show more")
            }
        }
    }
}

运行后,效果如下:

在这里插入图片描述

九、样式、主题

可以为字体设置样式,代码如下:

@Composable
private fun Greeting(name: String) {
    
    
    var expanded by remember {
    
     mutableStateOf(false) }

    val extraPadding by animateDpAsState(
        if (expanded) 48.dp else 0.dp,
        animationSpec = spring(
            dampingRatio = Spring.DampingRatioMediumBouncy,
            stiffness = Spring.StiffnessLow
        )
    )
    Surface(
        color = MaterialTheme.colors.primary,
        modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)
    ) {
    
    
        Row(modifier = Modifier.padding(24.dp)) {
    
    
            Column(
                modifier = Modifier
                    .weight(1f)
                    .padding(bottom = extraPadding.coerceAtLeast(0.dp))
            ) {
    
    
                Text(text = "Hello, ")
                Text(text = name, style = MaterialTheme.typography.h4.copy(
                    fontWeight = FontWeight.ExtraBold
                ))
            }
            OutlinedButton(
                onClick = {
    
     expanded = !expanded }
            ) {
    
    
                Text(if (expanded) "Show less" else "Show more")
            }
        }
    }
}

字体样式效果如下:

在这里插入图片描述

在 Color.kt 中添加颜色,代码如下:

val Navy = Color(0xFF073042)
val Blue = Color(0xFF4285F4)
val LightBlue = Color(0xFFD7EFFE)
val Chartreuse = Color(0xFFEFF7CF)

在 Theme.kt 中使用颜色,代码如下:

private val DarkColorPalette = darkColors(
    surface = Blue,
    onSurface = Navy,
    primary = Navy,
    onPrimary = Chartreuse,
)

private val LightColorPalette = lightColors(
    primary = LightBlue,
    onPrimary = Navy,
    surface = Blue,
    onSurface = Color.White,
}

运行后,效果如下:

在这里插入图片描述

十、用 IconButton 替换 Button

            IconButton(onClick = {
    
     expanded = !expanded }) {
    
    
                Icon(
                    imageVector = if (expanded) Icons.Filled.ExpandLess else Icons.Filled.ExpandMore,
                    contentDescription = if (expanded) {
    
    
                        stringResource(R.string.show_less)
                    } else {
    
    
                        stringResource(R.string.show_more)
                    }
                )
            }

效果如下:

在这里插入图片描述

运行后,完整效果如下:

猜你喜欢

转载自blog.csdn.net/jiaoyangwm/article/details/127189452