修改动态页面标签行效果
实现切换标签页时,标签行自动滚动
This commit is contained in:
@@ -163,7 +163,7 @@ fun NotificationsScreen() {
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.noRippleClickable {
|
||||
// TODO: 实现搜索功能
|
||||
navController.navigate(NavigationRoute.Search.route)
|
||||
},
|
||||
colorFilter = ColorFilter.tint(AppColors.text)
|
||||
)
|
||||
|
||||
@@ -29,9 +29,12 @@ import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
@@ -57,6 +60,7 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.layout.positionInParent
|
||||
import com.aiosman.ravenow.ui.composables.TabItem
|
||||
import com.aiosman.ravenow.ui.composables.UnderlineTabItem
|
||||
import com.aiosman.ravenow.ui.composables.rememberDebouncer
|
||||
@@ -77,6 +81,14 @@ fun MomentsList() {
|
||||
val tabCount = if (AppStore.isGuest) 5 else 6
|
||||
var pagerState = rememberPagerState { tabCount }
|
||||
var scope = rememberCoroutineScope()
|
||||
val scrollState = rememberScrollState()
|
||||
val density = LocalDensity.current
|
||||
// 存储每个标签的位置和宽度信息 (页面索引, 位置, 宽度)
|
||||
val tabPositions = remember { mutableStateOf<List<Triple<Int, Float, Float>>>(emptyList()) }
|
||||
// 存储容器宽度
|
||||
val containerWidth = remember { mutableStateOf(0f) }
|
||||
// 记录上一次的页面索引
|
||||
val previousPage = remember { mutableStateOf(pagerState.currentPage) }
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
@@ -96,113 +108,257 @@ fun MomentsList() {
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// 可滚动的标签页行
|
||||
Row(
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.horizontalScroll(rememberScrollState()),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
.onGloballyPositioned { coordinates ->
|
||||
containerWidth.value = with(density) { coordinates.size.width.toDp().toPx() }
|
||||
}
|
||||
) {
|
||||
val tabDebouncer = rememberDebouncer()
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.horizontalScroll(scrollState),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
val tabDebouncer = rememberDebouncer()
|
||||
|
||||
// 推荐标签
|
||||
UnderlineTabItem(
|
||||
text = stringResource(R.string.tab_recommend),
|
||||
isSelected = pagerState.currentPage == 0,
|
||||
onClick = {
|
||||
tabDebouncer {
|
||||
scope.launch {
|
||||
pagerState.animateScrollToPage(0)
|
||||
// 推荐标签
|
||||
Box(
|
||||
modifier = Modifier.onGloballyPositioned { coordinates ->
|
||||
val position = with(density) { coordinates.positionInParent().x.toDp().toPx() }
|
||||
val width = with(density) { coordinates.size.width.toDp().toPx() }
|
||||
val currentPositions = tabPositions.value.toMutableList()
|
||||
val existingIndex = currentPositions.indexOfFirst { it.first == 0 }
|
||||
if (existingIndex == -1) {
|
||||
currentPositions.add(Triple(0, position, width))
|
||||
tabPositions.value = currentPositions.sortedBy { it.first }
|
||||
} else {
|
||||
currentPositions[existingIndex] = Triple(0, position, width)
|
||||
tabPositions.value = currentPositions
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
// 短视频标签
|
||||
UnderlineTabItem(
|
||||
text = stringResource(R.string.tab_short_video),
|
||||
isSelected = pagerState.currentPage == 1,
|
||||
onClick = {
|
||||
tabDebouncer {
|
||||
scope.launch {
|
||||
pagerState.animateScrollToPage(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
// 动态标签
|
||||
UnderlineTabItem(
|
||||
text = stringResource(R.string.moment),
|
||||
isSelected = pagerState.currentPage == 2,
|
||||
onClick = {
|
||||
tabDebouncer {
|
||||
scope.launch {
|
||||
pagerState.animateScrollToPage(2)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
// 只有非游客用户才显示"关注"tab
|
||||
if (!AppStore.isGuest) {
|
||||
UnderlineTabItem(
|
||||
text = stringResource(R.string.index_following),
|
||||
isSelected = pagerState.currentPage == 3,
|
||||
onClick = {
|
||||
tabDebouncer {
|
||||
scope.launch {
|
||||
pagerState.animateScrollToPage(3)
|
||||
) {
|
||||
UnderlineTabItem(
|
||||
text = stringResource(R.string.tab_recommend),
|
||||
isSelected = pagerState.currentPage == 0,
|
||||
onClick = {
|
||||
tabDebouncer {
|
||||
scope.launch {
|
||||
pagerState.animateScrollToPage(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// 热门标签
|
||||
UnderlineTabItem(
|
||||
text = stringResource(R.string.index_hot),
|
||||
isSelected = pagerState.currentPage == 4,
|
||||
onClick = {
|
||||
tabDebouncer {
|
||||
scope.launch {
|
||||
pagerState.animateScrollToPage(4)
|
||||
}
|
||||
// 短视频标签
|
||||
Box(
|
||||
modifier = Modifier.onGloballyPositioned { coordinates ->
|
||||
val position = with(density) { coordinates.positionInParent().x.toDp().toPx() }
|
||||
val width = with(density) { coordinates.size.width.toDp().toPx() }
|
||||
val currentPositions = tabPositions.value.toMutableList()
|
||||
val existingIndex = currentPositions.indexOfFirst { it.first == 1 }
|
||||
if (existingIndex == -1) {
|
||||
currentPositions.add(Triple(1, position, width))
|
||||
tabPositions.value = currentPositions.sortedBy { it.first }
|
||||
} else {
|
||||
currentPositions[existingIndex] = Triple(1, position, width)
|
||||
tabPositions.value = currentPositions
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
// 热门标签 (游客模式) - 在游客模式下,热门标签对应第3页
|
||||
UnderlineTabItem(
|
||||
text = stringResource(R.string.index_hot),
|
||||
isSelected = pagerState.currentPage == 3,
|
||||
onClick = {
|
||||
tabDebouncer {
|
||||
scope.launch {
|
||||
pagerState.animateScrollToPage(3)
|
||||
) {
|
||||
UnderlineTabItem(
|
||||
text = stringResource(R.string.tab_short_video),
|
||||
isSelected = pagerState.currentPage == 1,
|
||||
onClick = {
|
||||
tabDebouncer {
|
||||
scope.launch {
|
||||
pagerState.animateScrollToPage(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// 动态标签
|
||||
Box(
|
||||
modifier = Modifier.onGloballyPositioned { coordinates ->
|
||||
val position = with(density) { coordinates.positionInParent().x.toDp().toPx() }
|
||||
val width = with(density) { coordinates.size.width.toDp().toPx() }
|
||||
val currentPositions = tabPositions.value.toMutableList()
|
||||
val existingIndex = currentPositions.indexOfFirst { it.first == 2 }
|
||||
if (existingIndex == -1) {
|
||||
currentPositions.add(Triple(2, position, width))
|
||||
tabPositions.value = currentPositions.sortedBy { it.first }
|
||||
} else {
|
||||
currentPositions[existingIndex] = Triple(2, position, width)
|
||||
tabPositions.value = currentPositions
|
||||
}
|
||||
}
|
||||
)
|
||||
) {
|
||||
UnderlineTabItem(
|
||||
text = stringResource(R.string.moment),
|
||||
isSelected = pagerState.currentPage == 2,
|
||||
onClick = {
|
||||
tabDebouncer {
|
||||
scope.launch {
|
||||
// 如果当前在动态标签右边的标签页,立即向左滚动显示推荐标签
|
||||
if (pagerState.currentPage > 2 && containerWidth.value > 0) {
|
||||
val recommendTab = tabPositions.value.find { it.first == 0 }
|
||||
if (recommendTab != null) {
|
||||
val (_, recommendPosition, _) = recommendTab
|
||||
val currentScroll = scrollState.value.toFloat()
|
||||
val scrollToPosition = recommendPosition.coerceAtLeast(0f)
|
||||
if (kotlin.math.abs(scrollToPosition - currentScroll) > 1f) {
|
||||
scrollState.animateScrollTo(scrollToPosition.toInt())
|
||||
}
|
||||
}
|
||||
}
|
||||
pagerState.animateScrollToPage(2)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// 只有非游客用户才显示"关注"tab
|
||||
if (!AppStore.isGuest) {
|
||||
Box(
|
||||
modifier = Modifier.onGloballyPositioned { coordinates ->
|
||||
val position = with(density) { coordinates.positionInParent().x.toDp().toPx() }
|
||||
val width = with(density) { coordinates.size.width.toDp().toPx() }
|
||||
val currentPositions = tabPositions.value.toMutableList()
|
||||
val existingIndex = currentPositions.indexOfFirst { it.first == 3 }
|
||||
if (existingIndex == -1) {
|
||||
currentPositions.add(Triple(3, position, width))
|
||||
tabPositions.value = currentPositions.sortedBy { it.first }
|
||||
} else {
|
||||
currentPositions[existingIndex] = Triple(3, position, width)
|
||||
tabPositions.value = currentPositions
|
||||
}
|
||||
}
|
||||
) {
|
||||
UnderlineTabItem(
|
||||
text = stringResource(R.string.index_following),
|
||||
isSelected = pagerState.currentPage == 3,
|
||||
onClick = {
|
||||
tabDebouncer {
|
||||
scope.launch {
|
||||
// 如果当前在关注标签左边的标签页,立即向右滚动显示新闻标签
|
||||
if (pagerState.currentPage < 3 && containerWidth.value > 0) {
|
||||
val newsTab = tabPositions.value.find { it.first == 5 }
|
||||
if (newsTab != null) {
|
||||
val (_, newsPosition, newsWidth) = newsTab
|
||||
val currentScroll = scrollState.value.toFloat()
|
||||
val scrollToPosition = (newsPosition + newsWidth - containerWidth.value).coerceAtLeast(0f)
|
||||
if (kotlin.math.abs(scrollToPosition - currentScroll) > 1f) {
|
||||
scrollState.animateScrollTo(scrollToPosition.toInt())
|
||||
}
|
||||
}
|
||||
}
|
||||
pagerState.animateScrollToPage(3)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// 热门标签
|
||||
Box(
|
||||
modifier = Modifier.onGloballyPositioned { coordinates ->
|
||||
val position = with(density) { coordinates.positionInParent().x.toDp().toPx() }
|
||||
val width = with(density) { coordinates.size.width.toDp().toPx() }
|
||||
val currentPositions = tabPositions.value.toMutableList()
|
||||
val existingIndex = currentPositions.indexOfFirst { it.first == 4 }
|
||||
if (existingIndex == -1) {
|
||||
currentPositions.add(Triple(4, position, width))
|
||||
tabPositions.value = currentPositions.sortedBy { it.first }
|
||||
} else {
|
||||
currentPositions[existingIndex] = Triple(4, position, width)
|
||||
tabPositions.value = currentPositions
|
||||
}
|
||||
}
|
||||
) {
|
||||
UnderlineTabItem(
|
||||
text = stringResource(R.string.index_hot),
|
||||
isSelected = pagerState.currentPage == 4,
|
||||
onClick = {
|
||||
tabDebouncer {
|
||||
scope.launch {
|
||||
pagerState.animateScrollToPage(4)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// 热门标签 (游客模式) - 在游客模式下,热门标签对应第3页
|
||||
Box(
|
||||
modifier = Modifier.onGloballyPositioned { coordinates ->
|
||||
val position = with(density) { coordinates.positionInParent().x.toDp().toPx() }
|
||||
val width = with(density) { coordinates.size.width.toDp().toPx() }
|
||||
val currentPositions = tabPositions.value.toMutableList()
|
||||
val existingIndex = currentPositions.indexOfFirst { it.first == 3 }
|
||||
if (existingIndex == -1) {
|
||||
currentPositions.add(Triple(3, position, width))
|
||||
tabPositions.value = currentPositions.sortedBy { it.first }
|
||||
} else {
|
||||
currentPositions[existingIndex] = Triple(3, position, width)
|
||||
tabPositions.value = currentPositions
|
||||
}
|
||||
}
|
||||
) {
|
||||
UnderlineTabItem(
|
||||
text = stringResource(R.string.index_hot),
|
||||
isSelected = pagerState.currentPage == 3,
|
||||
onClick = {
|
||||
tabDebouncer {
|
||||
scope.launch {
|
||||
pagerState.animateScrollToPage(3)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 新闻标签 - 在游客模式下对应第4页,非游客模式下对应第5页
|
||||
val newsPageIndex = if (AppStore.isGuest) 4 else 5
|
||||
Box(
|
||||
modifier = Modifier.onGloballyPositioned { coordinates ->
|
||||
val position = with(density) { coordinates.positionInParent().x.toDp().toPx() }
|
||||
val width = with(density) { coordinates.size.width.toDp().toPx() }
|
||||
val currentPositions = tabPositions.value.toMutableList()
|
||||
val existingIndex = currentPositions.indexOfFirst { it.first == newsPageIndex }
|
||||
if (existingIndex == -1) {
|
||||
currentPositions.add(Triple(newsPageIndex, position, width))
|
||||
tabPositions.value = currentPositions.sortedBy { it.first }
|
||||
} else {
|
||||
currentPositions[existingIndex] = Triple(newsPageIndex, position, width)
|
||||
tabPositions.value = currentPositions
|
||||
}
|
||||
}
|
||||
) {
|
||||
UnderlineTabItem(
|
||||
text = stringResource(R.string.tab_news),
|
||||
isSelected = pagerState.currentPage == newsPageIndex,
|
||||
onClick = {
|
||||
tabDebouncer {
|
||||
scope.launch {
|
||||
pagerState.animateScrollToPage(newsPageIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 新闻标签 - 在游客模式下对应第4页,非游客模式下对应第5页
|
||||
val newsPageIndex = if (AppStore.isGuest) 4 else 5
|
||||
UnderlineTabItem(
|
||||
text = stringResource(R.string.tab_news),
|
||||
isSelected = pagerState.currentPage == newsPageIndex,
|
||||
onClick = {
|
||||
tabDebouncer {
|
||||
scope.launch {
|
||||
pagerState.animateScrollToPage(newsPageIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// 搜索按钮
|
||||
@@ -224,6 +380,50 @@ fun MomentsList() {
|
||||
)
|
||||
}
|
||||
|
||||
// 监听页面变化,实现自动滚动
|
||||
LaunchedEffect(pagerState.currentPage, pagerState.currentPageOffsetFraction) {
|
||||
val currentPage = pagerState.currentPage
|
||||
val offsetFraction = pagerState.currentPageOffsetFraction
|
||||
val prevPage = previousPage.value
|
||||
|
||||
// 只在页面切换接近完成时执行(避免滑动过程中频繁触发)
|
||||
val absOffset = kotlin.math.abs(offsetFraction)
|
||||
if (absOffset < 0.1f && containerWidth.value > 0) {
|
||||
// 情况1:从关注标签页左边的标签页(0,1,2)滑动到关注标签页(3)时,向右滚动显示新闻标签(5)
|
||||
if (currentPage == 3 && prevPage < 3 && !AppStore.isGuest) {
|
||||
val newsTab = tabPositions.value.find { it.first == 5 }
|
||||
if (newsTab != null) {
|
||||
val (_, newsPosition, newsWidth) = newsTab
|
||||
val currentScroll = scrollState.value.toFloat()
|
||||
// 计算滚动位置,使新闻标签可见(确保新闻标签的结束位置在可见区域内)
|
||||
val scrollToPosition = (newsPosition + newsWidth - containerWidth.value).coerceAtLeast(0f)
|
||||
if (kotlin.math.abs(scrollToPosition - currentScroll) > 1f) {
|
||||
scrollState.animateScrollTo(scrollToPosition.toInt())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 情况2:从动态标签页右边的标签页(3,4,5)滑动到动态标签页(2)时,向左滚动显示推荐标签(0)
|
||||
if (currentPage == 2 && prevPage > 2) {
|
||||
val recommendTab = tabPositions.value.find { it.first == 0 }
|
||||
if (recommendTab != null) {
|
||||
val (_, recommendPosition, _) = recommendTab
|
||||
val currentScroll = scrollState.value.toFloat()
|
||||
// 计算滚动位置,使推荐标签可见(确保推荐标签的起始位置在可见区域内)
|
||||
val scrollToPosition = recommendPosition.coerceAtLeast(0f)
|
||||
if (kotlin.math.abs(scrollToPosition - currentScroll) > 1f) {
|
||||
scrollState.animateScrollTo(scrollToPosition.toInt())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新上一次的页面索引
|
||||
if (currentPage != prevPage) {
|
||||
previousPage.value = currentPage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HorizontalPager(
|
||||
state = pagerState,
|
||||
modifier = Modifier
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
<string name="posts">帖子</string>
|
||||
<string name="favourites_upper">收藏</string>
|
||||
<string name="notifications_upper">消息</string>
|
||||
<string name="following_upper">关注11</string>
|
||||
<string name="following_upper">关注</string>
|
||||
<string name="unfollow_upper">取消关注</string>
|
||||
<string name="comment_count">%d条评论</string>
|
||||
<string name="post_comment_hint">快来互动吧...</string>
|
||||
<string name="follow_upper">关注3</string>
|
||||
<string name="follow_upper">关注</string>
|
||||
<string name="follow_upper_had">已关注</string>
|
||||
<string name="login_upper">登录</string>
|
||||
<string name="lets_ride_upper">确认</string>
|
||||
|
||||
Reference in New Issue
Block a user