mirror of
https://github.com/BetterAndroid/Hikage.git
synced 2025-09-05 10:15:37 +08:00
feat: add State
This commit is contained in:
@@ -444,6 +444,66 @@ val subLayout = Hikageable<LinearLayout.LayoutParams> {
|
||||
}
|
||||
```
|
||||
|
||||
### State Management
|
||||
|
||||
Hikage has a similar state management workaround to Jetpack Compose, which makes it easy to set up state listening for layout components.
|
||||
|
||||
Hikage provides two states, `NonNullState` and `NullableState`, which are divided into two states: holding non-null and nullable.
|
||||
|
||||
Unlike the recompose of Jetpack Compose, Hikage will not be recomposed, and the states takes effect through listening and callbacks.
|
||||
|
||||
You can use both states in the following scenarios.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val myLayout = Hikageable {
|
||||
// Declare a non-null variable state.
|
||||
val mTextState = mutableStateOf("Hello, World!")
|
||||
// Declare a nullable and variable state.
|
||||
val mDrawState = mutableStateOfNull<Drawable>()
|
||||
// You can delegate the state to a variable.
|
||||
var mText by mTextState
|
||||
var mDraw by mDrawState
|
||||
LinearLayout(
|
||||
lparams = LayoutParams(matchParent = true),
|
||||
init = {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
}
|
||||
) {
|
||||
TextView {
|
||||
textSize = 16f
|
||||
gravity = Gravity.CENTER
|
||||
// Set (binding) state to text.
|
||||
setState(mTextState) {
|
||||
text = it
|
||||
}
|
||||
}
|
||||
ImageView {
|
||||
// Set (binding) state to Drawable.
|
||||
setState(mDrawState) {
|
||||
setImageDrawable(it)
|
||||
}
|
||||
}
|
||||
Button {
|
||||
text = "Click Me!"
|
||||
setOnClickListener {
|
||||
// Modify the value of non-null state.
|
||||
mText = "Hello, Hikage!"
|
||||
// Modify the value of the nullable state.
|
||||
mDraw = drawableResource(R.drawable.ic_my_drawable)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In the example above, we declare a non-null state `mTextState` with `"Hello, World!"` with `mutableStateOf`
|
||||
Then continue to declare a nullable state `mDrawState` with `null` using `mutableStateOfNull`.
|
||||
|
||||
When clicking the button, we modify the value of `mTextState` to `"Hello, Hikage!"` and the value of `mDrawState` is the property resource `R.drawable.ic_my_drawable`.
|
||||
At this time, the text and images of `TextView` and `ImageView` will be automatically updated.
|
||||
|
||||
### Custom Layout Factory
|
||||
|
||||
Hikage supports custom layout factories and is compatible with `LayoutInflater.Factory2`.
|
||||
|
@@ -425,6 +425,66 @@ val subLayout = Hikageable<LinearLayout.LayoutParams> {
|
||||
}
|
||||
```
|
||||
|
||||
### 状态管理
|
||||
|
||||
Hikage 拥有与 Jetpack Compose 类似的状态管理解决方法,它可以轻松地设置布局组件的状态监听。
|
||||
|
||||
Hikage 提供了两种状态,`NonNullState` 和 `NullableState`,分为持有非空和可空两种状态。
|
||||
|
||||
不同于 Jetpack Compose 的重组 (Recompose),Hikage 不会重组,状态通过监听与回调生效。
|
||||
|
||||
你可以在如下场景中使用这两种状态。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
val myLayout = Hikageable {
|
||||
// 声明一个非空可变状态
|
||||
val mTextState = mutableStateOf("Hello, World!")
|
||||
// 声明一个可空可变状态
|
||||
val mDrawState = mutableStateOfNull<Drawable>()
|
||||
// 你可以将状态委托给一个变量
|
||||
var mText by mTextState
|
||||
var mDraw by mDrawState
|
||||
LinearLayout(
|
||||
lparams = LayoutParams(matchParent = true),
|
||||
init = {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
}
|
||||
) {
|
||||
TextView {
|
||||
textSize = 16f
|
||||
gravity = Gravity.CENTER
|
||||
// 设置 (绑定) 状态到文本
|
||||
setState(mTextState) {
|
||||
text = it
|
||||
}
|
||||
}
|
||||
ImageView {
|
||||
// 设置 (绑定) 状态到 Drawable
|
||||
setState(mDrawState) {
|
||||
setImageDrawable(it)
|
||||
}
|
||||
}
|
||||
Button {
|
||||
text = "Click Me!"
|
||||
setOnClickListener {
|
||||
// 修改非空状态的值
|
||||
mText = "Hello, Hikage!"
|
||||
// 修改可空状态的值
|
||||
mDraw = drawableResource(R.drawable.ic_my_drawable)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
在上面的示例中,我们使用 `mutableStateOf` 声明了一个非空状态 `mTextState`,它的初始值为 `"Hello, World!"`,
|
||||
然后继续使用 `mutableStateOfNull` 声明了一个可空状态 `mDrawState`,它的初始值为 `null`。
|
||||
|
||||
在点击按钮时,我们修改 `mTextState` 的值为 `"Hello, Hikage!"`,`mDrawState` 的值为属性资源 `R.drawable.ic_my_drawable`,
|
||||
这时 `TextView` 和 `ImageView` 的文本和图片将会自动更新。
|
||||
|
||||
### 自定义布局装载器
|
||||
|
||||
Hikage 支持自定义布局装载器并同时兼容 `LayoutInflater.Factory2`,你可以通过以下方式自定义在 Hikage 布局装载过程中的事件和监听。
|
||||
|
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Hikage - An Android responsive UI building tool.
|
||||
* Copyright (C) 2019 HighCapable
|
||||
* https://github.com/BetterAndroid/Hikage
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2025/5/2.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
@file:JvmName("StateUtils")
|
||||
|
||||
package com.highcapable.hikage.core.runtime
|
||||
|
||||
import com.highcapable.hikage.core.Hikage
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
/**
|
||||
* Definition a [Hikage] runtime state interface.
|
||||
*/
|
||||
interface State<T> : ReadWriteProperty<Any?, T>
|
||||
|
||||
/**
|
||||
* Definition a [Hikage] runtime state interface for non-nullable type.
|
||||
*/
|
||||
interface NonNullState<T> : State<T> {
|
||||
|
||||
/** The current value of the state. */
|
||||
var value: T
|
||||
|
||||
/**
|
||||
* Observe the state changes.
|
||||
* @param observer the observer to be notified when the state changes.
|
||||
*/
|
||||
fun observe(observer: (T) -> Unit)
|
||||
}
|
||||
|
||||
/**
|
||||
* Definition a [Hikage] runtime state interface for nullable type.
|
||||
*/
|
||||
interface NullableState<T> : State<T?> {
|
||||
|
||||
/** The current value of the state. */
|
||||
var value: T?
|
||||
|
||||
/**
|
||||
* Observe the state changes.
|
||||
* @param observer the observer to be notified when the state changes.
|
||||
*/
|
||||
fun observe(observer: (T?) -> Unit)
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementing the [State] interface mutable state of [Hikage].
|
||||
*/
|
||||
class MutableState<T> private constructor() {
|
||||
|
||||
/**
|
||||
* The non-nullable state of [Hikage].
|
||||
*/
|
||||
class NonNull<T> internal constructor(private var holder: T) : NonNullState<T> {
|
||||
|
||||
private val observers = mutableSetOf<(T) -> Unit>()
|
||||
|
||||
override var value get() = holder
|
||||
set(value) {
|
||||
if (holder == value) return
|
||||
holder = value
|
||||
observers.forEach { it(value) }
|
||||
}
|
||||
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>) = value
|
||||
|
||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
||||
this.value = value
|
||||
}
|
||||
|
||||
override fun observe(observer: (T) -> Unit) {
|
||||
observers += observer
|
||||
observer(value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The nullable state of [Hikage].
|
||||
*/
|
||||
class Nullable<T> internal constructor(private var holder: T?) : NullableState<T?> {
|
||||
|
||||
private val observers = mutableSetOf<(T?) -> Unit>()
|
||||
|
||||
override var value get() = holder
|
||||
set(value) {
|
||||
if (holder == value) return
|
||||
holder = value
|
||||
observers.forEach { it(value) }
|
||||
}
|
||||
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>) = value
|
||||
|
||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
|
||||
this.value = value
|
||||
}
|
||||
|
||||
override fun observe(observer: (T?) -> Unit) {
|
||||
observers += observer
|
||||
observer(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a mutable state of [Hikage] with the specified value.
|
||||
* @param value the initial value of the state.
|
||||
* @return [MutableState.NonNull]
|
||||
*/
|
||||
fun <T> mutableStateOf(value: T) = MutableState.NonNull(value)
|
||||
|
||||
/**
|
||||
* Create a mutable state of [Hikage] with the specified value.
|
||||
* @param value the initial value of the state.
|
||||
* @return [MutableState.Nullable]
|
||||
*/
|
||||
fun <T> mutableStateOfNull(value: T? = null) = MutableState.Nullable(value)
|
||||
|
||||
/**
|
||||
* Set the [Hikage] state value.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* ```kotlin
|
||||
* val textState = mutableStateOf("Hello World!")
|
||||
* var text by textState
|
||||
* TextView {
|
||||
* setState(textState) {
|
||||
* text = it
|
||||
* }
|
||||
* }
|
||||
* // Modify the state.
|
||||
* text = "Hello Hikage!"
|
||||
* ```
|
||||
* @param state the state to be set.
|
||||
* @param apply the apply body.
|
||||
*/
|
||||
inline fun <T, R> R.setState(state: NonNullState<T>, crossinline apply: R.(T) -> Unit) {
|
||||
state.observe {
|
||||
this.apply(it)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the [Hikage] state value.
|
||||
* @see setState
|
||||
* @param state the state to be set.
|
||||
* @param apply the apply body.
|
||||
*/
|
||||
inline fun <T, R> R.setState(state: NullableState<T>, crossinline apply: R.(T?) -> Unit) {
|
||||
state.observe {
|
||||
this.apply(it)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user