Skip to content

Commit 036017a

Browse files
committed
feat: add State
1 parent 7c8c025 commit 036017a

File tree

3 files changed

+293
-0
lines changed

3 files changed

+293
-0
lines changed

docs-source/src/en/library/hikage-core.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,66 @@ val subLayout = Hikageable<LinearLayout.LayoutParams> {
444444
}
445445
```
446446

447+
### State Management
448+
449+
Hikage has a similar state management workaround to Jetpack Compose, which makes it easy to set up state listening for layout components.
450+
451+
Hikage provides two states, `NonNullState` and `NullableState`, which are divided into two states: holding non-null and nullable.
452+
453+
Unlike the recompose of Jetpack Compose, Hikage will not be recomposed, and the states takes effect through listening and callbacks.
454+
455+
You can use both states in the following scenarios.
456+
457+
> The following example
458+
459+
```kotlin
460+
val myLayout = Hikageable {
461+
// Declare a non-null variable state.
462+
val mTextState = mutableStateOf("Hello, World!")
463+
// Declare a nullable and variable state.
464+
val mDrawState = mutableStateOfNull<Drawable>()
465+
// You can delegate the state to a variable.
466+
var mText by mTextState
467+
var mDraw by mDrawState
468+
LinearLayout(
469+
lparams = LayoutParams(matchParent = true),
470+
init = {
471+
orientation = LinearLayout.VERTICAL
472+
}
473+
) {
474+
TextView {
475+
textSize = 16f
476+
gravity = Gravity.CENTER
477+
// Set (binding) state to text.
478+
setState(mTextState) {
479+
text = it
480+
}
481+
}
482+
ImageView {
483+
// Set (binding) state to Drawable.
484+
setState(mDrawState) {
485+
setImageDrawable(it)
486+
}
487+
}
488+
Button {
489+
text = "Click Me!"
490+
setOnClickListener {
491+
// Modify the value of non-null state.
492+
mText = "Hello, Hikage!"
493+
// Modify the value of the nullable state.
494+
mDraw = drawableResource(R.drawable.ic_my_drawable)
495+
}
496+
}
497+
}
498+
}
499+
```
500+
501+
In the example above, we declare a non-null state `mTextState` with `"Hello, World!"` with `mutableStateOf`
502+
Then continue to declare a nullable state `mDrawState` with `null` using `mutableStateOfNull`.
503+
504+
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`.
505+
At this time, the text and images of `TextView` and `ImageView` will be automatically updated.
506+
447507
### Custom Layout Factory
448508

449509
Hikage supports custom layout factories and is compatible with `LayoutInflater.Factory2`.

docs-source/src/zh-cn/library/hikage-core.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,66 @@ val subLayout = Hikageable<LinearLayout.LayoutParams> {
425425
}
426426
```
427427

428+
### 状态管理
429+
430+
Hikage 拥有与 Jetpack Compose 类似的状态管理解决方法,它可以轻松地设置布局组件的状态监听。
431+
432+
Hikage 提供了两种状态,`NonNullState``NullableState`,分为持有非空和可空两种状态。
433+
434+
不同于 Jetpack Compose 的重组 (Recompose),Hikage 不会重组,状态通过监听与回调生效。
435+
436+
你可以在如下场景中使用这两种状态。
437+
438+
> 示例如下
439+
440+
```kotlin
441+
val myLayout = Hikageable {
442+
// 声明一个非空可变状态
443+
val mTextState = mutableStateOf("Hello, World!")
444+
// 声明一个可空可变状态
445+
val mDrawState = mutableStateOfNull<Drawable>()
446+
// 你可以将状态委托给一个变量
447+
var mText by mTextState
448+
var mDraw by mDrawState
449+
LinearLayout(
450+
lparams = LayoutParams(matchParent = true),
451+
init = {
452+
orientation = LinearLayout.VERTICAL
453+
}
454+
) {
455+
TextView {
456+
textSize = 16f
457+
gravity = Gravity.CENTER
458+
// 设置 (绑定) 状态到文本
459+
setState(mTextState) {
460+
text = it
461+
}
462+
}
463+
ImageView {
464+
// 设置 (绑定) 状态到 Drawable
465+
setState(mDrawState) {
466+
setImageDrawable(it)
467+
}
468+
}
469+
Button {
470+
text = "Click Me!"
471+
setOnClickListener {
472+
// 修改非空状态的值
473+
mText = "Hello, Hikage!"
474+
// 修改可空状态的值
475+
mDraw = drawableResource(R.drawable.ic_my_drawable)
476+
}
477+
}
478+
}
479+
}
480+
```
481+
482+
在上面的示例中,我们使用 `mutableStateOf` 声明了一个非空状态 `mTextState`,它的初始值为 `"Hello, World!"`
483+
然后继续使用 `mutableStateOfNull` 声明了一个可空状态 `mDrawState`,它的初始值为 `null`
484+
485+
在点击按钮时,我们修改 `mTextState` 的值为 `"Hello, Hikage!"``mDrawState` 的值为属性资源 `R.drawable.ic_my_drawable`
486+
这时 `TextView``ImageView` 的文本和图片将会自动更新。
487+
428488
### 自定义布局装载器
429489

430490
Hikage 支持自定义布局装载器并同时兼容 `LayoutInflater.Factory2`,你可以通过以下方式自定义在 Hikage 布局装载过程中的事件和监听。
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
/*
2+
* Hikage - An Android responsive UI building tool.
3+
* Copyright (C) 2019 HighCapable
4+
* https://github.com/BetterAndroid/Hikage
5+
*
6+
* Apache License Version 2.0
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* https://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*
20+
* This file is created by fankes on 2025/5/2.
21+
*/
22+
@file:Suppress("unused")
23+
@file:JvmName("StateUtils")
24+
25+
package com.highcapable.hikage.core.runtime
26+
27+
import com.highcapable.hikage.core.Hikage
28+
import kotlin.properties.ReadWriteProperty
29+
import kotlin.reflect.KProperty
30+
31+
/**
32+
* Definition a [Hikage] runtime state interface.
33+
*/
34+
interface State<T> : ReadWriteProperty<Any?, T>
35+
36+
/**
37+
* Definition a [Hikage] runtime state interface for non-nullable type.
38+
*/
39+
interface NonNullState<T> : State<T> {
40+
41+
/** The current value of the state. */
42+
var value: T
43+
44+
/**
45+
* Observe the state changes.
46+
* @param observer the observer to be notified when the state changes.
47+
*/
48+
fun observe(observer: (T) -> Unit)
49+
}
50+
51+
/**
52+
* Definition a [Hikage] runtime state interface for nullable type.
53+
*/
54+
interface NullableState<T> : State<T?> {
55+
56+
/** The current value of the state. */
57+
var value: T?
58+
59+
/**
60+
* Observe the state changes.
61+
* @param observer the observer to be notified when the state changes.
62+
*/
63+
fun observe(observer: (T?) -> Unit)
64+
}
65+
66+
/**
67+
* Implementing the [State] interface mutable state of [Hikage].
68+
*/
69+
class MutableState<T> private constructor() {
70+
71+
/**
72+
* The non-nullable state of [Hikage].
73+
*/
74+
class NonNull<T> internal constructor(private var holder: T) : NonNullState<T> {
75+
76+
private val observers = mutableSetOf<(T) -> Unit>()
77+
78+
override var value get() = holder
79+
set(value) {
80+
if (holder == value) return
81+
holder = value
82+
observers.forEach { it(value) }
83+
}
84+
85+
override fun getValue(thisRef: Any?, property: KProperty<*>) = value
86+
87+
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
88+
this.value = value
89+
}
90+
91+
override fun observe(observer: (T) -> Unit) {
92+
observers += observer
93+
observer(value)
94+
}
95+
}
96+
97+
/**
98+
* The nullable state of [Hikage].
99+
*/
100+
class Nullable<T> internal constructor(private var holder: T?) : NullableState<T?> {
101+
102+
private val observers = mutableSetOf<(T?) -> Unit>()
103+
104+
override var value get() = holder
105+
set(value) {
106+
if (holder == value) return
107+
holder = value
108+
observers.forEach { it(value) }
109+
}
110+
111+
override fun getValue(thisRef: Any?, property: KProperty<*>) = value
112+
113+
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
114+
this.value = value
115+
}
116+
117+
override fun observe(observer: (T?) -> Unit) {
118+
observers += observer
119+
observer(value)
120+
}
121+
}
122+
}
123+
124+
/**
125+
* Create a mutable state of [Hikage] with the specified value.
126+
* @param value the initial value of the state.
127+
* @return [MutableState.NonNull]
128+
*/
129+
fun <T> mutableStateOf(value: T) = MutableState.NonNull(value)
130+
131+
/**
132+
* Create a mutable state of [Hikage] with the specified value.
133+
* @param value the initial value of the state.
134+
* @return [MutableState.Nullable]
135+
*/
136+
fun <T> mutableStateOfNull(value: T? = null) = MutableState.Nullable(value)
137+
138+
/**
139+
* Set the [Hikage] state value.
140+
*
141+
* Usage:
142+
*
143+
* ```kotlin
144+
* val textState = mutableStateOf("Hello World!")
145+
* var text by textState
146+
* TextView {
147+
* setState(textState) {
148+
* text = it
149+
* }
150+
* }
151+
* // Modify the state.
152+
* text = "Hello Hikage!"
153+
* ```
154+
* @param state the state to be set.
155+
* @param apply the apply body.
156+
*/
157+
inline fun <T, R> R.setState(state: NonNullState<T>, crossinline apply: R.(T) -> Unit) {
158+
state.observe {
159+
this.apply(it)
160+
}
161+
}
162+
163+
/**
164+
* Set the [Hikage] state value.
165+
* @see setState
166+
* @param state the state to be set.
167+
* @param apply the apply body.
168+
*/
169+
inline fun <T, R> R.setState(state: NullableState<T>, crossinline apply: R.(T?) -> Unit) {
170+
state.observe {
171+
this.apply(it)
172+
}
173+
}

0 commit comments

Comments
 (0)