I have a Box layout in which I have a canvas composable that draws a circle animation. I also have another Test Composable in which I would be drawing a few other shapes(Denoted in Grey color in the video attachment).
The problem I'm facing is every time the animation value animates the circle, the draw scope lambda inside the test composable function is also triggered continuously which I do not want. I put a print line to confirm it.
I'm unable to understand why the draw scope lambda of another composable is triggered. Any help in understanding this behavior and how not to make it trigger continuously is deeply appreciated.
Box(
modifier = Modifier.
.fillMaxWidth(1f)
.wrapContentHeight()
) {
BlinkingCircle()
val modifier = Modifier.align(Alignment.Center)
Test(modifier)
}
#Composable
fun BlinkingCircle() {
val screenWidthInDp = LocalConfiguration.current.screenWidthDp
val infiniteScale = rememberInfiniteTransition()
val animatableSize = infiniteScale.animateFloat(
initialValue = 40f,
targetValue = 6f,
animationSpec = infiniteRepeatable(
animation = tween(
durationMillis = 1500,
easing = FastOutLinearInEasing
),
repeatMode = RepeatMode.Reverse
)
)
val xStart = 57f
val yStart = 126f
Canvas(modifier = Modifier
.width(screenWidthInDp.dp)
.fillMaxHeight(1f),
onDraw = {
drawScope.drawCircle(
color = Color.White,
radius = animatableSize.value,
center = Offset(
x = xStart,
y = yStart)
)
}
)
}
#Composable
fun Test(modifier: Modifier) {
Box(
modifier = modifier
.background(Color.LightGray)
.width(150.dp)
.height(100.dp)
.offset(300.dp, 50.dp)
) {
Canvas(modifier = Modifier
.width(50.dp)
.height(50.dp),
onDraw = {
println("Testing Box Text - 2")
//drawCircle(Color.Green, radius = 30f)
})
}
}
2022-08-10 01:43:56.897 15718-15718/com.xxxx.dev I/System.out: Testing Box Text - 2
2022-08-10 01:43:56.911 15718-15718/com.xxxx.dev I/System.out: Testing Box Text - 2
2022-08-10 01:43:56.927 15718-15718/com.xxxx.dev I/System.out: Testing Box Text - 2
2022-08-10 01:43:56.946 15718-15718/com.xxxx.dev I/System.out: Testing Box Text - 2
2022-08-10 01:43:56.962 15718-15718/com.xxxx.dev I/System.out: Testing Box Text - 2
2022-08-10 01:43:56.979 15718-15718/com.xxxx.dev I/System.out: Testing Box Text - 2
....
....
Video sample
Related
I'm new to Jetpack Compose, and I'm trying to rotate the home screen with animation when the menu button is tapped. It works fine 3-5 times, but suddenly it lags like crazy and I don't know why? Is this a bug, or am I doing something wrong?
var isOpen by remember { mutableStateOf(false) }
val transition = updateTransition(targetState = isOpen, "Menu")
val rotation by transition.animateFloat(
transitionSpec = { spring(0.4f, Spring.StiffnessLow) },
label = "MenuRotation",
targetValueByState = { if (it) -30f else 0f }
)
val scale by transition.animateFloat(
label = "MenuScale",
targetValueByState = { if (it) 0.9f else 1f }
)
val translateX by transition.animateFloat(
transitionSpec = { tween(400) },
label = "MenuTranslation",
targetValueByState = { if (it) 536f else 0f }
)
Box(
modifier = Modifier
.fillMaxSize()
.graphicsLayer {
cameraDistance = density * 10f
rotationY = rotation
scaleX = scale
translationX = translateX
}
) {
HomeScreen()
}
Box {
DefaultButton(
onClick = { isOpen = ! isOpen },
modifier = Modifier
.padding(16.dp)
.padding(top = 32.dp)
.shadow(blur = 8.dp, radius = 16.dp)
.size(32.dp, 32.dp),
shape = Shapes.large,
) {
Icon(
imageVector = if (isOpen) Icons.Filled.Close else Icons.Filled.Menu,
contentDescription = "Menu",
tint = Color.Black,
)
}
}
Update #1
I found this in the logcat
Skipped 52 frames! The application may be doing too much work on its main thread.
Davey! duration=1067ms; Flags=0, FrameTimelineVsyncId=2511155, IntendedVsync=4294436921331, Vsync=4294870254647, InputEventId=0, HandleInputStart=4294871069349, AnimationStart=4294871070287, PerformTraversalsStart=4294871939089, DrawStart=4294872039558, FrameDeadline=4294486921330, FrameInterval=4294870971954, FrameStartTime=41666666, SyncQueued=4294872645860, SyncStart=4295312217578, IssueDrawCommandsStart=4295312304089, SwapBuffers=4295937520703, FrameCompleted=4295944298047, DequeueBufferDuration=5729, QueueBufferDuration=166719, GpuCompleted=4295944298047, SwapBuffersCompleted=4295937862943, DisplayPresentTime=4237530536663, CommandSubmissionCompleted=4295937520703,
Update #2
The animation works flawlessly when I comment out all text components and vice versa. So what's wrong with the text components?
Please check the HomeScreen composable, component recomposition counts。
I suspect that HomeScreen reorganizes too many times。
You can just replace #Composable HomeScreen with an #Composeable Image verify。
Iam designing a Horizontal Pager in Android Jetpack Compose using googles accompanist Pager
(implementation "com.google.accompanist:accompanist-pager:0.24.2-alpha").
I Implement this Horizontal Pager Correctly. But I want to change the default Animation of pager from horizontal left/ right swipe to fadein/ fadeout animation. Iam new to compose and i didn't find any useful resources in the internet. If anyone knows this help me to find the correct solutions. Thanks in Advance.
val pagerState = rememberPagerState()
HorizontalPager(count = 3, state = pagerState
) { pagePosition ->
PagerScreen(onBoardingData = onBoardingDataList[pagePosition])
}
Here PagerScreen is a composable function for each screen.
Like this Gif I want fadein / fadeout for horizontal Pager
I'm not sure if there's a better solution using the HorizontalPager, so I did this solution using swipeable modifier.
#ExperimentalMaterialApi
#Composable
fun SwipeableSampleScreen5() {
val pages = listOf(
596 to Color(203, 41, 61),
801 to Color(107, 0, 141),
492 to Color(40, 38, 149),
718 to Color(56, 144, 244),
550 to Color(60, 149, 159)
)
val swipeableState = rememberSwipeableState(0)
// This Box is only to get the screen's width
BoxWithConstraints(Modifier.fillMaxWidth()) {
val widthPx = with(LocalDensity.current) {
maxWidth.toPx()
}
// Each anchor of the swipeable is the index * screen width
val anchors = remember(pages) {
List(pages.size) { index ->
-(index * widthPx) to index
}.toMap()
}
// This Box is capturing the swipe gesture
Box(
modifier = Modifier
.fillMaxSize()
.swipeable(
state = swipeableState,
anchors = anchors,
thresholds = { _, _ -> FractionalThreshold(0.5f) },
orientation = Orientation.Horizontal,
)
) {
// swipeableState provides the current index and the next index
val currentIndex = swipeableState.currentValue
val nextIndex = swipeableState.progress.to
// This Box is behind and will display the nextValue
Box(
Modifier
// graphicsLayer is applied to the content
.graphicsLayer {
alpha = 1f
},
) {
Box(
Modifier
.fillMaxSize()
.background(pages[nextIndex].second),
contentAlignment = Alignment.Center
) {
Text(
text = pages[nextIndex].first.toString(),
style = MaterialTheme.typography.h1.copy(color = Color.White),
)
}
}
// This Box is showing the current value which will change
// the alpha during the swiping
Box(
Modifier
.graphicsLayer {
alpha = 1f - swipeableState.progress.fraction
},
) {
Box(
Modifier
.fillMaxSize()
.background(pages[currentIndex].second),
contentAlignment = Alignment.Center
) {
Text(
text = pages[currentIndex].first.toString(),
style = MaterialTheme.typography.h1.copy(color = Color.White),
)
}
}
}
}
}
The solution is commented, but for a better understanding this article helped me a lot.
Here's the result:
You can find the full source here.
Can't resolve the problem with scrolling a Custom View in Compose.
I have a Canvas nested in a Column with fillMaxSize() modifier.
In Canvas I also use fillMaxSize() modifier (or size() with fixed value, doesn't matter)
I draw a vertical line that can be higher than screen size. And want, if this vertical line is higher, make canvas scrollable, but I can't. Any methods like verticalScroll(), scrollable() has no effects. I'm trying to make height of Canvas higher and use scrollable modifier, but it does no effect also (when I check "size" variable of DrawScope, it's always 1630 on Pixel 4).
Please help.
My code (Simplified and this method will be nested in Column):
#Composable
fun DrawTimeLine(list: List<Unit>){
Canvas(modifier = Modifier
.fillMaxSize()
.scrollable(rememberScrollState(), Orientation.Vertical)
.padding(
top = Dimens.DEFAULT_MARGIN,
bottom = Dimens.BOTTOM_BAR_SIZE,
start = Dimens.DEFAULT_MARGIN
)){
var offsetY = 0f
val offsetX = 0f
val increaseValue = 150f
fun drawTimePoint() {
drawCircle(
color = Color.White,
radius = 15.dp.value,
center = Offset(offsetX, offsetY)
)
}
fun drawTimeLine() {
drawLine(
color = Color.White,
start = Offset(offsetX, offsetY),
end = Offset(offsetX, offsetY + increaseValue),
strokeWidth = 5.dp.value
)
offsetY += increaseValue
}
list.forEach {
drawTimePoint()
drawTimeLine()
}
}
}
I am trying to achieve translate animation in Jetpack compose but i am not able to find Suitable Source for this.Can any one Help me to achieve translate Animation in jetpack compose in which i can set start and edning positionl Manually..
The alternative of translate animation in jetpack compose is OFFSET ANIMATION
yes, I was able to achieve this through offset animation.I am sharing the code below with comments in detail so it will be easier for the reader to understand it
// Courtine Scope to Run the animation in thread
val coroutineScope = rememberCoroutineScope()
val offsetX = remember { Animatable(0f) }
val offsetY = remember { Animatable(0f) }
Image(
painter = rememberDrawablePainter(
ContextCompat.getDrawable(
context,
R.drawable.image
)
),
contentDescription = "s", contentScale = ContentScale.Crop,
modifier = Modifier
.offset {
IntOffset(
offsetX.value.toInt(),
offsetY.value.toInt()
)
}
.width(300.dp)
.height(300.dp)
)
//Finally run the animation on the Click of your button or whenever you wants to start it...
coroutineScope.launch {
launch {
offsetXFirst.animateTo(
targetValue = targetValue,
animationSpec = tween(
durationMillis = 2000,
delayMillis = 0))}
launch {
offsetYFirst.animateTo(
targetValue = size.height.toFloat(),
animationSpec = tween(
durationMillis = 2000,
delayMillis = 0))}
}
Using the offset was my solution as well. However used animateDpAsState instead. There a tab indicator is moved on the x axis:
val offsetState = animateDpAsState(targetValue = targetPositionDp)
Box(modifier = Modifier
.offset(offsetState.value, 0.dp)
.background(color = Color.Red)
.size(tabWidth, tabHeight))
I am trying to convert my View based code to Compose. I have a composable which takes an image (Painter) as argument and displays it using Image composable. What I want is that whenever the argument value changes, my Image should do a 360 degree rotation and the image should change while angle is approx. 180 degree (i.e. mid-way in the animation)
This is the composable I made.
#Composable
fun MyImage(displayImage: Painter) {
Image(
painter = displayImage,
contentDescription = null,
modifier = Modifier
.size(36.dp)
.clip(CircleShape)
)
}
Right now when the displayImage changes, the new image is displayed immediately without any animation (obviously). How can I achieve the desired animation?
The code that I am trying to convert looks like this:
fun onImageChange(imageRes: Int) {
ObjectAnimator.ofFloat(imageView, View.ROTATION, 0f, 360f)
.apply {
addUpdateListener {
if (animatedFraction == 0.5f) {
imageView.setImageResource(imageRes)
}
}
start()
}
}
It can be done using Animatable.
Compose animations are based on coroutines, so you can wait for the animateTo suspend function to complete, change the image and run another animation. Here's a basic example:
var flag by remember { mutableStateOf(true) }
val resourceId = remember(flag) { if (flag) R.drawable.profile else R.drawable.profile_inverted }
val rotation = remember { Animatable(0f) }
val scope = rememberCoroutineScope()
Column(Modifier.padding(30.dp)) {
Button(onClick = {
scope.launch {
rotation.animateTo(
targetValue = 180f,
animationSpec = tween(1000, easing = LinearEasing)
)
flag = !flag
rotation.animateTo(
targetValue = 360f,
animationSpec = tween(1000, easing = LinearEasing)
)
rotation.snapTo(0f)
}
}) {
Text("Rotate")
}
Image(
painterResource(id = resourceId),
contentDescription = null,
modifier = Modifier
.size(300.dp)
.rotate(rotation.value)
)
}
Output:
If you want to animate the changing images, you have to put two images in a Box and animate the opacity of both as they rotate using one more Animatable.