Created
March 31, 2023 06:46
-
-
Save maxfie1d/70a5713bc74e2fa2cb5e0c76375b4060 to your computer and use it in GitHub Desktop.
Custom list arrangement (LazyColumn, sticky footer)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.custom_list_arrangement | |
import android.os.Bundle | |
import androidx.activity.ComponentActivity | |
import androidx.activity.compose.setContent | |
import androidx.compose.foundation.background | |
import androidx.compose.foundation.clickable | |
import androidx.compose.foundation.layout.Arrangement | |
import androidx.compose.foundation.layout.Box | |
import androidx.compose.foundation.layout.Row | |
import androidx.compose.foundation.layout.fillMaxSize | |
import androidx.compose.foundation.layout.fillMaxWidth | |
import androidx.compose.foundation.layout.padding | |
import androidx.compose.foundation.lazy.LazyColumn | |
import androidx.compose.material3.DropdownMenuItem | |
import androidx.compose.material3.ExperimentalMaterial3Api | |
import androidx.compose.material3.ExposedDropdownMenuBox | |
import androidx.compose.material3.ExposedDropdownMenuDefaults | |
import androidx.compose.material3.MaterialTheme | |
import androidx.compose.material3.Surface | |
import androidx.compose.material3.Switch | |
import androidx.compose.material3.Text | |
import androidx.compose.material3.TextField | |
import androidx.compose.runtime.Composable | |
import androidx.compose.runtime.getValue | |
import androidx.compose.runtime.mutableStateOf | |
import androidx.compose.runtime.remember | |
import androidx.compose.runtime.setValue | |
import androidx.compose.ui.Alignment | |
import androidx.compose.ui.Modifier | |
import androidx.compose.ui.graphics.Color | |
import androidx.compose.ui.unit.Density | |
import androidx.compose.ui.unit.dp | |
import com.example.custom_list_arrangement.ui.theme.CustomlistarrangementTheme | |
class MainActivity : ComponentActivity() { | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
setContent { | |
CustomlistarrangementTheme { | |
var isMany by remember { mutableStateOf(false) } | |
val itemCount = if (isMany) 20 else 3 | |
var selectedType by remember { mutableStateOf(ArrangementType.Normal) } | |
Surface( | |
modifier = Modifier.fillMaxSize(), | |
color = MaterialTheme.colorScheme.background | |
) { | |
LazyColumn( | |
verticalArrangement = CustomArrangement(selectedType) | |
) { | |
item { | |
Box( | |
modifier = Modifier.padding(4.dp) | |
) { | |
Row( | |
verticalAlignment = Alignment.CenterVertically | |
) { | |
Text(text = "アイテムいっぱい") | |
Switch(checked = isMany, onCheckedChange = { isMany = !isMany }) | |
TypeSelector( | |
selectedType = selectedType, | |
onTypeClick = { | |
selectedType = it | |
} | |
) | |
} | |
} | |
} | |
items(itemCount) { index -> | |
Text( | |
text = "Item ${index + 1}", | |
modifier = Modifier | |
.clickable { } | |
.padding(16.dp) | |
.fillMaxWidth() | |
) | |
} | |
item { | |
Text( | |
text = "footer", | |
modifier = Modifier | |
.clickable { } | |
.fillMaxWidth() | |
.background(Color.Yellow) | |
.padding(16.dp) | |
) | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
class CustomArrangement( | |
private val type: ArrangementType | |
) : Arrangement.Vertical { | |
override fun Density.arrange( | |
totalSize: Int, // LazyColumnの高さ? | |
sizes: IntArray, // それぞれのアイテムの高さ? | |
outPositions: IntArray // 描画位置の設定? | |
) { | |
when (type) { | |
ArrangementType.Normal -> { | |
var y = 0 | |
sizes.forEachIndexed { index, size -> | |
outPositions[index] = y | |
y += size | |
} | |
} | |
ArrangementType.HalfSticky -> { | |
var y = 0 | |
sizes.forEachIndexed { index, size -> | |
outPositions[index] = y | |
y += size | |
} | |
if (y < totalSize) { | |
val lastIndex = outPositions.lastIndex | |
outPositions[lastIndex] = totalSize - sizes.last() | |
} | |
} | |
ArrangementType.Sticky -> { | |
var y = 0 | |
sizes.forEachIndexed { index, size -> | |
outPositions[index] = y | |
y += size | |
} | |
val lastIndex = outPositions.lastIndex | |
outPositions[lastIndex] = totalSize | |
} | |
ArrangementType.Spaced -> { | |
var y = 0 | |
sizes.forEachIndexed { index, size -> | |
outPositions[index] = y | |
y += size + (20.dp.roundToPx()) | |
} | |
} | |
} | |
} | |
} | |
@OptIn(ExperimentalMaterial3Api::class) | |
@Composable | |
fun TypeSelector( | |
selectedType: ArrangementType, | |
onTypeClick: (ArrangementType) -> Unit | |
) { | |
val options = ArrangementType.values().toList() | |
var expanded by remember { mutableStateOf(false) } | |
ExposedDropdownMenuBox( | |
expanded = expanded, | |
onExpandedChange = { expanded = !expanded }, | |
) { | |
TextField( | |
modifier = Modifier.menuAnchor(), | |
readOnly = true, | |
value = selectedType.toString(), | |
onValueChange = {}, | |
colors = ExposedDropdownMenuDefaults.textFieldColors(), | |
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) }, | |
label = { Text("Label") }, | |
) | |
ExposedDropdownMenu( | |
expanded = expanded, | |
onDismissRequest = { expanded = false }, | |
) { | |
options.forEach { selectionOption -> | |
DropdownMenuItem( | |
text = { Text(selectionOption.toString()) }, | |
onClick = { | |
onTypeClick(selectionOption) | |
expanded = false | |
}, | |
contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding, | |
) | |
} | |
} | |
} | |
} | |
enum class ArrangementType { | |
Normal, | |
HalfSticky, | |
Sticky, // Not working | |
Spaced; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
spacing
で実現するべきだった