图片添加加载效果
- 为AsyncImage添加了Shimmer加载效果 - 优化了热门动态的加载逻辑 - 统一了ViewModel的重置方法名
This commit is contained in:
@@ -194,7 +194,7 @@ object AppState {
|
|||||||
// 重置动态列表页面
|
// 重置动态列表页面
|
||||||
TimelineMomentViewModel.ResetModel()
|
TimelineMomentViewModel.ResetModel()
|
||||||
DynamicViewModel.ResetModel()
|
DynamicViewModel.ResetModel()
|
||||||
HotMomentViewModel.ResetModel()
|
HotMomentViewModel.resetModel()
|
||||||
|
|
||||||
// 重置我的页面
|
// 重置我的页面
|
||||||
MyProfileViewModel.ResetModel()
|
MyProfileViewModel.ResetModel()
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import android.content.Context
|
|||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -79,7 +81,8 @@ fun CustomAsyncImage(
|
|||||||
errorRes: Int? = null,
|
errorRes: Int? = null,
|
||||||
@DrawableRes
|
@DrawableRes
|
||||||
defaultRes: Int? = null,
|
defaultRes: Int? = null,
|
||||||
contentScale: ContentScale = ContentScale.Crop
|
contentScale: ContentScale = ContentScale.Crop,
|
||||||
|
showShimmer: Boolean = true
|
||||||
) {
|
) {
|
||||||
val localContext = LocalContext.current
|
val localContext = LocalContext.current
|
||||||
|
|
||||||
@@ -91,28 +94,88 @@ fun CustomAsyncImage(
|
|||||||
// 优先使用 defaultRes,然后是 placeholderRes
|
// 优先使用 defaultRes,然后是 placeholderRes
|
||||||
val fallbackRes = defaultRes ?: placeholderRes
|
val fallbackRes = defaultRes ?: placeholderRes
|
||||||
if (fallbackRes != null) {
|
if (fallbackRes != null) {
|
||||||
|
if (showShimmer) {
|
||||||
|
SimpleShimmer(modifier = modifier) {
|
||||||
|
Image(
|
||||||
|
painter = androidx.compose.ui.res.painterResource(fallbackRes),
|
||||||
|
contentDescription = contentDescription,
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentScale = contentScale
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
Image(
|
Image(
|
||||||
painter = androidx.compose.ui.res.painterResource(fallbackRes),
|
painter = androidx.compose.ui.res.painterResource(fallbackRes),
|
||||||
contentDescription = contentDescription,
|
contentDescription = contentDescription,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
contentScale = contentScale
|
contentScale = contentScale
|
||||||
)
|
)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理 Bitmap 类型
|
// 处理 Bitmap 类型
|
||||||
if (imageUrl is Bitmap) {
|
if (imageUrl is Bitmap) {
|
||||||
|
if (showShimmer) {
|
||||||
|
SimpleShimmer(modifier = modifier) {
|
||||||
|
Image(
|
||||||
|
bitmap = imageUrl.asImageBitmap(),
|
||||||
|
contentDescription = contentDescription,
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentScale = contentScale
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
Image(
|
Image(
|
||||||
bitmap = imageUrl.asImageBitmap(),
|
bitmap = imageUrl.asImageBitmap(),
|
||||||
contentDescription = contentDescription,
|
contentDescription = contentDescription,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
contentScale = contentScale
|
contentScale = contentScale
|
||||||
)
|
)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理字符串URL
|
// 处理字符串URL
|
||||||
|
if (showShimmer) {
|
||||||
|
var isLoading by remember { mutableStateOf(true) }
|
||||||
|
|
||||||
|
Box(modifier = modifier) {
|
||||||
|
AsyncImage(
|
||||||
|
model = ImageRequest.Builder(context ?: localContext)
|
||||||
|
.data(imageUrl)
|
||||||
|
.crossfade(200)
|
||||||
|
.memoryCachePolicy(coil.request.CachePolicy.ENABLED)
|
||||||
|
.diskCachePolicy(coil.request.CachePolicy.ENABLED)
|
||||||
|
.apply {
|
||||||
|
// 设置占位符图片
|
||||||
|
if (placeholderRes != null) {
|
||||||
|
placeholder(placeholderRes)
|
||||||
|
}
|
||||||
|
// 设置错误时显示的图片
|
||||||
|
if (errorRes != null) {
|
||||||
|
error(errorRes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.build(),
|
||||||
|
contentDescription = contentDescription,
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentScale = contentScale,
|
||||||
|
imageLoader = imageLoader,
|
||||||
|
onLoading = { isLoading = true },
|
||||||
|
onSuccess = { isLoading = false },
|
||||||
|
onError = { isLoading = false }
|
||||||
|
)
|
||||||
|
|
||||||
|
// 只在加载时显示shimmer
|
||||||
|
if (isLoading) {
|
||||||
|
SimpleShimmer(modifier = Modifier.fillMaxSize()) {
|
||||||
|
Box(modifier = Modifier.fillMaxSize())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = ImageRequest.Builder(context ?: localContext)
|
model = ImageRequest.Builder(context ?: localContext)
|
||||||
.data(imageUrl)
|
.data(imageUrl)
|
||||||
@@ -136,6 +199,7 @@ fun CustomAsyncImage(
|
|||||||
imageLoader = imageLoader
|
imageLoader = imageLoader
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
使用示例:
|
使用示例:
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package com.aiosman.ravenow.ui.composables
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.*
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.graphics.Brush
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从左到右扫描的Shimmer加载效果
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun ShimmerEffect(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
content: @Composable () -> Unit
|
||||||
|
) {
|
||||||
|
val infiniteTransition = rememberInfiniteTransition()
|
||||||
|
val translateAnim by infiniteTransition.animateFloat(
|
||||||
|
initialValue = 0f,
|
||||||
|
targetValue = 1000f,
|
||||||
|
animationSpec = infiniteRepeatable(
|
||||||
|
animation = tween(
|
||||||
|
durationMillis = 1200,
|
||||||
|
easing = FastOutSlowInEasing
|
||||||
|
),
|
||||||
|
repeatMode = RepeatMode.Restart
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val shimmerColors = listOf(
|
||||||
|
Color.LightGray.copy(alpha = 0.6f),
|
||||||
|
Color.LightGray.copy(alpha = 0.2f),
|
||||||
|
Color.LightGray.copy(alpha = 0.6f),
|
||||||
|
)
|
||||||
|
|
||||||
|
val brush = Brush.linearGradient(
|
||||||
|
colors = shimmerColors,
|
||||||
|
start = Offset.Zero,
|
||||||
|
end = Offset(x = translateAnim, y = translateAnim)
|
||||||
|
)
|
||||||
|
|
||||||
|
Box(modifier = modifier) {
|
||||||
|
content()
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(brush)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 带圆角的Shimmer占位符
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun ShimmerPlaceholder(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
cornerRadius: Float = 8f
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.background(
|
||||||
|
color = Color.LightGray.copy(alpha = 0.3f),
|
||||||
|
shape = RoundedCornerShape(cornerRadius.dp)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package com.aiosman.ravenow.ui.composables
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.*
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.graphics.Brush
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 简单的从左到右扫描的Shimmer加载效果
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun SimpleShimmer(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
content: @Composable () -> Unit
|
||||||
|
) {
|
||||||
|
val infiniteTransition = rememberInfiniteTransition()
|
||||||
|
val translateAnim by infiniteTransition.animateFloat(
|
||||||
|
initialValue = 0f,
|
||||||
|
targetValue = 1000f,
|
||||||
|
animationSpec = infiniteRepeatable(
|
||||||
|
animation = tween(
|
||||||
|
durationMillis = 1200,
|
||||||
|
easing = FastOutSlowInEasing
|
||||||
|
),
|
||||||
|
repeatMode = RepeatMode.Restart
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val shimmerColors = listOf(
|
||||||
|
Color.LightGray.copy(alpha = 0.6f),
|
||||||
|
Color.LightGray.copy(alpha = 0.2f),
|
||||||
|
Color.LightGray.copy(alpha = 0.6f),
|
||||||
|
)
|
||||||
|
|
||||||
|
val brush = Brush.linearGradient(
|
||||||
|
colors = shimmerColors,
|
||||||
|
start = Offset.Zero,
|
||||||
|
end = Offset(x = translateAnim, y = translateAnim)
|
||||||
|
)
|
||||||
|
|
||||||
|
Box(modifier = modifier) {
|
||||||
|
content()
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(brush)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 带圆角的Shimmer占位符
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun SimpleShimmerPlaceholder(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
cornerRadius: Float = 8f
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.background(
|
||||||
|
color = Color.LightGray.copy(alpha = 0.3f),
|
||||||
|
shape = RoundedCornerShape(cornerRadius.dp)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -2,51 +2,86 @@ package com.aiosman.ravenow.ui.index.tabs.moment.tabs.hot
|
|||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.paging.Pager
|
|
||||||
import androidx.paging.PagingConfig
|
|
||||||
import androidx.paging.PagingData
|
|
||||||
import androidx.paging.cachedIn
|
|
||||||
import com.aiosman.ravenow.AppState
|
|
||||||
import com.aiosman.ravenow.data.MomentService
|
import com.aiosman.ravenow.data.MomentService
|
||||||
import com.aiosman.ravenow.entity.MomentEntity
|
import com.aiosman.ravenow.entity.MomentEntity
|
||||||
import com.aiosman.ravenow.entity.MomentLoaderExtraArgs
|
|
||||||
import com.aiosman.ravenow.entity.MomentPagingSource
|
|
||||||
import com.aiosman.ravenow.entity.MomentRemoteDataSource
|
|
||||||
import com.aiosman.ravenow.entity.MomentServiceImpl
|
import com.aiosman.ravenow.entity.MomentServiceImpl
|
||||||
import com.aiosman.ravenow.ui.index.tabs.moment.BaseMomentModel
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.greenrobot.eventbus.EventBus
|
|
||||||
|
|
||||||
|
|
||||||
object HotMomentViewModel : ViewModel() {
|
object HotMomentViewModel : ViewModel() {
|
||||||
private val momentService: MomentService = MomentServiceImpl()
|
private val momentService: MomentService = MomentServiceImpl()
|
||||||
private val _discoverMomentsFlow =
|
private val _discoverMoments = MutableStateFlow<List<MomentEntity>>(emptyList())
|
||||||
MutableStateFlow<PagingData<MomentEntity>>(PagingData.empty())
|
val discoverMoments = _discoverMoments.asStateFlow()
|
||||||
val discoverMomentsFlow = _discoverMomentsFlow.asStateFlow()
|
|
||||||
var firstLoad = true
|
private val _isLoading = MutableStateFlow(false)
|
||||||
fun refreshPager() {
|
val isLoading = _isLoading.asStateFlow()
|
||||||
if (!firstLoad) {
|
|
||||||
return
|
private val _isRefreshing = MutableStateFlow(false)
|
||||||
}
|
val isRefreshing = _isRefreshing.asStateFlow()
|
||||||
firstLoad = false
|
|
||||||
|
private var currentPage = 1
|
||||||
|
private var hasMoreData = true
|
||||||
|
private val pageSize = 20
|
||||||
|
|
||||||
|
fun loadMoments() {
|
||||||
|
if (_isLoading.value || !hasMoreData) return
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
Pager(
|
_isLoading.value = true
|
||||||
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
|
try {
|
||||||
pagingSourceFactory = {
|
val response = momentService.getMoments(
|
||||||
MomentPagingSource(
|
pageNumber = currentPage,
|
||||||
MomentRemoteDataSource(momentService),
|
|
||||||
trend = true
|
trend = true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (response.list.isNotEmpty()) {
|
||||||
|
if (currentPage == 1) {
|
||||||
|
_discoverMoments.value = response.list
|
||||||
|
} else {
|
||||||
|
_discoverMoments.value = _discoverMoments.value + response.list
|
||||||
}
|
}
|
||||||
).flow.cachedIn(viewModelScope).collectLatest {
|
currentPage++
|
||||||
_discoverMomentsFlow.value = it
|
hasMoreData = response.list.size >= response.pageSize
|
||||||
|
} else {
|
||||||
|
hasMoreData = false
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// 处理错误
|
||||||
|
e.printStackTrace()
|
||||||
|
} finally {
|
||||||
|
_isLoading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fun ResetModel(){
|
|
||||||
firstLoad = true
|
fun refreshMoments() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_isRefreshing.value = true
|
||||||
|
currentPage = 1
|
||||||
|
hasMoreData = true
|
||||||
|
try {
|
||||||
|
val response = momentService.getMoments(
|
||||||
|
pageNumber = 1,
|
||||||
|
trend = true
|
||||||
|
)
|
||||||
|
_discoverMoments.value = response.list
|
||||||
|
currentPage = 2
|
||||||
|
hasMoreData = response.list.size >= response.pageSize
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
} finally {
|
||||||
|
_isRefreshing.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetModel() {
|
||||||
|
currentPage = 1
|
||||||
|
hasMoreData = true
|
||||||
|
_discoverMoments.value = emptyList()
|
||||||
|
_isLoading.value = false
|
||||||
|
_isRefreshing.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -15,6 +15,8 @@ import androidx.compose.foundation.layout.size
|
|||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.grid.GridCells
|
import androidx.compose.foundation.lazy.grid.GridCells
|
||||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
|
import androidx.compose.foundation.lazy.grid.items
|
||||||
|
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.ExperimentalMaterialApi
|
import androidx.compose.material.ExperimentalMaterialApi
|
||||||
@@ -29,13 +31,15 @@ import androidx.compose.runtime.mutableStateOf
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.runtime.snapshotFlow
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.paging.compose.collectAsLazyPagingItems
|
import androidx.compose.runtime.collectAsState
|
||||||
import com.aiosman.ravenow.LocalAppTheme
|
import com.aiosman.ravenow.LocalAppTheme
|
||||||
import com.aiosman.ravenow.LocalNavController
|
import com.aiosman.ravenow.LocalNavController
|
||||||
import com.aiosman.ravenow.R
|
import com.aiosman.ravenow.R
|
||||||
@@ -57,12 +61,14 @@ fun HotMomentsList() {
|
|||||||
val navController = LocalNavController.current
|
val navController = LocalNavController.current
|
||||||
val navigationBarPaddings =
|
val navigationBarPaddings =
|
||||||
WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() + 48.dp
|
WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() + 48.dp
|
||||||
|
val isRefreshing by model.isRefreshing.collectAsState()
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
model.refreshPager()
|
model.loadMoments()
|
||||||
}
|
}
|
||||||
var refreshing by remember { mutableStateOf(false) }
|
|
||||||
val state = rememberPullRefreshState(refreshing, onRefresh = {
|
val state = rememberPullRefreshState(isRefreshing, onRefresh = {
|
||||||
model.refreshPager()
|
model.refreshMoments()
|
||||||
})
|
})
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
@@ -84,7 +90,7 @@ fun HotMomentsList() {
|
|||||||
.padding(2.dp)
|
.padding(2.dp)
|
||||||
) {
|
) {
|
||||||
DiscoverView()
|
DiscoverView()
|
||||||
PullRefreshIndicator(refreshing, state, Modifier.align(Alignment.TopCenter))
|
PullRefreshIndicator(isRefreshing, state, Modifier.align(Alignment.TopCenter))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,17 +99,37 @@ fun HotMomentsList() {
|
|||||||
@Composable
|
@Composable
|
||||||
fun DiscoverView() {
|
fun DiscoverView() {
|
||||||
val model = HotMomentViewModel
|
val model = HotMomentViewModel
|
||||||
var dataFlow = model.discoverMomentsFlow
|
val moments by model.discoverMoments.collectAsState()
|
||||||
var moments = dataFlow.collectAsLazyPagingItems()
|
val isLoading by model.isLoading.collectAsState()
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val navController = LocalNavController.current
|
val navController = LocalNavController.current
|
||||||
|
val gridState = rememberLazyGridState()
|
||||||
|
|
||||||
|
// 监听滚动到底部,自动加载更多
|
||||||
|
LaunchedEffect(gridState, moments.size) {
|
||||||
|
snapshotFlow {
|
||||||
|
val layoutInfo = gridState.layoutInfo
|
||||||
|
val visibleItemsInfo = layoutInfo.visibleItemsInfo
|
||||||
|
if (visibleItemsInfo.isNotEmpty() && moments.isNotEmpty()) {
|
||||||
|
val lastVisibleItemIndex = visibleItemsInfo.lastOrNull()?.index ?: 0
|
||||||
|
lastVisibleItemIndex >= moments.size - 6 // 距离底部还有6个项目时开始加载
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}.collectLatest { shouldLoadMore ->
|
||||||
|
if (shouldLoadMore && !isLoading) {
|
||||||
|
model.loadMoments()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LazyVerticalGrid(
|
LazyVerticalGrid(
|
||||||
columns = GridCells.Fixed(3),
|
columns = GridCells.Fixed(3),
|
||||||
|
state = gridState,
|
||||||
modifier = Modifier.fillMaxSize().padding(bottom = 8.dp),
|
modifier = Modifier.fillMaxSize().padding(bottom = 8.dp),
|
||||||
// contentPadding = PaddingValues(8.dp)
|
// contentPadding = PaddingValues(8.dp)
|
||||||
) {
|
) {
|
||||||
items(moments.itemCount) { idx ->
|
items(moments) { momentItem ->
|
||||||
val momentItem = moments[idx] ?: return@items
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -122,7 +148,8 @@ fun DiscoverView() {
|
|||||||
contentDescription = "",
|
contentDescription = "",
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize(),
|
.fillMaxSize(),
|
||||||
context = context
|
context = context,
|
||||||
|
showShimmer = true
|
||||||
)
|
)
|
||||||
if (momentItem.images.size > 1) {
|
if (momentItem.images.size > 1) {
|
||||||
Box(
|
Box(
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ object ResourceCleanupManager {
|
|||||||
// 重置动态相关ViewModel
|
// 重置动态相关ViewModel
|
||||||
TimelineMomentViewModel.ResetModel()
|
TimelineMomentViewModel.ResetModel()
|
||||||
DynamicViewModel.ResetModel()
|
DynamicViewModel.ResetModel()
|
||||||
HotMomentViewModel.ResetModel()
|
HotMomentViewModel.resetModel()
|
||||||
|
|
||||||
// 重置个人资料相关ViewModel
|
// 重置个人资料相关ViewModel
|
||||||
MyProfileViewModel.ResetModel()
|
MyProfileViewModel.ResetModel()
|
||||||
@@ -233,7 +233,7 @@ object ResourceCleanupManager {
|
|||||||
"moment" -> {
|
"moment" -> {
|
||||||
TimelineMomentViewModel.ResetModel()
|
TimelineMomentViewModel.ResetModel()
|
||||||
DynamicViewModel.ResetModel()
|
DynamicViewModel.ResetModel()
|
||||||
HotMomentViewModel.ResetModel()
|
HotMomentViewModel.resetModel()
|
||||||
}
|
}
|
||||||
"profile" -> {
|
"profile" -> {
|
||||||
MyProfileViewModel.ResetModel()
|
MyProfileViewModel.ResetModel()
|
||||||
|
|||||||
Reference in New Issue
Block a user