From b897ac8d50710f50840841bc323bb401edd71d41 Mon Sep 17 00:00:00 2001 From: Jamal Mulla Date: Sat, 22 Jan 2022 14:47:32 +0000 Subject: [PATCH] Fixed scroll issue in ListPref and MultiSelectListPref (#6) --- ComposePrefs/build.gradle | 12 +-- .../ui/{PrefDsl.kt => PrefsDsl.kt} | 0 .../ui/{PrefListItem.kt => PrefsListItem.kt} | 0 .../ui/{PrefScreen.kt => PrefsScreen.kt} | 0 .../ui/{PrefUtils.kt => PrefsUtils.kt} | 0 .../jamal/composeprefs/ui/prefs/ListPref.kt | 75 ++++++++++--------- .../ui/prefs/MultiSelectListPref.kt | 68 +++++++++-------- README.md | 5 +- .../composeprefssample/SettingsScreen.kt | 46 +++++++++++- build.gradle | 4 +- 10 files changed, 131 insertions(+), 79 deletions(-) rename ComposePrefs/src/main/java/com/jamal/composeprefs/ui/{PrefDsl.kt => PrefsDsl.kt} (100%) rename ComposePrefs/src/main/java/com/jamal/composeprefs/ui/{PrefListItem.kt => PrefsListItem.kt} (100%) rename ComposePrefs/src/main/java/com/jamal/composeprefs/ui/{PrefScreen.kt => PrefsScreen.kt} (100%) rename ComposePrefs/src/main/java/com/jamal/composeprefs/ui/{PrefUtils.kt => PrefsUtils.kt} (100%) diff --git a/ComposePrefs/build.gradle b/ComposePrefs/build.gradle index 7287671..9a11a08 100644 --- a/ComposePrefs/build.gradle +++ b/ComposePrefs/build.gradle @@ -10,8 +10,8 @@ android { defaultConfig { minSdk 21 targetSdk 31 - versionCode 1 - versionName "1.0.1" + versionCode 2 + versionName "1.0.2" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles "consumer-rules.pro" @@ -35,13 +35,13 @@ android { compose true } composeOptions { - kotlinCompilerExtensionVersion "1.1.0-rc01" + kotlinCompilerExtensionVersion "1.2.0-alpha01" } } dependencies { - implementation "androidx.compose.ui:ui:1.1.0-rc01" - implementation "androidx.compose.material:material:1.1.0-rc01" + implementation "androidx.compose.ui:ui:1.2.0-alpha01" + implementation "androidx.compose.material:material:1.2.0-alpha01" implementation "androidx.datastore:datastore-preferences:1.0.0" } @@ -54,7 +54,7 @@ afterEvaluate { groupId = 'com.github.jamalmulla' artifactId = 'ComposePrefs' - version = '1.0.1' + version = '1.0.2' } } } diff --git a/ComposePrefs/src/main/java/com/jamal/composeprefs/ui/PrefDsl.kt b/ComposePrefs/src/main/java/com/jamal/composeprefs/ui/PrefsDsl.kt similarity index 100% rename from ComposePrefs/src/main/java/com/jamal/composeprefs/ui/PrefDsl.kt rename to ComposePrefs/src/main/java/com/jamal/composeprefs/ui/PrefsDsl.kt diff --git a/ComposePrefs/src/main/java/com/jamal/composeprefs/ui/PrefListItem.kt b/ComposePrefs/src/main/java/com/jamal/composeprefs/ui/PrefsListItem.kt similarity index 100% rename from ComposePrefs/src/main/java/com/jamal/composeprefs/ui/PrefListItem.kt rename to ComposePrefs/src/main/java/com/jamal/composeprefs/ui/PrefsListItem.kt diff --git a/ComposePrefs/src/main/java/com/jamal/composeprefs/ui/PrefScreen.kt b/ComposePrefs/src/main/java/com/jamal/composeprefs/ui/PrefsScreen.kt similarity index 100% rename from ComposePrefs/src/main/java/com/jamal/composeprefs/ui/PrefScreen.kt rename to ComposePrefs/src/main/java/com/jamal/composeprefs/ui/PrefsScreen.kt diff --git a/ComposePrefs/src/main/java/com/jamal/composeprefs/ui/PrefUtils.kt b/ComposePrefs/src/main/java/com/jamal/composeprefs/ui/PrefsUtils.kt similarity index 100% rename from ComposePrefs/src/main/java/com/jamal/composeprefs/ui/PrefUtils.kt rename to ComposePrefs/src/main/java/com/jamal/composeprefs/ui/PrefsUtils.kt diff --git a/ComposePrefs/src/main/java/com/jamal/composeprefs/ui/prefs/ListPref.kt b/ComposePrefs/src/main/java/com/jamal/composeprefs/ui/prefs/ListPref.kt index 95194e9..7e9e658 100644 --- a/ComposePrefs/src/main/java/com/jamal/composeprefs/ui/prefs/ListPref.kt +++ b/ComposePrefs/src/main/java/com/jamal/composeprefs/ui/prefs/ListPref.kt @@ -1,9 +1,11 @@ package com.jamal.composeprefs.ui.prefs import android.util.Log -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyItemScope +import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.foundation.lazy.items import androidx.compose.foundation.selection.selectable import androidx.compose.material.* import androidx.compose.runtime.* @@ -12,6 +14,7 @@ import androidx.compose.ui.Alignment.Companion.CenterVertically import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp import androidx.compose.ui.window.DialogProperties import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey @@ -49,26 +52,27 @@ fun ListPref( contentColor: Color = contentColorFor(dialogBackgroundColor), textColor: Color = MaterialTheme.colors.onBackground, enabled: Boolean = true, - entries: Map = mapOf() + entries: Map = mapOf(), //TODO: Change to List? ) { + + val entryList = entries.toList() var showDialog by rememberSaveable { mutableStateOf(false) } val selectionKey = stringPreferencesKey(key) val scope = rememberCoroutineScope() - // need to observe state with some sort of flow maybe? this also works val datastore = LocalPrefsDataStore.current val prefs by remember { datastore.data }.collectAsState(initial = null) var selected = defaultValue prefs?.get(selectionKey)?.also { selected = it } // starting value if it exists in datastore - fun edit(current: Map.Entry) = run { + fun edit(current: Pair) = run { scope.launch { try { datastore.edit { preferences -> - preferences[selectionKey] = current.key + preferences[selectionKey] = current.first } - onValueChange?.invoke(current.key) + onValueChange?.invoke(current.first) showDialog = false } catch (e: Exception) { Log.e("ListPref", "Could not write pref $key to database. ${e.printStackTrace()}") @@ -92,33 +96,36 @@ fun ListPref( if (showDialog) { AlertDialog( onDismissRequest = { showDialog = false }, - title = { Text(text = title) }, text = { - Column { - entries.forEach { current -> - val isSelected = selected == current.key - val onSelected = { - edit(current) - } - Row( - modifier = Modifier - .fillMaxWidth() - .selectable( + Column() { + Text(modifier = Modifier.padding(vertical = 16.dp), text = title) + LazyColumn { + items(entryList) { current -> + + val isSelected = selected == current.first + val onSelected = { + edit(current) + } + Row( + modifier = Modifier + .fillMaxWidth() + .selectable( + selected = isSelected, + onClick = { if (!isSelected) onSelected() } + ), + verticalAlignment = CenterVertically, + ) { + RadioButton( selected = isSelected, - onClick = { if (!isSelected) onSelected() } - ), - verticalAlignment = CenterVertically, - ) { - RadioButton( - selected = isSelected, - onClick = { if (!isSelected) onSelected() }, - colors = RadioButtonDefaults.colors(selectedColor = MaterialTheme.colors.primary) - ) - Text( - text = current.value, - style = MaterialTheme.typography.body2, - color = textColor - ) + onClick = { if (!isSelected) onSelected() }, + colors = RadioButtonDefaults.colors(selectedColor = MaterialTheme.colors.primary) + ) + Text( + text = current.second, + style = MaterialTheme.typography.body2, + color = textColor + ) + } } } } @@ -138,6 +145,4 @@ fun ListPref( ), ) } - - } \ No newline at end of file diff --git a/ComposePrefs/src/main/java/com/jamal/composeprefs/ui/prefs/MultiSelectListPref.kt b/ComposePrefs/src/main/java/com/jamal/composeprefs/ui/prefs/MultiSelectListPref.kt index 58e2991..c48c737 100644 --- a/ComposePrefs/src/main/java/com/jamal/composeprefs/ui/prefs/MultiSelectListPref.kt +++ b/ComposePrefs/src/main/java/com/jamal/composeprefs/ui/prefs/MultiSelectListPref.kt @@ -4,6 +4,9 @@ import android.util.Log import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items import androidx.compose.foundation.selection.selectable import androidx.compose.material.* import androidx.compose.runtime.* @@ -12,6 +15,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp import androidx.compose.ui.window.DialogProperties import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringSetPreferencesKey @@ -46,8 +50,10 @@ fun MultiSelectListPref( dialogBackgroundColor: Color = MaterialTheme.colors.surface, textColor: Color = MaterialTheme.colors.onBackground, enabled: Boolean = true, - entries: Map = mapOf() + entries: Map = mapOf() //TODO: Change to List? ) { + + val entryList = entries.toList() var showDialog by rememberSaveable { mutableStateOf(false) } val selectionKey = stringSetPreferencesKey(key) val scope = rememberCoroutineScope() @@ -58,13 +64,12 @@ fun MultiSelectListPref( var selected = defaultValue prefs?.get(selectionKey)?.also { selected = it } // starting value if it exists in datastore - fun edit(isSelected: Boolean, current: Map.Entry) = run { + fun edit(isSelected: Boolean, current: Pair) = run { scope.launch { try { - //todo improve by handling errors val result = when (!isSelected) { - true -> selected + current.key - false -> selected - current.key + true -> selected + current.first + false -> selected - current.first } datastore.edit { preferences -> preferences[selectionKey] = result @@ -92,36 +97,37 @@ fun MultiSelectListPref( if (showDialog) { AlertDialog( onDismissRequest = { showDialog = false }, - title = { Text(text = title) }, text = { Column { - entries.forEach { current -> - val isSelected = selected.contains(current.key) - val onSelectionChanged = { - edit(isSelected, current) - } - Row( - modifier = Modifier - .fillMaxWidth() - .selectable( - selected = isSelected, - onClick = { onSelectionChanged() } - ), - verticalAlignment = Alignment.CenterVertically, - ) { - Checkbox( - checked = isSelected, - onCheckedChange = { onSelectionChanged() }, - colors = CheckboxDefaults.colors(checkedColor = MaterialTheme.colors.primary) - ) - Text( - text = current.value, - style = MaterialTheme.typography.body2, - color = textColor - ) + Text(modifier = Modifier.padding(vertical = 16.dp), text = title) + LazyColumn { + items(entryList) { current -> + val isSelected = selected.contains(current.first) + val onSelectionChanged = { + edit(isSelected, current) + } + Row( + modifier = Modifier + .fillMaxWidth() + .selectable( + selected = isSelected, + onClick = { onSelectionChanged() } + ), + verticalAlignment = Alignment.CenterVertically, + ) { + Checkbox( + checked = isSelected, + onCheckedChange = { onSelectionChanged() }, + colors = CheckboxDefaults.colors(checkedColor = MaterialTheme.colors.primary) + ) + Text( + text = current.second, + style = MaterialTheme.typography.body2, + color = textColor + ) + } } } - } }, confirmButton = { diff --git a/README.md b/README.md index 9a823cc..a41d1d0 100644 --- a/README.md +++ b/README.md @@ -204,8 +204,7 @@ MultiSelectListPref( And that's it! You can create your whole preference screen in this way, and you can modify the individual parameters of each preference composable to achieve the functionality you require. If -something is missing, please create an Issue so we can discuss possible solutions. After all, this -is still version 1.0.0 and there are bound to be bugs and missing features. +something is missing, please create an Issue so we can discuss possible solutions. # Download @@ -218,7 +217,7 @@ maven { url "https://jitpack.io" } and in your module `build.gradle` file add the dependencies ``` groovy -implementation "com.github.JamalMulla:ComposePrefs:" // Current is 1.0.1 +implementation "com.github.JamalMulla:ComposePrefs:" // Current is 1.0.2 implementation "androidx.datastore:datastore-preferences:1.0.0" ``` diff --git a/app/src/main/java/com/jamal/composeprefssample/SettingsScreen.kt b/app/src/main/java/com/jamal/composeprefssample/SettingsScreen.kt index bd18597..bb2b095 100644 --- a/app/src/main/java/com/jamal/composeprefssample/SettingsScreen.kt +++ b/app/src/main/java/com/jamal/composeprefssample/SettingsScreen.kt @@ -190,7 +190,20 @@ fun SettingsScreen() { "1" to "Entry 2", "2" to "Entry 3", "3" to "Entry 4", - "4" to "Entry 5" + "4" to "Entry 5", + "5" to "Entry 6", + "6" to "Entry 7", + "7" to "Entry 8", + "8" to "Entry 9", + "9" to "Entry 10", + "10" to "Entry 11", + "11" to "Entry 12", + "12" to "Entry 13", + "13" to "Entry 14", + "14" to "Entry 15", + "15" to "Entry 16", + "16" to "Entry 17", + "17" to "Entry 18" ) ) } @@ -222,7 +235,36 @@ fun SettingsScreen() { "1" to "Entry 2", "2" to "Entry 3", "3" to "Entry 4", - "4" to "Entry 5" + "4" to "Entry 5", + "5" to "Entry 6" + ) + ) + } + + prefsItem { + MultiSelectListPref( + key = "msl2", + title = "MultiSelectListPref", + summary = "Pick multiple entries at once from a long list", + entries = mapOf( + "0" to "Entry 1", + "1" to "Entry 2", + "2" to "Entry 3", + "3" to "Entry 4", + "4" to "Entry 5", + "5" to "Entry 6", + "6" to "Entry 7", + "7" to "Entry 8", + "8" to "Entry 9", + "9" to "Entry 10", + "10" to "Entry 11", + "11" to "Entry 12", + "12" to "Entry 13", + "13" to "Entry 14", + "14" to "Entry 15", + "15" to "Entry 16", + "16" to "Entry 17", + "17" to "Entry 18" ) ) } diff --git a/build.gradle b/build.gradle index 18dd571..37f31a9 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { ext { - compose_version = '1.1.0-rc01' + compose_version = '1.2.0-alpha01' } repositories { google() @@ -9,7 +9,7 @@ buildscript { } dependencies { classpath "com.android.tools.build:gradle:7.0.4" - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.0" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files