commonMain.com.lt.compose_views.refresh_layout.RefreshLayoutState.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ComposeViews-desktop Show documentation
Show all versions of ComposeViews-desktop Show documentation
Jatpack(JetBrains) Compose views
The newest version!
/*
* Copyright lt 2023
*
* 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
*
* http://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.
*/
package com.lt.compose_views.refresh_layout
import androidx.compose.animation.core.Animatable
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshotFlow
import com.lt.compose_views.util.ComposePosition
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
import kotlin.math.abs
/**
* creator: lt 2022/9/1 [email protected]
* effect : RefreshLayout的状态
* State of the [RefreshLayout]
* warning:
* @param onRefreshListener 触发刷新时的回调(不保证线程)
* Callback when refresh is triggered
*/
@Stable
class RefreshLayoutState(
internal val onRefreshListener: RefreshLayoutState.() -> Unit
) {
//刷新布局内容区域的组件状态
internal val refreshContentState = mutableStateOf(RefreshContentStateEnum.Stop)
//刷新布局内容区域的Offset(位移)的状态和子内容区域的Offset(位移)的状态,如果contentIsMove==false,则一直为0,单位px
internal val refreshContentOffsetState = Animatable(0f)
//composePosition的状态,参考[RefreshLayout]的参数
internal val composePositionState = mutableStateOf(ComposePosition.Top)
//刷新布局拖动的阈值,单位px
internal val refreshContentThresholdState = mutableStateOf(0f)
//协程作用域
internal lateinit var coroutineScope: CoroutineScope
//是否可以触发[onRefreshListener]
var canCallRefreshListener = true
/**
* 获取刷新布局内容区域的组件状态
* Get the [State] of the refresh content
*/
fun getRefreshContentState(): State = refreshContentState
/**
* 创建刷新布局内容区域的Offset(位移)的flow
* Create the [Flow] of the offset
*/
fun createRefreshContentOffsetFlow(): Flow =
snapshotFlow { refreshContentOffsetState.value }
/**
* 获取composePosition的状态,参考[RefreshLayout]的参数
* Get the [State] of the [ComposePosition]
*/
fun getComposePositionState(): State = composePositionState
/**
* 获取刷新布局拖动的阈值,单位px
* Get threshold of the refresh content
*/
fun getRefreshContentThreshold(): Float = refreshContentThresholdState.value
/**
* 刷新布局内容区域的Offset的值,单位px
* Get the offset of content area
*/
fun getRefreshContentOffset(): Float = refreshContentOffsetState.value
/**
* 设置刷新布局的状态
* Set the state of refresh content
*/
fun setRefreshState(state: RefreshContentStateEnum) {
when (state) {
RefreshContentStateEnum.Stop -> {
if (refreshContentState.value == RefreshContentStateEnum.Stop)
return
if (!this::coroutineScope.isInitialized)
throw IllegalStateException("[RefreshLayoutState]还未初始化完成,请在[LaunchedEffect]中或composable至少组合一次后使用此方法")
coroutineScope.launch {
refreshContentState.value = RefreshContentStateEnum.Stop
delay(300)
refreshContentOffsetState.animateTo(0f)
}
}
RefreshContentStateEnum.Refreshing -> {
if (refreshContentState.value == RefreshContentStateEnum.Refreshing)
return
if (!this::coroutineScope.isInitialized)
throw IllegalStateException("[RefreshLayoutState]还未初始化完成,请在[LaunchedEffect]中或composable至少组合一次后使用此方法")
coroutineScope.launch {
refreshContentState.value = RefreshContentStateEnum.Refreshing
if (canCallRefreshListener)
onRefreshListener()
else
setRefreshState(RefreshContentStateEnum.Stop)
animateToThreshold()
}
}
RefreshContentStateEnum.Dragging -> throw IllegalStateException("设置为[RefreshContentStateEnum.Dragging]无意义")
}
}
//偏移量归位,并检查是否超过了刷新阈值,如果超过了执行刷新逻辑
internal fun offsetHoming() {
coroutineScope.launch {
//检查是否进入了刷新状态
if (abs(refreshContentOffsetState.value) >= refreshContentThresholdState.value) {
refreshContentState.value = RefreshContentStateEnum.Refreshing
if (canCallRefreshListener)
onRefreshListener()
else
setRefreshState(RefreshContentStateEnum.Stop)
animateToThreshold()
} else {
refreshContentOffsetState.animateTo(0f)
refreshContentState.value = RefreshContentStateEnum.Stop
}
}
}
//动画滑动至阈值处
private suspend fun animateToThreshold() {
val composePosition = composePositionState.value
if (composePosition == ComposePosition.Start || composePosition == ComposePosition.Top)
refreshContentOffsetState.animateTo(refreshContentThresholdState.value)
else
refreshContentOffsetState.animateTo(-refreshContentThresholdState.value)
}
//增加偏移量
internal fun offset(refreshContentOffset: Float) {
coroutineScope.launch {
val targetValue = refreshContentOffsetState.value + refreshContentOffset
if (refreshContentState.value != RefreshContentStateEnum.Dragging && targetValue != 0f) {
refreshContentState.value = RefreshContentStateEnum.Dragging
}
refreshContentOffsetState.snapTo(targetValue)
}
}
}
/**
* 创建一个[remember]的[RefreshLayoutState]
* Create the [RefreshLayoutState] of [remember]
* @param onRefreshListener 触发刷新时的回调
* Callback when refresh is triggered
*/
@Composable
fun rememberRefreshLayoutState(onRefreshListener: RefreshLayoutState.() -> Unit) =
remember { RefreshLayoutState(onRefreshListener) }
© 2015 - 2025 Weber Informatics LLC | Privacy Policy