Skip to content

Instantly share code, notes, and snippets.

@f2janyway
Created November 17, 2023 14:22
Show Gist options
  • Select an option

  • Save f2janyway/3c6532e8e96278a7fb28638bc4242e42 to your computer and use it in GitHub Desktop.

Select an option

Save f2janyway/3c6532e8e96278a7fb28638bc4242e42 to your computer and use it in GitHub Desktop.
fixed background of page of horizontalPager of Jetpack Compose example
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AccountBox
import androidx.compose.material.icons.filled.AddCircle
import androidx.compose.material.icons.filled.Build
import androidx.compose.material.icons.filled.Call
import androidx.compose.material.icons.filled.List
import androidx.compose.material3.Icon
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Outline
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.LayoutCoordinates
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import dagger.hilt.android.AndroidEntryPoint
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun FixedBGHorizontalPager() {
val list = listOf("java", "kotlin", "python", "rust", "javascript")
val listOfColor = listOf(Color.Gray, Color.Yellow, Color.LightGray, Color.Magenta, Color.Green)
val listOfIcons = listOf(
Icons.Default.List,
Icons.Default.AccountBox,
Icons.Default.AddCircle,
Icons.Default.Build,
Icons.Default.Call
)
val pagerState = rememberPagerState {
list.count()
}
val modifier = Modifier
Column {
HorizontalPager(
state = pagerState, modifier = modifier
.height(200.dp)
.fillMaxWidth()
) { page ->
Box(
modifier = modifier
.fillMaxSize()
.background(listOfColor[page]),
contentAlignment = Alignment.Center
) {
Icon(
imageVector = listOfIcons[page],
contentDescription = null,
modifier = modifier
.fillMaxSize()
.graphicsLayer {
val pageOffset = pagerState.offsetForPage(page)
translationX = size.width * pageOffset
shape = ProportionalClipShape(shownSpace = pagerState.shownSpace(page))
clip = true
},
tint = Color.White
)
Text(text = list[page], color = Color.Black, fontSize = 30.sp)
}
}
list.forEachIndexed { page, i ->
Box(
modifier = modifier
.fillMaxWidth()
.padding(bottom = 6.dp)
) {
Text(
text = "page: $page :" +
"offsetForPage:${
pagerState.offsetForPage(page).toRangeStr()
},\n" +
"starrOffsetForPage:${
pagerState.startOffsetForPage(page).toRangeStr()
}," +
"endOffsetForPage:${
pagerState.endOffsetForPage(page).toRangeStr()
},\n" +
"showSpace:${
pagerState.shownSpace(page).toRangeStr()
}"
)
}
}
}
}
class ProportionalClipShape(val shownSpace: Float) : Shape {
override fun createOutline(
size: Size,
layoutDirection: LayoutDirection,
density: Density
): Outline {
val clipSize = when {
shownSpace < 0 -> size.copy(width = size.width * -shownSpace)
shownSpace > 0 && shownSpace < 1 -> size.copy(width = size.width * shownSpace)
else -> size
}
val offset = when {
shownSpace < 0 -> Offset(0f, 0f)
shownSpace > 0 && shownSpace < 1 -> Offset(size.width * (1 - shownSpace), 0f)
else -> Offset.Zero
}
return Outline.Rectangle(rect = Rect(offset,clipSize))
}
}
fun Float.toRangeStr(count: Int = 5): String {
if ("$this".length > count) {
return "$this".substring(0, count)
} else {
return "$this"
}
}
//https://www.sinasamaki.com/pager-animations/
// ACTUAL OFFSET
@OptIn(ExperimentalFoundationApi::class)
fun PagerState.offsetForPage(page: Int) = (currentPage - page) + currentPageOffsetFraction
// OFFSET ONLY FROM THE LEFT
@OptIn(ExperimentalFoundationApi::class)
fun PagerState.startOffsetForPage(page: Int): Float {
return offsetForPage(page).coerceAtLeast(0f)
}
// OFFSET ONLY FROM THE RIGHT
@OptIn(ExperimentalFoundationApi::class)
fun PagerState.endOffsetForPage(page: Int): Float {
return offsetForPage(page).coerceAtMost(0f)
}
/**
*
* This function considered only fillMaxWidth of HorizontalPager
* 왼쪽이 보이면 음수 :Left (value < 0 && value > -1) or (value < 0 && abs(value) < 1)
* 오른쪽에 보이면 양수 :Right (value > 0 && value < 1)
* 안보이면 0 :Invisiable
* 전체 다보이면 1 :Settle (centered)
* */
@OptIn(ExperimentalFoundationApi::class)
fun PagerState.shownSpace(page: Int): Float {
val offsetForPage = offsetForPage(page)
return when {
0f == offsetForPage -> 1f
1 > kotlin.math.abs(offsetForPage) && offsetForPage > 0 -> -(1 - offsetForPage)
1 > kotlin.math.abs(offsetForPage) && offsetForPage < 0 -> (1 + offsetForPage)
else -> 0f
}
}
@Preview
@Composable
private fun Pre() {
FixedBGHorizontalPager()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment