前言
如需以左右或上下方式浏览内容,您可以分别使用 HorizontalPager 和 VerticalPager可组合项。
这些可组合项的功能与视图系统中的 ViewPager类似。
官方文档:
https://android-dot-google-developers.gonglchuangl.net/jetpack/compose/layouts/pager?hl=zh-cn
基本示例
1 2 3 4 5 6 7 8 9 10 11
| val pagerState = rememberPagerState(pageCount = { 10 }) HorizontalPager(state = pagerState) { page -> Text( text = "Page: $page", modifier = Modifier.fillMaxWidth() ) }
|
默认加载页数
1 2 3 4 5 6 7 8
| HorizontalPager( state = pagerState, modifier = Modifier.fillMaxSize(), verticalAlignment = Alignment.Top, beyondViewportPageCount = 2 ) { page -> }
|
默认分页器只会加载一页,这样页面切换的时候就会不流畅,这里设置beyondViewportPageCount为2,效果就好很多。
参数解释
beyondViewportPageCount = 0, // 预加载页面数量:0=仅当前页,1=当前页+1个相邻页
页面切换添加效果
该示例实现了切换时页面从半透明到不透明的效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| import android.util.Log import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.PagerState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.graphicsLayer import com.xhkjedu.xh_control_appstore.vm.AppViewModel import kotlin.math.absoluteValue
@Composable fun MPager(appViewModel: AppViewModel, pagerState: PagerState, callback: (page: Int) -> Unit) { HorizontalPager( state = pagerState, modifier = Modifier.fillMaxSize(), verticalAlignment = Alignment.Top, beyondBoundsPageCount = 10, ) { page -> Box( Modifier .fillMaxSize() .graphicsLayer { val pageOffset = ((pagerState.currentPage - page) + pagerState.currentPageOffsetFraction).absoluteValue alpha = 1f - pageOffset.coerceIn(0f, 1f) } ) { if (page == 0) { AppListView(appViewModel.appList) } else { AppListView(appViewModel.appList2) } } }
LaunchedEffect(pagerState) { snapshotFlow { pagerState.currentPage }.collect { page -> Log.i("Page change", "Page changed to $page") callback(page) } } }
|
注意
添加graphicsLayer设置会让页面滑动时剪裁掉内部超出的部分,如果不想被剪裁就别设置这个。
滚动到某页
无动画滚动
1 2 3 4 5 6 7 8
| val coroutineScope = rememberCoroutineScope() Button(onClick = { coroutineScope.launch { pagerState.scrollToPage(5) } }, modifier = Modifier.align(Alignment.BottomCenter)) { Text("Jump to Page 5") }
|
注意
这里建议使用无动画滚动,有动画的页面相差比较多的时候跳转有卡顿现象。
带动画滚动
1 2 3 4 5 6 7 8
| val coroutineScope = rememberCoroutineScope() Button(onClick = { coroutineScope.launch { pagerState.animateScrollToPage(5) } }, modifier = Modifier.align(Alignment.BottomCenter)) { Text("Jump to Page 5") }
|
页面切换监听
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| val pagerState = rememberPagerState(pageCount = { 10 })
LaunchedEffect(pagerState) { snapshotFlow { pagerState.currentPage }.collect { page -> Log.d("Page change", "Page changed to $page") } }
VerticalPager( state = pagerState, ) { page -> Text(text = "Page: $page") }
|
页面指示器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, modifier = Modifier.fillMaxSize() ) { page -> Text( text = "Page: $page", ) } Row( Modifier .wrapContentHeight() .fillMaxWidth() .align(Alignment.BottomCenter) .padding(bottom = 8.dp), horizontalArrangement = Arrangement.Center ) { repeat(pagerState.pageCount) { iteration -> val color = if (pagerState.currentPage == iteration) Color.DarkGray else Color.LightGray Box( modifier = Modifier .padding(2.dp) .clip(CircleShape) .background(color) .size(16.dp) ) } }
|
指示器组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp
@Composable fun BoxScope.ZPageIndicatorComp(totalPageNum: Int, currPageNum: Int) { Box( Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { Row( ) { for (i in 0 until totalPageNum) { Box( Modifier .padding(start = 4.dp, end = 4.dp) .size(4.dp) .clip(CircleShape) .background(if (i == currPageNum) Color.White else Color.White.copy(0.5f)) ) } } } }
|
项目实战
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| import android.util.Log import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.PagerState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer import kotlin.math.absoluteValue
@Composable fun MainPager(pagerState: PagerState, callback: (page: Int) -> Unit) { HorizontalPager( state = pagerState, modifier = Modifier.fillMaxSize(), verticalAlignment = Alignment.Top, ) { page -> Log.i("page", page.toString()) Box( Modifier .fillMaxSize() .graphicsLayer { val pageOffset = ((pagerState.currentPage - page) + pagerState.currentPageOffsetFraction).absoluteValue alpha = 1f - pageOffset.coerceIn(0f, 1f) } ) { if (page == 0) { Box( modifier = Modifier .fillMaxSize() .background(Color.Red) ) { } } else { Box( modifier = Modifier .fillMaxSize() .background(Color.Green) ) { } } } }
LaunchedEffect(pagerState) { snapshotFlow { pagerState.currentPage }.collect { page -> Log.i("Page change", "Page changed to $page") callback(page) } } }
|
注意
默认情况下,分页器只会加载屏幕上显示的页面。如需在屏幕外加载更多页面,请将 beyondBoundsPageCount 设置为大于零的值。
使用
1 2 3 4 5 6 7 8 9 10
| val pagerState = rememberPagerState( initialPage = 0, initialPageOffsetFraction = 0f, ) { 2 } MainPager(pagerState) {
}
|
点击切换页面
1 2 3 4 5
| val coroutineScope = rememberCoroutineScope()
coroutineScope.launch { pagerState.animateScrollToPage(index) }
|