Pantalla de configuración
Lo primero que se realizar es la creación de la pantalla de configuración.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_dark"
app:tint="@color/texto"
android:layout_margin="16dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Modo oscuro"
android:layout_weight="1"
android:textColor="@color/texto"
android:layout_margin="16dp"/>
<Switch
android:id="@+id/switchDarkMod"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:layout_margin="16dp"/>
</LinearLayout>
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginVertical="5dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_volume"
app:tint="@color/texto"
android:layout_margin="16dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Volumen"
android:textColor="@color/texto"
android:layout_margin="16dp"/>
<com.google.android.material.slider.RangeSlider
android:id="@+id/rsVolume"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:stepSize="1"
android:valueFrom="0"
android:valueTo="100"/>
</LinearLayout>
</LinearLayout>
Grabación de la información
Es necesario modificar el gradel para meter la dependencia al DataStore
//DataStore
implementation ("androidx.datastore:datastore-preferences:1.0.0")
A continuación, realizar la grabación de la información de pantalla, siempre en una coroutina.
package com.javi.u11v
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Switch
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import com.google.android.material.slider.RangeSlider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
val Context.dataStore: DataStore<Preferences> by preferencesDataStore("settings")
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val switchDarkMod=findViewById<Switch>(R.id.switchDarkMod)
val rsVolume = findViewById<RangeSlider>(R.id.rsVolume)
//GRABACIÓN
switchDarkMod.setOnCheckedChangeListener { compoundButton, b ->
Log.i("JAI","El valor es $b")
CoroutineScope(Dispatchers.IO).launch {
dataStore.edit {
it[booleanPreferencesKey("OSCURE_MODE")]=b
}
}
}
rsVolume.addOnChangeListener { slider, value, fromUser ->
Log.i("JAI","El volumen es $value")
CoroutineScope(Dispatchers.IO).launch {
dataStore.edit {
it[intPreferencesKey("VOLUME")]=value.toInt()
}
}
}
}
}
Lectura de la información
Será necesario realizar la lectura y guardar la información en los controles. Hay que tener la precaución de los controles muy dinámicos que la lectura y grabación de los valores hará inestable el control, con lo que hay que poner una variable para controlar que solo lo haga una vez.
package com.javi.u11v
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Switch
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import com.google.android.material.slider.RangeSlider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
val Context.dataStore: DataStore<Preferences> by preferencesDataStore("settings")
var firstTime=true
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val switchDarkMod=findViewById<Switch>(R.id.switchDarkMod)
val rsVolume = findViewById<RangeSlider>(R.id.rsVolume)
//LECTURA
CoroutineScope(Dispatchers.IO).launch {
val darkMode: Flow<Boolean> = dataStore.data.map{
it[booleanPreferencesKey("OSCURE_MODE")]?:false
}
darkMode.collect{
runOnUiThread{
switchDarkMod.isChecked=it
}
}
}
CoroutineScope(Dispatchers.IO).launch {
val volume: Flow<Int> = dataStore.data.map{
it[intPreferencesKey("VOLUME")]?:50
}
volume.filter{firstTime}.collect{
runOnUiThread{
rsVolume.setValues(it.toFloat())
firstTime=!firstTime
}
}
}
//GRABACIÓN
switchDarkMod.setOnCheckedChangeListener { compoundButton, b ->
Log.i("JAI","El valor es $b")
CoroutineScope(Dispatchers.IO).launch {
dataStore.edit {
it[booleanPreferencesKey("OSCURE_MODE")]=b
}
}
}
rsVolume.addOnChangeListener { slider, value, fromUser ->
Log.i("JAI","El volumen es $value")
CoroutineScope(Dispatchers.IO).launch {
dataStore.edit {
it[intPreferencesKey("VOLUME")]=value.toInt()
}
}
}
}
}
Modo oscuro
Vemos como podemos activar y desactivar el modo oscuro en el momento de activar y desactivar el control
package com.javi.u11v
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Switch
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import com.google.android.material.slider.RangeSlider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
val Context.dataStore: DataStore<Preferences> by preferencesDataStore("settings")
var firstTime=true
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val switchDarkMod=findViewById<Switch>(R.id.switchDarkMod)
val rsVolume = findViewById<RangeSlider>(R.id.rsVolume)
//LECTURA
CoroutineScope(Dispatchers.IO).launch {
val darkMode: Flow<Boolean> = dataStore.data.map{
it[booleanPreferencesKey("OSCURE_MODE")]?:false
}
darkMode.collect{
runOnUiThread{
switchDarkMod.isChecked=it
if (it) enableDarkMode()
else disableDarkMode()
}
}
}
CoroutineScope(Dispatchers.IO).launch {
val volume: Flow<Int> = dataStore.data.map{
it[intPreferencesKey("VOLUME")]?:50
}
volume.filter{firstTime}.collect{
runOnUiThread{
rsVolume.setValues(it.toFloat())
firstTime=!firstTime
}
}
}
//GRABACIÓN
switchDarkMod.setOnCheckedChangeListener { compoundButton, b ->
Log.i("JAI","El valor es $b")
if (b) enableDarkMode()
else disableDarkMode()
CoroutineScope(Dispatchers.IO).launch {
dataStore.edit {
it[booleanPreferencesKey("OSCURE_MODE")]=b
}
}
}
rsVolume.addOnChangeListener { slider, value, fromUser ->
Log.i("JAI","El volumen es $value")
CoroutineScope(Dispatchers.IO).launch {
dataStore.edit {
it[intPreferencesKey("VOLUME")]=value.toInt()
}
}
}
}
private fun disableDarkMode() {
AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_NO)
delegate.applyDayNight()
}
private fun enableDarkMode() {
AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_YES)
delegate.applyDayNight()
}
}
Información recopila de Aristidevs