1 /*
<lambda>null2 * Copyright 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 @file:OptIn(ExperimentalMotionApi::class)
18
19 package androidx.constraintlayout.compose.integration.macrobenchmark.target.graphs
20
21 import androidx.compose.animation.core.Animatable
22 import androidx.compose.animation.core.tween
23 import androidx.compose.foundation.background
24 import androidx.compose.foundation.clickable
25 import androidx.compose.foundation.layout.Box
26 import androidx.compose.foundation.layout.fillMaxSize
27 import androidx.compose.foundation.layout.height
28 import androidx.compose.foundation.layout.padding
29 import androidx.compose.foundation.lazy.LazyColumn
30 import androidx.compose.foundation.shape.RoundedCornerShape
31 import androidx.compose.runtime.Composable
32 import androidx.compose.runtime.LaunchedEffect
33 import androidx.compose.runtime.getValue
34 import androidx.compose.runtime.mutableStateOf
35 import androidx.compose.runtime.remember
36 import androidx.compose.runtime.setValue
37 import androidx.compose.ui.Modifier
38 import androidx.compose.ui.draw.clip
39 import androidx.compose.ui.graphics.Color
40 import androidx.compose.ui.layout.layoutId
41 import androidx.compose.ui.platform.testTag
42 import androidx.compose.ui.tooling.preview.Preview
43 import androidx.compose.ui.unit.dp
44 import androidx.constraintlayout.compose.Dimension
45 import androidx.constraintlayout.compose.ExperimentalMotionApi
46 import androidx.constraintlayout.compose.MotionLayout
47 import androidx.constraintlayout.compose.MotionScene
48 import kotlin.random.Random
49
50 /**
51 * Shows how to use MotionLayout to have animated graphs in a LazyColumn, where each graph is
52 * animated as it's revealed.
53 *
54 * Demonstrates how to dynamically create constraints based on input. See [DynamicGraph]. Where
55 * constraints are created to lay out the given values into a single graph layout.
56 */
57 @Preview(group = "scroll", device = "spec:width=480dp,height=800dp,dpi=440")
58 @Composable
59 fun DynamicGraphsPreview(modifier: Modifier = Modifier) {
60 val graphs = remember {
61 val list = mutableListOf<List<Float>>()
62 repeat(300) {
63 val values = FloatArray(10) { Random.nextInt(100).toFloat() + 10f }.asList()
64 list.add(values)
65 }
66 return@remember list
67 }
68 LazyColumn(modifier.testTag("LazyColumn")) {
69 items(graphs.size) {
70 Box(modifier = Modifier.padding(3.dp).height(200.dp)) { DynamicGraph(graphs[it]) }
71 }
72 }
73 }
74
75 @Preview(group = "scroll", device = "spec:width=480dp,height=800dp,dpi=440")
76 @Composable
DynamicGraphnull77 private fun DynamicGraph(
78 values: List<Float> = listOf<Float>(12f, 32f, 21f, 32f, 2f),
79 max: Int = 100
80 ) {
81 val scene = remember {
82 val scale = values.map { (it * 0.8f) / max }
83 val count = values.size
84 val widthPercent = 1 / (count * 2f)
85
86 MotionScene {
87 val cols = Array(count) { i -> createRefFor("foo$i") }
88 val start1 = constraintSet {
89 createHorizontalChain(elements = cols)
90 cols.forEach {
91 constrain(it) {
92 width = Dimension.percent(widthPercent)
93 height = Dimension.value(1.dp)
94 bottom.linkTo(parent.bottom, 16.dp)
95 }
96 }
97 }
98
99 val end1 = constraintSet {
100 createHorizontalChain(elements = cols)
101 cols.forEachIndexed { i, col ->
102 constrain(col) {
103 width = Dimension.percent(widthPercent)
104 height = Dimension.percent(scale[i])
105 bottom.linkTo(parent.bottom, 16.dp)
106 }
107 }
108 }
109 defaultTransition(start1, end1)
110 }
111 }
112 var animateToEnd by remember { mutableStateOf(true) }
113 val progress = remember { Animatable(0f) }
114
115 // Animate on reveal
116 LaunchedEffect(animateToEnd) {
117 progress.animateTo(if (animateToEnd) 1f else 0f, animationSpec = tween(800))
118 }
119
120 MotionLayout(
121 modifier =
122 Modifier.background(Color(0xFF221010))
123 .fillMaxSize()
124 .clickable { animateToEnd = !animateToEnd }
125 .padding(1.dp),
126 motionScene = scene,
127 progress = progress.value
128 ) {
129 for (i in 0..values.size) {
130 Box(
131 modifier =
132 Modifier.layoutId("foo$i")
133 .clip(RoundedCornerShape(20.dp))
134 .background(Color.hsv(i * 240f / values.size, 0.6f, 0.6f))
135 )
136 }
137 }
138 }
139