我毫不怀疑在可组合函数中使用视图模型.我正在添加我的活动代码,我正在传递我的意向Bundle 包.

  1. 所以我想问一下,在活动中使用这样的视图模型来创建viewmodel个全局是不是最好的做法?

InputActivity.kt

class InputActivity : ComponentActivity() {

    private val viewModel by viewModel<InputViewModel>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setupViewModel()
        setContent {
            Theme {
                AppBarScaffold(
                    displayHomeAsUpEnabled = true,
                    titleId = R.string.personal_health
                ) {
                    viewModel.OptionData?.let {
                        Input(it)
                    }
                }
            }
        }
    }

    private fun setupViewModel() {
        viewModel.optionData = intent.getParcelableExtra("optiondata")
    }
}

我有这么多可组合的功能

Input

@Composable
fun Input(optionData: OptionData) {
    var value by rememberSaveable {
        mutableStateOf(false)
    }
    Column(
        modifier = Modifier
            .fillMaxHeight()
            .verticalScroll(rememberScrollState())
        verticalArrangement = Arrangement.SpaceBetween
    ) {
        InputItem()
        Spacer()
        OnSubmitPulse()
    }
}

InputItem

@Composable
fun InputItem() {
    Image()
    PulsePressure()
}

PulsePressure

@Composable
fun PulsePressure() {
    Column {
        InputWithUnitContainer()
        InputWithUnitContainer()
    }
}

InputWithUnitContainer

@Composable
fun InputWithUnitContainer() {
    Row() {
        Text()
        TextField(value = "")
        Text()
    }
}

每个函数都有我想存储在视图模型中的逻辑.

  1. 那么,我是应该在构造函数参数中创建视图模型,还是应该每次都传递视图模型实例?

Scenario 1

fun Input(optionData: OptionData,viewModel: InputViewModel = viewModel())

and个个个

fun InputItem(viewModel: InputViewModel = viewModel())

and个个个

fun PulsePressure(viewModel: InputViewModel = viewModel())

Scenario 2

fun Input(optionData: OptionData,viewModel: InputViewModel = viewModel()) {
        InputItem(viewModel)
}

and个个个

fun InputItem(viewModel: InputViewModel) {
      PulsePressure(viewModel)
}

and个个个

fun PulsePressure(viewModel: InputViewModel) {
    // more function call
}

那么你们在喷气背包作曲中有什么建议呢?如果你不明白我的问题,请问我.非常感谢

推荐答案

我不认为只有一种最佳实践,而是要 Select 适合您需求的方法.

您应该决定当您的应用程序处于活动状态时,您的ViewModel是否需要存储在内存中,或者是否需要将其范围限定为导航图或Composable.

第二件要考虑的事情是,你是否会在另一个屏幕或另一个应用程序中使用相同的Composable.如果是这样的话,您可以考虑通过回调将状态作为参数和事件传递给ViewModel,而不是将ViewModel传递给Composable.

而不是这个

fun Input(optionData: OptionData,viewModel: InputViewModel = viewModel()) {
        InputItem(viewModel)
}

如果我需要使用,或者我认为我将来会在不同的部分或其他应用程序中使用Input,我倾向于使用这个

fun Input(optionData: OptionData, someOtherData, onOptionDataChanged:()->Unit, onSomeOtherDataChanged: () -> Unit) {
        InputItem(viewModel)
}

关于这个主题,State in jetpack Compose from codelabs是一篇很好的阅读文章.

@Composable
fun WellnessScreen(
    modifier: Modifier = Modifier, 
    wellnessViewModel: WellnessViewModel = viewModel()
) {
   Column(modifier = modifier) {
       StatefulCounter()

       WellnessTasksList(
           list = wellnessViewModel.tasks,
           onCheckedTask = { task, checked ->
               wellnessViewModel.changeTaskChecked(task, checked)
           },
           onCloseTask = { task ->
               wellnessViewModel.remove(task)
           }
       )
   }
}

@Composable
fun WellnessTasksList(
   list: List<WellnessTask>,
   onCheckedTask: (WellnessTask, Boolean) -> Unit,
   onCloseTask: (WellnessTask) -> Unit,
   modifier: Modifier = Modifier
) {
   LazyColumn(
       modifier = modifier
   ) {
       items(
           items = list,
           key = { task -> task.id }
       ) { task ->
           WellnessTaskItem(
               taskName = task.label,
               checked = task.checked,
               onCheckedChange = { checked -> onCheckedTask(task, checked) },
               onClose = { onCloseTask(task) }
           )
       }
   }
}

@Composable
fun WellnessTaskItem(
   taskName: String,
   checked: Boolean,
   onCheckedChange: (Boolean) -> Unit,
   onClose: () -> Unit,
   modifier: Modifier = Modifier
) {
   Row(
       modifier = modifier, verticalAlignment = Alignment.CenterVertically
   ) {
       Text(
           modifier = Modifier
               .weight(1f)
               .padding(start = 16.dp),
           text = taskName
       )
       Checkbox(
           checked = checked,
           onCheckedChange = onCheckedChange
       )
       IconButton(onClick = onClose) {
           Icon(Icons.Filled.Close, contentDescription = "Close")
       }
   }
}

最后但并非最不重要的一点是,如果它是不依赖于任何业务逻辑的UI逻辑,或者如果您正在构建独立的定制可组合组件作为定制视图的对应物,您可以考虑在包装在remember中的类中捕获UI逻辑,而不是在ViewModel中. 这种方法的例子是我们在列表、脚手架和其他默认可组合项中使用的任何reemory X函数.

例如,remmeberScrollState

@Stable
class ScrollState(initial: Int) : ScrollableState {

    /**
     * current scroll position value in pixels
     */
    var value: Int by mutableStateOf(initial, structuralEqualityPolicy())
        private set

    /**
     * maximum bound for [value], or [Int.MAX_VALUE] if still unknown
     */
    var maxValue: Int
        get() = _maxValueState.value
        internal set(newMax) {
            _maxValueState.value = newMax
            if (value > newMax) {
                value = newMax
            }
        }

    /**
     * [InteractionSource] that will be used to dispatch drag events when this
     * list is being dragged. If you want to know whether the fling (or smooth scroll) is in
     * progress, use [isScrollInProgress].
     */
    val interactionSource: InteractionSource get() = internalInteractionSource

    internal val internalInteractionSource: MutableInteractionSource = MutableInteractionSource()

    private var _maxValueState = mutableStateOf(Int.MAX_VALUE, structuralEqualityPolicy())

    /**
     * We receive scroll events in floats but represent the scroll position in ints so we have to
     * manually accumulate the fractional part of the scroll to not completely ignore it.
     */
    private var accumulator: Float = 0f

    private val scrollableState = ScrollableState {
        val absolute = (value + it + accumulator)
        val newValue = absolute.coerceIn(0f, maxValue.toFloat())
        val changed = absolute != newValue
        val consumed = newValue - value
        val consumedInt = consumed.roundToInt()
        value += consumedInt
        accumulator = consumed - consumedInt

        // Avoid floating-point rounding error
        if (changed) consumed else it
    }

    override suspend fun scroll(
        scrollPriority: MutatePriority,
        block: suspend ScrollScope.() -> Unit
    ): Unit = scrollableState.scroll(scrollPriority, block)

    override fun dispatchRawDelta(delta: Float): Float =
        scrollableState.dispatchRawDelta(delta)

    override val isScrollInProgress: Boolean
        get() = scrollableState.isScrollInProgress

    /**
     * Scroll to position in pixels with animation.
     *
     * @param value target value in pixels to smooth scroll to, value will be coerced to
     * 0..maxPosition
     * @param animationSpec animation curve for smooth scroll animation
     */
    suspend fun animateScrollTo(
        value: Int,
        animationSpec: AnimationSpec<Float> = SpringSpec()
    ) {
        this.animateScrollBy((value - this.value).toFloat(), animationSpec)
    }

    /**
     * Instantly jump to the given position in pixels.
     *
     * Cancels the currently running scroll, if any, and suspends until the cancellation is
     * complete.
     *
     * @see animateScrollTo for an animated version
     *
     * @param value number of pixels to scroll by
     * @return the amount of scroll consumed
     */
    suspend fun scrollTo(value: Int): Float = this.scrollBy((value - this.value).toFloat())

    companion object {
        /**
         * The default [Saver] implementation for [ScrollState].
         */
        val Saver: Saver<ScrollState, *> = Saver(
            save = { it.value },
            restore = { ScrollState(it) }
        )
    }
} 

额外的

此外,根据您的需要或适用性,使用ModifierComposable的州可能会使它更容易与其他Composasble一起使用.

例如

class MyState(val color:Color)

@composable
fun rememberMyState(color:Color) = remember{MyState(color)}

在修饰符中包装UI逻辑

fun Modifier.myModifier(myState:State)= this.then(
   Modifier.color(myState.color)
)

在某些情况下,可重用性可能比可组合性更高

@Composable
fun MyComposable(myState: MyState) {
   Column(Modifier.background(color){...}
}

如果您在上面的示例中使用Composable,我们将布局限制为Column,而您可以使用第一个布局,您可以使用任何Composable.实现主要取决于您的喜好.

Android相关问答推荐

Jetpack编写Lazy列滑动删除动画不顺利结束

懒惰的垂直网格中盒子的重量-Jetpack组合

try 用Jetpack Compose理解视图模型和导航

Android 14预测性背部手势-闪烁的白色背景色

Android Studio SQLite 错误:列不正确(没有这样的列:_id)

activity在 Runnable 中如何工作?我的 Android 表格未显示

如何在同一行中滚动自定义布局和惰性列,就好像它们是一个组件一样

参数化类RecyclerView.Adapter的原始使用

Android - 如何使 TextInputEditText 的高度恰好为 2 行?

在 Android 房间迁移中获取上下文

具有管理员权限的 Kotlin 中的多用户系统

我该怎么做文本计时器

插入查询室 OnConflictStrategy.REPLACE

Unity:Android 上随机接近零的 FPS 下降(提供了很多线索)

如何在 Jetpack Compose 中更改 ModalNavigationDrawer 的抽屉容器 colored颜色 ?

在开发过程中我应该把 mp4 文件放在哪里?

如何使用jetpack compose实现布局,其中图标在列布局上是绝对位置

基线配置文件 x R8/Proguard

Android:如何获取具有纬度和经度的位置的图像

单击后退按钮时,应用程序会在一秒钟后崩溃