Android Jetpack Compose實(shí)現(xiàn)輪播圖效果
在最近思索如何使用Compose方式改進(jìn)我的開(kāi)源TMDB電影列表應(yīng)用程序的主屏幕時(shí),一個(gè)激動(dòng)人心的概念浮現(xiàn)在我的腦海中——為什么不整合一個(gè)吸引人的輪播圖來(lái)展示即將上映的電影呢?在本文中,我將分享我的開(kāi)發(fā)和實(shí)現(xiàn)自定義輪播圖的經(jīng)歷,提供涉及不同步驟的見(jiàn)解。
首先,我搜索了現(xiàn)有的解決方案,考慮到Material Design 3文檔中有一個(gè)輪播圖組件的可用性。然而,我很快發(fā)現(xiàn)它尚未發(fā)布用于Jetpack Compose,而且僅在最新的MDC-Android(視圖系統(tǒng))Alpha版本中可訪問(wèn)。這迫使我決定開(kāi)發(fā)自己的輪播圖,利用HorizontalPager
,這是一個(gè)Compose組件,允許水平滾動(dòng)和頁(yè)面變換。
為了有效地解決開(kāi)發(fā)過(guò)程,我將問(wèn)題分為四個(gè)不同的部分:
- 將
HorizontalPager
轉(zhuǎn)換為實(shí)現(xiàn)所需的旋轉(zhuǎn)木馬效果。 - 加入指示器顯示當(dāng)前可見(jiàn)頁(yè)面。
- 為
HorizontalPager
頁(yè)面實(shí)現(xiàn)自動(dòng)滾動(dòng)。 - 利用動(dòng)畫(huà)增強(qiáng)即將出現(xiàn)的頁(yè)面的外觀,同時(shí)聚焦當(dāng)前顯示的頁(yè)面并淡化其他頁(yè)面。
讓我們逐個(gè)詳細(xì)討論這些部分。
將HorizontalPager轉(zhuǎn)換為實(shí)現(xiàn)所需的旋轉(zhuǎn)木馬效果
為了創(chuàng)建所需的旋轉(zhuǎn)木馬效果,我們必須首先自定義HorizontalPager
以部分顯示前一個(gè)和后一個(gè)頁(yè)面。
為此,HorizontalPager
提供了兩個(gè)參數(shù):
-
contentPadding
:允許我們從每個(gè)軸應(yīng)用邊距,創(chuàng)建所需的頁(yè)面周圍間距。 -
pageSpacing
:在兩個(gè)相鄰頁(yè)面之間引入間隙,確保視覺(jué)上吸引人的布局。
HorizontalPager(
contentPadding = PaddingValues(horizontal = 32.dp),
pageSpacing = 16.dp
) { page ->
...
CarouselItem(item)
}
將指示器合并到頁(yè)面中以顯示當(dāng)前可見(jiàn)頁(yè)面
這個(gè)任務(wù)很容易完成。我只需要將HorizontalPager
置于Box布局中,并將DotIndicators
合成到Box底部對(duì)齊即可。
Box {
HorizontalPager(pageCount = pageCount, state = pagerState) { page ->
CarouselItem(itemList[page])
}
DotIndicators(
pageCount = pageCount,
pagerState = pagerState,
modifier = Modifier.align(Alignment.BottomCenter)
)
}
我們創(chuàng)建了一個(gè)名為DotIndicators
的可組合組件。它需要兩個(gè)參數(shù):頁(yè)數(shù)(pageCount
)和頁(yè)面狀態(tài)(pageState
)。我們使用頁(yè)數(shù)(pageCount
)添加相同數(shù)量的指示器,而使用頁(yè)面狀態(tài)(pageState
)使選定的點(diǎn)顯示更暗。內(nèi)部實(shí)現(xiàn)如下:
@Composable
fun DotIndicators(
...,
modifier: Modifier
) {
Row(modifier = modifier) {
repeat(pageCount) { iteration ->
val color = if (pagerState.currentPage == iteration) selectedColor else unselectedColor
Box(
modifier = Modifier
.clip(CircleShape)
.background(color)
)
}
}
}
上面指示器效果如下:
為HorizontalPager頁(yè)面實(shí)現(xiàn)自動(dòng)滾動(dòng)
目前,我們的設(shè)置要求用戶手動(dòng)水平滑動(dòng)以導(dǎo)航到下一頁(yè)。為了增強(qiáng)用戶體驗(yàn),我們可以實(shí)現(xiàn)自動(dòng)滾動(dòng)。我們通過(guò)利用HorizontalPager提供的Coroutine、LaunchedEffect和PagerState來(lái)實(shí)現(xiàn)這一點(diǎn)。
以下是一個(gè)完全具備功能的示例實(shí)現(xiàn):
@Composable
fun AwesomeCarousel(
pageCount: Int = 10,
pagerState: PagerState = rememberPagerState(),
autoScrollDuration: Long = 3000L
) {
val isDragged by pagerState.interactionSource.collectIsDraggedAsState()
if (isDragged.not()) {
with(pagerState) {
var currentPageKey by remember { mutableStateOf(0) }
LaunchedEffect(key1 = currentPageKey) {
launch {
delay(timeMillis = autoScrollDuration)
val nextPage = (currentPage + 1).mod(pageCount)
animateScrollToPage(page = nextPage)
currentPageKey = nextPage
}
}
}
}
HorizontalPager(pageCount = pageCount, state = pagerState) { page ->
CarouselItem(itemList[page])
}
}
代碼片段中的主要要點(diǎn)是:
-
LaunchedEffect
:LaunchedEffect
是 Jetpack Compose 提供的一種特殊效果,允許我們?cè)趨f(xié)程中執(zhí)行副作用,當(dāng)特定鍵值發(fā)生改變時(shí)觸發(fā)。在這種情況下,key1
參數(shù)設(shè)置為currentPageKey
,這意味著只要currentPageKey
的值發(fā)生更改,該效果就會(huì)被觸發(fā)。在LaunchedEffect
塊內(nèi),我們使用launch
函數(shù)啟動(dòng)一個(gè)新的協(xié)程。這個(gè)協(xié)程在合成過(guò)程中異步執(zhí)行并且獨(dú)立于合成進(jìn)程。 -
delay(timeMillis = autoScrollDuration)
:這一行引入了一個(gè)延遲,協(xié)程會(huì)在 autoScrollDuration 變量指定的持續(xù)時(shí)間內(nèi)暫停,然后繼續(xù)執(zhí)行下一行代碼。在暫停后,我們?cè)俅胃逆I,這導(dǎo)致協(xié)程重新啟動(dòng),因此這意味著LaunchedEffect
內(nèi)部的塊將無(wú)限次地執(zhí)行。 -
isDragged.not()
:此代碼段確保僅在不拖動(dòng)頁(yè)面時(shí)執(zhí)行后續(xù)操作。它控制自動(dòng)滾動(dòng)行為,避免了用戶交互和自動(dòng)滾動(dòng)過(guò)程之間的沖突。例如,如果用戶正在滑動(dòng)頁(yè)面,自動(dòng)滾動(dòng)將停止。
利用動(dòng)畫(huà)增強(qiáng)頁(yè)面外觀
實(shí)現(xiàn)的最后一部分著重于增強(qiáng)輪播頁(yè)面的視覺(jué)過(guò)渡效果。通過(guò)利用graphicLayer修飾器,我們可以實(shí)現(xiàn)平滑無(wú)縫的過(guò)渡效果。關(guān)鍵動(dòng)畫(huà)包括在淡出舊項(xiàng)的同時(shí)淡入新項(xiàng),以及在項(xiàng)接近或遠(yuǎn)離中心時(shí)進(jìn)行縮放。
以下代碼計(jì)算當(dāng)前頁(yè)面和目標(biāo)頁(yè)面之間的偏移量,然后使用此偏移量在0.7和1之間插值出一個(gè)轉(zhuǎn)換值。然后將此轉(zhuǎn)換值應(yīng)用于graphicLayer
的alpha
和scaleY
屬性。
fun Modifier.carouselTransition(page: Int, pagerState: PagerState) =
graphicsLayer {
val pageOffset =
((pagerState.currentPage - page) + pagerState.currentPageOffsetFraction).absoluteValue
val transformation =
lerp(
start = 0.7f,
stop = 1f,
fraction = 1f - pageOffset.coerceIn(0f, 1f)
)
alpha = transformation
scaleY = transformation
}
- 我們計(jì)算了在頁(yè)面分頁(yè)器中當(dāng)前頁(yè)面和目標(biāo)頁(yè)面之間的偏移量。它考慮了整個(gè)頁(yè)面差異(
pagerState.currentPage-page
)和分?jǐn)?shù)(pagerState.currentPageOffsetFraction
),后者表示在頁(yè)面之間轉(zhuǎn)換的進(jìn)度。絕對(duì)值確保偏移量始終為正數(shù)。 -
lerp
函數(shù)用于基于分?jǐn)?shù)在起始點(diǎn)和結(jié)束點(diǎn)之間插值計(jì)算一個(gè)值。在這種情況下,起始值為0.7f,停止值為1f,而分?jǐn)?shù)則被計(jì)算為1f -pageOffset.coerceIn(0f, 1f)
。coerceIn函數(shù)可以確保分?jǐn)?shù)保持在0到1的范圍內(nèi)。 -
alpha = transformation
將transformation
的值分配給了graphicLayer
的alpha
屬性。將alpha設(shè)置為介于0和1之間的值可以確定圖層的透明度。值為1表示圖層完全不透明,而值為0表示它完全透明。 -
scaleY = transformation
同樣,這行將transformation
的值分配給了graphicLayer
的scaleY
屬性。調(diào)整scaleY
屬性會(huì)垂直縮放圖層。值為1表示圖層的原始大小,而小于1的值會(huì)減少垂直比例,大于1的值會(huì)增加垂直比例。
我們?cè)?code>HorizontalPager的item modifier
上利用graphicLayer
擴(kuò)展函數(shù)。這使我們可以對(duì)每個(gè)旋轉(zhuǎn)木馬項(xiàng)目應(yīng)用變換并創(chuàng)建效果。
HorizontalPager() { page ->
val page = list[page]
CarouselItem(
item = page,
modifier = Modifier.carouselTransition(page, pagerState)
)
}
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-475314.html
github地址
https://github.com/TheSomeshKumar/Flixplorer文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-475314.html
到了這里,關(guān)于Android Jetpack Compose實(shí)現(xiàn)輪播圖效果的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!