Skip to content

Commit 13a1642

Browse files
committed
Merge branch 'main' into lazily-load-list
# Conflicts: # compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt
2 parents f1ba73a + 5e2384b commit 13a1642

File tree

15 files changed

+318
-74
lines changed

15 files changed

+318
-74
lines changed

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ plugins {
55
alias(libs.plugins.android.application) apply false
66
alias(libs.plugins.android.library) apply false
77
alias(libs.plugins.kotlin.android) apply false
8-
alias(libs.plugins.kapt) apply false
8+
alias(libs.plugins.ksp) apply false
99
alias(libs.plugins.hilt) apply false
1010
alias(libs.plugins.kotlin.parcelize) apply false
1111
alias(libs.plugins.compose.compiler) apply false

compose/snippets/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
plugins {
1818
alias(libs.plugins.android.application)
1919
alias(libs.plugins.kotlin.android)
20-
alias(libs.plugins.kapt)
20+
alias(libs.plugins.ksp)
2121
alias(libs.plugins.hilt)
2222
alias(libs.plugins.kotlin.parcelize)
2323
alias(libs.plugins.compose.compiler)
@@ -146,7 +146,7 @@ dependencies {
146146
implementation(libs.hilt.android)
147147
implementation(libs.glide.compose)
148148

149-
kapt(libs.hilt.compiler)
149+
ksp(libs.hilt.compiler)
150150

151151
testImplementation(libs.junit)
152152

compose/snippets/src/main/java/com/example/compose/snippets/animations/AnimationSnippets.kt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ import androidx.compose.ui.unit.Dp
109109
import androidx.compose.ui.unit.IntSize
110110
import androidx.compose.ui.unit.dp
111111
import com.example.compose.snippets.R
112+
import java.text.BreakIterator
113+
import java.text.StringCharacterIterator
114+
import kotlinx.coroutines.delay
112115

113116
/*
114117
* Copyright 2023 The Android Open Source Project
@@ -820,3 +823,39 @@ private fun Expanded() {
820823
@Composable
821824
private fun ContentIcon() {
822825
}
826+
827+
// [START android_compose_animations_char_by_char]
828+
@Composable
829+
private fun AnimatedText() {
830+
val text = "This text animates as though it is being typed \uD83E\uDDDE\u200D\uFE0F \uD83D\uDD10 \uD83D\uDC69\u200D\uFE0F\u200D\uD83D\uDC68 \uD83D\uDC74\uD83C\uDFFD"
831+
832+
// Use BreakIterator as it correctly iterates over characters regardless of how they are
833+
// stored, for example, some emojis are made up of multiple characters.
834+
// You don't want to break up an emoji as it animates, so using BreakIterator will ensure
835+
// this is correctly handled!
836+
val breakIterator = remember(text) { BreakIterator.getCharacterInstance() }
837+
838+
// Define how many milliseconds between each character should pause for. This will create the
839+
// illusion of an animation, as we delay the job after each character is iterated on.
840+
val typingDelayInMs = 50L
841+
842+
var substringText by remember {
843+
mutableStateOf("")
844+
}
845+
LaunchedEffect(text) {
846+
// Initial start delay of the typing animation
847+
delay(1000)
848+
breakIterator.text = StringCharacterIterator(text)
849+
850+
var nextIndex = breakIterator.next()
851+
// Iterate over the string, by index boundary
852+
while (nextIndex != BreakIterator.DONE) {
853+
substringText = text.subSequence(0, nextIndex).toString()
854+
// Go to the next logical character boundary
855+
nextIndex = breakIterator.next()
856+
delay(typingDelayInMs)
857+
}
858+
}
859+
Text(substringText)
860+
// [END android_compose_animations_char_by_char]
861+
}

compose/snippets/src/main/java/com/example/compose/snippets/interop/ComposeWithOtherLibraries.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ private object StreamData {
162162
}
163163
}
164164

165-
private object HiltExample3 {
165+
object HiltExample3 {
166166
// [START android_compose_libraries_hilt_3]
167167
@HiltViewModel
168168
class MyViewModel @Inject constructor(
@@ -180,7 +180,7 @@ private object HiltExample3 {
180180
interface ExampleRepository
181181
}
182182

183-
private object HiltViewModel {
183+
object HiltViewModel {
184184
@HiltViewModel
185185
class MyViewModel @Inject constructor() : ViewModel() { /* ... */ }
186186
// [START android_compose_libraries_hilt_viewmodel]
@@ -207,7 +207,7 @@ private object HiltViewModel {
207207
}
208208
}
209209

210-
private object HiltViewModelBackStack {
210+
object HiltViewModelBackStack {
211211
@HiltViewModel
212212
class MyViewModel @Inject constructor() : ViewModel() { /* ... */ }
213213

@@ -273,7 +273,7 @@ private object MapsExample {
273273
cameraPositionState = cameraPositionState
274274
) {
275275
Marker(
276-
state = MarkerState(position = singapore),
276+
state = remember { MarkerState(position = singapore) },
277277
title = "Singapore",
278278
snippet = "Marker in Singapore"
279279
)

compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import androidx.compose.animation.core.Spring
2323
import androidx.compose.animation.core.spring
2424
import androidx.compose.animation.core.tween
2525
import androidx.compose.foundation.ExperimentalFoundationApi
26+
import androidx.compose.foundation.border
2627
import androidx.compose.foundation.horizontalScroll
2728
import androidx.compose.foundation.layout.Arrangement
2829
import androidx.compose.foundation.layout.Box
@@ -34,11 +35,14 @@ import androidx.compose.foundation.layout.fillMaxWidth
3435
import androidx.compose.foundation.layout.height
3536
import androidx.compose.foundation.layout.padding
3637
import androidx.compose.foundation.layout.size
38+
import androidx.compose.foundation.layout.width
3739
import androidx.compose.foundation.layout.wrapContentHeight
40+
import androidx.compose.foundation.layout.wrapContentSize
3841
import androidx.compose.foundation.lazy.LazyColumn
3942
import androidx.compose.foundation.lazy.LazyRow
4043
import androidx.compose.foundation.lazy.grid.GridCells
4144
import androidx.compose.foundation.lazy.grid.GridItemSpan
45+
import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
4246
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
4347
import androidx.compose.foundation.lazy.grid.items
4448
import androidx.compose.foundation.lazy.items
@@ -48,6 +52,7 @@ import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
4852
import androidx.compose.foundation.lazy.staggeredgrid.items
4953
import androidx.compose.foundation.rememberScrollState
5054
import androidx.compose.foundation.verticalScroll
55+
import androidx.compose.material3.Button
5156
import androidx.compose.material3.Card
5257
import androidx.compose.material3.CircularProgressIndicator
5358
import androidx.compose.material3.Text
@@ -60,6 +65,7 @@ import androidx.compose.runtime.rememberCoroutineScope
6065
import androidx.compose.runtime.saveable.rememberSaveable
6166
import androidx.compose.runtime.snapshotFlow
6267
import androidx.compose.ui.Modifier
68+
import androidx.compose.ui.graphics.Color
6369
import androidx.compose.ui.layout.ContentScale
6470
import androidx.compose.ui.tooling.preview.Preview
6571
import androidx.compose.ui.unit.Density
@@ -769,7 +775,6 @@ fun MessageList(modifier: Modifier,
769775
}
770776
}
771777
}
772-
773778
@Composable
774779
fun MessagePlaceholder(modifier: Modifier) {
775780
Box(
@@ -795,4 +800,55 @@ fun MessageList(modifier: Modifier,
795800
}
796801
}
797802
}
798-
// [END android_compose_layouts_lazily_load_list]
803+
// [END android_compose_layouts_lazily_load_list]
804+
805+
// [START android_compose_lists_snap_scroll_button]
806+
@Composable
807+
fun MessageList(modifier: Modifier = Modifier) {
808+
val listState = rememberLazyListState()
809+
val coroutineScope = rememberCoroutineScope()
810+
811+
LazyColumn(state = listState, modifier = Modifier.height(120.dp)) {
812+
items(10) { index ->
813+
Text(
814+
modifier = Modifier.height(40.dp),
815+
text = "Item $index"
816+
)
817+
}
818+
}
819+
820+
Button(onClick = {
821+
coroutineScope.launch {
822+
listState.animateScrollToItem(index = 0)
823+
}
824+
}) {
825+
Text(text = "Go top")
826+
}
827+
}
828+
// [END android_compose_lists_snap_scroll_button]
829+
830+
// [START android_compose_layout_scrollable_grid]
831+
@Composable
832+
fun ScrollingGrid() {
833+
val itemsList = (0..15).toList()
834+
835+
val itemModifier = Modifier
836+
.border(1.dp, Color.Blue)
837+
.width(80.dp)
838+
.wrapContentSize()
839+
840+
LazyHorizontalGrid(
841+
rows = GridCells.Fixed(3),
842+
horizontalArrangement = Arrangement.spacedBy(16.dp),
843+
verticalArrangement = Arrangement.spacedBy(16.dp)
844+
) {
845+
items(itemsList) {
846+
Text("Item is $it", itemModifier)
847+
}
848+
849+
item {
850+
Text("Single item", itemModifier)
851+
}
852+
}
853+
}
854+
// [END android_compose_layout_scrollable_grid]
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2024 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+
* https://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+
package com.example.compose.snippets.lists
18+
19+
import android.provider.MediaStore
20+
import androidx.compose.foundation.lazy.LazyColumn
21+
import androidx.compose.runtime.Composable
22+
import org.w3c.dom.Text
23+
24+
// [START android_compose_lists_multiple_item_types]
25+
@Composable
26+
fun ListWithMultipleItems(messages: List<Any>) {
27+
LazyColumn {
28+
items(
29+
messages.size,
30+
contentType = { it }
31+
) {
32+
for (message in messages)
33+
when (message) {
34+
is MediaStore.Audio -> AudioMessage(message)
35+
is Text -> TextMessage(message)
36+
}
37+
}
38+
}
39+
}
40+
41+
@Composable
42+
fun AudioMessage(message: MediaStore.Audio) {
43+
TODO("Not yet implemented.")
44+
}
45+
46+
@Composable
47+
fun TextMessage(message: Text) {
48+
TODO("Not yet implemented.")
49+
}
50+
51+
data class SampleMessage(val text: String, val content: Any)
52+
// [END android_compose_lists_multiple_item_types]
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright 2024 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+
* https://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+
package com.example.compose.snippets.lists
18+
19+
import androidx.compose.foundation.layout.size
20+
import androidx.compose.foundation.lazy.LazyColumn
21+
import androidx.compose.foundation.lazy.LazyRow
22+
import androidx.compose.material3.Text
23+
import androidx.compose.runtime.Composable
24+
import androidx.compose.ui.Modifier
25+
import androidx.compose.ui.unit.dp
26+
import coil.compose.AsyncImage
27+
28+
// [START android_compose_layouts_nested_scrolling]
29+
@Composable
30+
fun NestedScrollingRowsList(urls: List<String>) {
31+
LazyColumn {
32+
items(10) {
33+
LazyRow {
34+
item { Text("Row: $it") }
35+
items(urls.size) { index ->
36+
// AsyncImage provided by Coil.
37+
AsyncImage(
38+
model = urls[index],
39+
modifier = Modifier.size(150.dp),
40+
contentDescription = null
41+
)
42+
}
43+
}
44+
}
45+
}
46+
}
47+
// [END android_compose_layouts_nested_scrolling]

compose/snippets/src/main/java/com/example/compose/snippets/mentalmodel/ThinkingInComposeSnippets.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import androidx.compose.foundation.layout.Row
2525
import androidx.compose.foundation.lazy.LazyColumn
2626
import androidx.compose.foundation.lazy.items
2727
import androidx.compose.material3.Button
28+
import androidx.compose.material3.Card
2829
import androidx.compose.material3.Checkbox
2930
import androidx.compose.material3.HorizontalDivider
3031
import androidx.compose.material3.MaterialTheme
@@ -108,15 +109,16 @@ fun ListComposable(myList: List<String>) {
108109

109110
// [START android_compose_thinking_in_compose_incorrect]
110111
@Composable
111-
@Deprecated("Example with bug")
112112
fun ListWithBug(myList: List<String>) {
113113
var items = 0
114114

115115
Row(horizontalArrangement = Arrangement.SpaceBetween) {
116116
Column {
117117
for (item in myList) {
118-
Text("Item: $item")
119-
items++ // Avoid! Side-effect of the column recomposing.
118+
Card {
119+
Text("Item: $item")
120+
items++ // Avoid! Side-effect of the column recomposing.
121+
}
120122
}
121123
}
122124
Text("Count: $items")

compose/snippets/src/main/java/com/example/compose/snippets/sideeffects/SideEffectsSnippets.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,18 +180,18 @@ fun rememberFirebaseAnalytics(user: User): FirebaseAnalytics {
180180
}
181181
// [END android_compose_side_effects_sideeffect]
182182

183+
// b/368420773
184+
@Suppress("ProduceStateDoesNotAssignValue")
183185
// [START android_compose_side_effects_producestate]
184186
@Composable
185187
fun loadNetworkImage(
186188
url: String,
187189
imageRepository: ImageRepository = ImageRepository()
188190
): State<Result<Image>> {
189-
190191
// Creates a State<T> with Result.Loading as initial value
191192
// If either `url` or `imageRepository` changes, the running producer
192193
// will cancel and will be re-launched with the new inputs.
193194
return produceState<Result<Image>>(initialValue = Result.Loading, url, imageRepository) {
194-
195195
// In a coroutine, can make suspend calls
196196
val image = imageRepository.load(url)
197197

0 commit comments

Comments
 (0)