创建它的参数更改时,如何替换Android Jetpack Compose AndroidView? [英] How can I make Android Jetpack Compose AndroidView be replaced when the parameters that created it change?

查看:185
本文介绍了创建它的参数更改时,如何替换Android Jetpack Compose AndroidView?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个应用程序,显示了封装在AndroidView中的几个不同视图.在下面的简单示例中,这些只是TextView实例.问题在于,更改文本(在这种情况下,通过三个不同的值循环显示)似乎无法更新应用程序显示的内容.

I have an app that shows several different views encapsulated in AndroidView. In the simple example to reproduce below, these are just TextView instances. The problem is that changing the text (in this case cycling through three different values) doesn't seem to update what the app is displaying.

sealed class AppView
data class ShowSomeText(val text: String) : AppView()
data class SomeOtherState(val data: Any?) : AppView()
data class ShowSomeText2(val text: String) : AppView()

class AppViewModel : ViewModel() {

    var currentView = MutableLiveData<AppView>(ShowSomeText("original text"))
    var currentViewWorkaround = MutableLiveData<AppView>(ShowSomeText("original text"))


    private val textRing = arrayOf("one", "two", "three")
    private var textRingPosition = 0

    fun incrementTextState() {
        val nextState = ShowSomeText(textRing[textRingPosition])
        currentView.postValue(nextState)

        val nextStateWorkaround = when(currentViewWorkaround.value) {
            is ShowSomeText -> ShowSomeText2(textRing[textRingPosition])
            else -> ShowSomeText(textRing[textRingPosition])
        }
        currentViewWorkaround.postValue(nextStateWorkaround)
        textRingPosition = (textRingPosition + 1) % textRing.size
    }
}

class MainActivity : AppCompatActivity() {

    private val viewModel = AppViewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ViewContainer(viewModel)
        }
    }
}

@Composable
fun ViewContainer(viewModel: AppViewModel) {

    // Add this to gradle.build for the observeAsState function:
    //     implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
    val currentView: AppView by viewModel.currentView.observeAsState(ShowSomeText("starting text"))
    val currentViewWorkaround: AppView by viewModel.currentViewWorkaround.observeAsState(ShowSomeText("starting text"))

    Column {
        Button(onClick = viewModel::incrementTextState) {
            Text(
                text = "tap to change",
                style = TextStyle(fontSize = 12.em)
            )
        }
        Text("Compose Text")
        when (currentView) {
            is ShowSomeText -> createComposeTextView((currentView as ShowSomeText).text)
            is SomeOtherState -> Text("the other state")
        }
        Text("AndroidView wrapping TextView")
        when (currentView) {
            is ShowSomeText -> createAndroidViewForTextView((currentView as ShowSomeText).text)
            is SomeOtherState -> Text("the other state")
        }
        Text("AndroidView wrapping TextView with 2-state workaround")
        when (currentViewWorkaround) {
            is ShowSomeText -> createAndroidViewForTextView((currentViewWorkaround as ShowSomeText).text)
            is ShowSomeText2 -> createAndroidViewForTextView((currentViewWorkaround as ShowSomeText2).text)
            is SomeOtherState -> Text("the other state")
        }
    }

}

@Composable
fun createAndroidViewForTextView(text: String) {
    val context = ContextAmbient.current
    val tv = remember(text, context) {
        val x = TextView(context)
        x.text = text
        x.textSize = 48.0f
        x
    }
    AndroidView({ tv })
}

@Composable
fun createComposeTextView(text: String) {
    Text(text, style = TextStyle(fontSize = 12.em))
}

第一个文本通过Compose Text函数显示并起作用,第二个文本带有TextView包装的AndroidView Compose函数并且不起作用,第三个文本也使用相同的AndroidView包装器,但通过使用另一个状态变量以某种方式触发了更改.

The first text is displayed via the Compose Text function and works, the second with a TextView wrapped an AndroidView Compose function and does not work, the third also uses the same AndroidView wrapper but triggers the change somehow by using another state variable.

为什么中间文本不更新?

Why doesn't the middle text update?

具有hack修复程序的可复制kt文件的完整摘要: https://gist.github.com /okhobb/ba7791af4562ea672d0c52769a7cd8ba

Full gist of a reproducing kt file with the hack fix: https://gist.github.com/okhobb/ba7791af4562ea672d0c52769a7cd8ba

推荐答案

索引必须是可变状态,例如:textRingPosition = remember{ mutableStateOf<Int>(0) }.

The index must be a mutable state like: textRingPosition = remember{ mutableStateOf<Int>(0) }.

作为一般性提示,如果您的撰写代码包含var,则可能是错误的,应该是一种状态.

As a general tip if your Compose Code contains a var something might be wrong and should be a state.

说明: 如果您运行此代码段,请按下按钮,但是composable不会更新,但是在控制台中您会看到该值已更改.

Explaination: If you run this snippet you press the button but the composable is not updated but in the console you see the value changed.

Surface(modifier = Modifier.padding(all = 16.dp).fillMaxSize()) {
    val textRing = arrayOf("one", "two", "three")
    var textRingPosition = 0
    val liveData = MutableLiveData<String>()
    val observeAsState: String by liveData.observeAsState(initial = textRing[textRingPosition])

    Column() {
        println("Composed with " + observeAsState + " and pos: " + textRingPosition)
        Text("Text: " + observeAsState)
        Button(onClick = {
            textRingPosition = textRingPosition.inc().rem(textRing.size)
            println("OnClick: " + textRingPosition)
            liveData.postValue(textRing[textRingPosition])
        }) {
            Text("change")
        }
    }
}

控制台中的输出:

1 | I/System.out: Composed with one and pos: 0
2 | I/System.out: OnClick: 1
3 | I/System.out: Composed with two and pos: 0
4 | I/System.out: OnClick: 1
5 | I/System.out: OnClick: 2
6 | I/System.out: Composed with three and pos: 0
7 | I/System.out: OnClick: 1
8 | I/System.out: Composed with two and pos: 0
9 | I/System.out: OnClick: 1

在第4行中,您可以看到索引已更改为1,但是它应该已经在2处,这是因为视图在第3行中进行了重组并重置了索引.由于当前视图的值与状态中的新值相同,因此视图不需要更新,因此此处不会重新组合.在第5行中,索引为2,它将状态值更改为另一个字符串,从而触发了重新组合.

In line 4 you can see the index is changed to 1, but it should already be at 2 this is because the view recomposed in Line 3 and reset the index. Compose will not recompose here because the view does not need to update because the value of the current is the same as the new value from the state. In line 5 the index is 2 which changes the value of the state to another string, thus triggering a recomposition.

具有索引的记住状态和可变状态,该组件将重新组合每次更改:

With a remember and mutable state for the index the component will recompose each change:

Surface(modifier = Modifier.padding(all = 16.dp).fillMaxSize()) {
    val textRing = arrayOf("one", "two", "three")
    var textRingPosition = 0
    val liveData = MutableLiveData<String>()
    val observeAsState: String by liveData.observeAsState(initial = textRing[textRingPosition])

    val rememberTextRingPosition = remember { mutableStateOf(0) }

    Column() {
        println("Composed with $observeAsState and pos: ${textRingPosition}expected value: ${rememberTextRingPosition.value}")
        Text("Text: $observeAsState")
        Button(onClick = {
            textRingPosition = textRingPosition.inc().rem(textRing.size)
            rememberTextRingPosition.value = rememberTextRingPosition.value.inc().rem(textRing.size)
            println("OnClick: var:$textRingPosition remember: ${rememberTextRingPosition.value}")
            liveData.postValue(textRing[textRingPosition])
        }) {
            Text("change")
        }
    }
}

输出:

1 | I/System.out: Composed with one and pos: 0expected value: 0
2 | I/System.out: OnClick: var:1 remember: 1
3 | I/System.out: Composed with two and pos: 0expected value: 1
4 | I/System.out: OnClick: var:1 remember: 2
5 | I/System.out: Composed with two and pos: 0expected value: 2
6 | I/System.out: OnClick: var:1 remember: 0
7 | I/System.out: Composed with two and pos: 0expected value: 0
8 | I/System.out: OnClick: var:1 remember: 1

作为日志状态,组件将按应有的方式进行每次重组,并且由于记忆而不会重置状态.

As the log states, the component gets recomposed each time as it should be and the state is not reset because of the remember.

这篇关于创建它的参数更改时,如何替换Android Jetpack Compose AndroidView?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆