如何在自定义类中实现 MutableState 接口/使对象可观察到 Jetpack Compose?

ste*_*wpf 4 android observable android-jetpack-compose

我有一个更复杂的对象,我希望 Jetpack Compose 可以观察它。但是我不能使用

// in my view model
var observableProperty by mutableStateOf(myObject)
Run Code Online (Sandbox Code Playgroud)

因为我需要myObject保留它的参考。所以我的计划是使用

// in my view model
var observableProperty = myObject
Run Code Online (Sandbox Code Playgroud)

并使其myObject自身实现MutableState接口。但是我不明白如何在自定义类中实现这一点,特别是如何向观察者(Jetpack Compose)发出更改通知?

我目前拥有的简化示例:

class MyComplexObject(){
    private val map = mutableMapOf<String,Any>()

    fun setValue(key: String, value: Any) {
        map[key] = value
        
        // how to notify about a change? <================
    }

    fun getValue(key: String) : Any? {
        return map[key]
    }
}
Run Code Online (Sandbox Code Playgroud)

Thr*_*ian 5

您不必让复杂对象实现 MutableState。您可以创建一个包含mutableStates 和其他不需要在更改时触发重组的对象的复杂对象。这是 JetSnack 应用程序中的常见模式。

@Stable
class SearchState(
    query: TextFieldValue,
    focused: Boolean,
    searching: Boolean,
    categories: List<SearchCategoryCollection>,
    suggestions: List<SearchSuggestionGroup>,
    filters: List<Filter>,
    searchResults: List<Snack>
) {
    var query by mutableStateOf(query)
    var focused by mutableStateOf(focused)
    var searching by mutableStateOf(searching)
    var categories by mutableStateOf(categories)
    var suggestions by mutableStateOf(suggestions)
    var filters by mutableStateOf(filters)
    var searchResults by mutableStateOf(searchResults)
    val searchDisplay: SearchDisplay
        get() = when {
            !focused && query.text.isEmpty() -> SearchDisplay.Categories
            focused && query.text.isEmpty() -> SearchDisplay.Suggestions
            searchResults.isEmpty() -> SearchDisplay.NoResults
            else -> SearchDisplay.Results
        }
}
Run Code Online (Sandbox Code Playgroud)

并且你用记住来包装这个对象,以防止在每次重组时不实例化

@Composable
private fun rememberSearchState(
    query: TextFieldValue = TextFieldValue(""),
    focused: Boolean = false,
    searching: Boolean = false,
    categories: List<SearchCategoryCollection> = SearchRepo.getCategories(),
    suggestions: List<SearchSuggestionGroup> = SearchRepo.getSuggestions(),
    filters: List<Filter> = SnackRepo.getFilters(),
    searchResults: List<Snack> = emptyList()
): SearchState {
    return remember {
        SearchState(
            query = query,
            focused = focused,
            searching = searching,
            categories = categories,
            suggestions = suggestions,
            filters = filters,
            searchResults = searchResults
        )
    }
}
Run Code Online (Sandbox Code Playgroud)

任何MutableStates 中的任何更改都将触发读取这些值的可组合范围中的重组。

常用的记住函数(例如 RememberScrollState 和其他一些函数)也使用这种方法。

@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())
 // Rest of the code
}
Run Code Online (Sandbox Code Playgroud)