Jetpack Compose 指南

37
8个回答

70
我们可以使用LocalConfiguration来确定当前的方向是什么,但是下面的代码并不能帮助监听配置的变化:
@Composable
fun ConfigChangeExample() {
    val configuration = LocalConfiguration.current
    when (configuration.orientation) {
        Configuration.ORIENTATION_LANDSCAPE -> {
            Text("Landscape")
        }
        else -> {
            Text("Portrait")
        }
    }
}

优秀!谢谢。 - Sirelon
6
在配置更改后,所有可组成的对象都会在活动被销毁并重新创建时重新组合吗? - Daniel Weidensdörfer
1
要“监听”方向变化,只需在onCreate方法中检查方向,并将此值传递给您的组合。这是因为每次活动重新创建时都会调用onCreate()。 - Waldmann
或者,如果您在清单中的活动中添加android:configChanges="orientation|screenSize",则活动在方向更改时不会重新创建,并且仍然会发生重新组合。如果您需要根据方向更改更新状态,则可以使用LaunchedEffect和LocalConfiguration.current.orientation作为键,无需侦听器。 - Eric
@DanielWeidensdörfer ... 在屏幕方向改变时,活动本身将被销毁并重新创建,因此可组合项也将被销毁并重新创建,但视图模型实例将被保留。 - undefined

13
要观察方向,我们可以创建一个快照流来观察方向的变化,该流将提供一个状态变量,您可以直接使用。
var orientation by remember { mutableStateOf(Configuration.ORIENTATION_PORTRAIT) }

val configuration = LocalConfiguration.current

// If our configuration changes then this will launch a new coroutine scope for it
LaunchedEffect(configuration) {
    // Save any changes to the orientation value on the configuration object
    snapshotFlow { configuration.orientation }
        .collect { orientation = it }
}

when (orientation) {
    Configuration.ORIENTATION_LANDSCAPE -> {
        LandscapeContent()
    }
    else -> {
        PortraitContent()
    }
}

我其实不确定是否值得,因为我自己还在学习中! - Tom

5
我们可以在Jetpack Compose中使用state,这样当状态改变时,可组合重新组合自己。
监听{{code:configuration change}}的一个示例使用{{code:state}}如下:
@Composable
fun ShowConfig(config: String) {
   Text(text = "$config!")
}

在活动中保留配置状态

var state by mutableStateOf("Potrait")

然后监听活动中的配置更改,当配置更改时,通过该值更新状态,例如:

override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        state = "Landscape" // this will automatically change the text to landscape
    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
        state = "Potrait"   // this will automatically change the text to potrait
    }
}

问候组件会观察配置字符串的状态,每当配置字符串的状态发生变化时,它就会重新组合。

3
您可以从 BoxWithConstraints 中获取它,并比较 宽度高度
简单示例:
    @Composable
    fun ShowScreenOrientation() {
        BoxWithConstraints {
            val mode = remember { mutableStateOf("") }
            mode.value = if (maxWidth < maxHeight) "Portrait" else "Landscape"
            Text(text = "Now it is in ${mode.value} mode")
        }
    }

2
根据新的androidx.compose.material3.windowsizeclass库,更新答案。
现在可以使用WindowSizeClass查询宽度和高度类别,而不是直接检测方向。该库专门用于构建自适应布局,并在活动大小/方向更改时自动重新组合布局。
此API为您提供宽度和高度的大致尺寸(COMPACTMEDIUMEXPANDING),这使得处理可折叠设备和大屏幕显示器变得容易。这还考虑了使用分屏的手机(方向保持不变)和其他无法通过简单的方向检查处理的选项。
这是我制作的一个简单示例:
class MainActivity {
    /* ...... */
    setContent {
        val windowSizeClass = calculateWindowSizeClass(this)
        /* .... */
        MyApp(windowWidthSizeClass = windowSizeClass.widthSizeClass, /* .... */ )
    }
}

@Composable
fun MyApp(windowWidthSizeClass: WindowWidthSizeClass, /* ... */) {
    when(windowWidthSizeClass) {
        WindowWidthSizeClass.Expanded -> // orientation is landscape in most devices including foldables (width 840dp+)
        WindowWidthSizeClass.Medium -> // Most tablets are in landscape, larger unfolded inner displays in portrait (width 600dp+)
        WindowWidthSizeClass.Compact -> // Most phones in portrait
    }
}

在这里,当windowWidthSizeClass等于WindowWidthSizeClass.Expanded时,我可以将布局设置为横向视图。我还可以使用WindowWidthSizeClass.Medium来优化我的布局,以适应大型设备(如平板电脑和可折叠设备)。 最后,WindowWidthSizeClass.Compact告诉我大多数移动设备都是纵向的,我可以相应地更新我的用户界面。
这些相同的枚举值也适用于活动的高度,但文档说明如下:
“大多数应用程序只需考虑宽度窗口大小类即可构建响应式用户界面。”
(至少对我来说是真的)
请注意,此库仍处于 alpha 版本,并明确标记为实验性质,因此可能会发生变化。我只是想在这里添加一些内容,供任何尝试使用 Jetpack Compose 的 Material 3 用户参考。
官方指南- https://developer.android.com/guide/topics/large-screens/support-different-screen-sizes#window_size_classes 示例实现可以在JetNews示例应用程序中找到- https://github.com/android/compose-samples/tree/main/JetNews
文档 - https://developer.android.com/reference/kotlin/androidx/compose/material3/windowsizeclass/package-summary 发布说明(发布于1.0.0-alpha10,当前版本-alpha13)- https://developer.android.com/jetpack/androidx/releases/compose-material3#1.0.0-alpha10

1
这是一个小助手,用于在转换为纵向模式时将撰写行交换为列。您可能需要根据自己的需求进行一些微调。
@Composable
fun OrientationSwapper(
    content1: @Composable (modifier: Modifier) -> Unit,
    content2: @Composable (modifier: Modifier) -> Unit
) {
    val portrait = LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT
    if (portrait) {
        Column(verticalArrangement = Arrangement.SpaceEvenly, modifier = Modifier.fillMaxSize()) {
            content1(modifier = Modifier.weight(1f))
            content2(modifier = Modifier.weight(1f))
        }
    } else {
        Row(horizontalArrangement = Arrangement.SpaceEvenly, modifier = Modifier.fillMaxSize()) {
            content1(modifier = Modifier.weight(1f))
            content2(modifier = Modifier.weight(1f))
        }
    }
}

并使用如下方式:
    OrientationSwapper(
        content1 = { modifier ->
            Text("foo", modifier = modifier) },
        content2 = {  modifier ->
            Text("bar", modifier = modifier) })

0
var orientation by remember { mutableStateOf(Configuration.ORIENTATION_PORTRAIT) }
LaunchedEffect(configuration) {
    snapshotFlow { configuration.orientation }
        .collect { orientation = it }
}

when (orientation) {
    Configuration.ORIENTATION_LANDSCAPE -> {
       LandScapeScreen(
            isPortrait = false,
        ) {
            activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
            orientation = Configuration.ORIENTATION_PORTRAIT
        }
    }
    else -> {
        PortraitUI(
            isPortrait = true,
            onClick = {
                activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
                orientation = Configuration.ORIENTATION_LANDSCAPE
            },
        )
    }
}

0

您可以使用此方法动态更改值:

@Composable
fun <T> dynamicOrientationValue(portraitValue: T, landscapeValue: T): T =
    when (LocalConfiguration.current.orientation) {
        Configuration.ORIENTATION_LANDSCAPE -> landscapeValue
        else -> portraitValue
    }

使用方法:

SpinBtn(
    modifier = Modifier
        .fillMaxWidth(dynamicOrientationValue(0.6f, 0.3f))
        .align(dynamicOrientationValue(BottomCenter, CenterEnd))
        .padding(32.dp), isSpinning = isSpinning
) { isSpinning = true }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接