Issue
code:
package com.example.saveandloadusername
import android.content.Context
import android.content.Context.MODE_PRIVATE
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.unit.dp
import com.example.saveandloadusername.ui.theme.SaveAndLoadUserNameTheme
import java.io.File
import java.io.IOException
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
SaveAndLoadUserNameTheme {
Surface(color = MaterialTheme.colors.background) {
MainScreen(baseContext)
}
}
}
}
}
@Composable
fun MainScreen(context: Context) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
)
{
var name by remember { mutableStateOf("")}
if(checkIfNameIsEmpty(readNameFromInternalStorage(context))) {
Text(text="Hello, give me your name :)")
} else {
Text(text="welcome back ${readNameFromInternalStorage(context)}")
}
Spacer(modifier = Modifier
.height(10.dp)
.fillMaxWidth())
OutlinedTextField(
value=name,
onValueChange={ name = it },
label={Text(text="Name")},
singleLine = true,
keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Words)
)
Spacer(modifier = Modifier
.height(10.dp)
.fillMaxWidth())
Button(
onClick = {
name = name.replace(" ", "".replace("\n", ""))
if(name == "") {
Toast.makeText(context, "name invalid", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, "name (${name}) saved :D", Toast.LENGTH_SHORT).show()
saveNameToInternalStorage(name, context)
}
},
)
{
Text(text="save")
}
Spacer(modifier = Modifier
.height(10.dp)
.fillMaxWidth())
Button(
onClick = {
if(checkIfNameIsEmpty(readNameFromInternalStorage(context))) {
Toast.makeText(context, "you need to give me a name first ;)", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(
context,
"the name is: '${readNameFromInternalStorage(context)}'",
Toast.LENGTH_SHORT
).show()
}
},
)
{
Text(text="check")
}
Spacer(modifier = Modifier
.height(10.dp)
.fillMaxWidth())
Button(onClick = { cleanNameData(context)}) {
Text("Remove name")
}
}
}
private fun saveNameToInternalStorage(name: String, context: Context): Boolean {
return try {
context.applicationContext.openFileOutput("name.txt", MODE_PRIVATE).use { stream ->
stream.flush()
stream.write(name.toByteArray())
Log.i("SAVE_STATE","name ($name) written as ${name.toByteArray()}")
}
return true
} catch(e: IOException) {
e.printStackTrace()
false
}
}
private fun readNameFromInternalStorage(context: Context): String {
val file = File(context.filesDir, "name.txt")
return if (file.exists()) {
val contentOfFile = file.readBytes().decodeToString()
Log.i("CONTENTTT", contentOfFile)
contentOfFile
} else {
""
}
}
private fun checkIfNameIsEmpty(name: String): Boolean {
return name.isEmpty()
}
private fun cleanNameData(context: Context) {
context.applicationContext.openFileOutput("name.txt", MODE_PRIVATE).use { stream ->
stream.flush()
Log.i("cleanNameData", "Name Removed from memory")
Toast.makeText(context, "Name removed", Toast.LENGTH_SHORT).show()
}
}
I've created a little app to get to know android compose and I've stumbled upon a problem that I cannot solve, the Text (that is above the TextField) doesn't update after the buttons "remove name" or "save" are pressed, the text only updates when something changes in the textBox, is there a way to force a recomposition of that text manually? Any help appreciated :)
Solution
Firstly, if all that you need to do is display that name over there, then why take the hassle of storing it to storage, then reading back? Suggestion: You could just use the name parameter as the value of the Text
, and upon modification, you could just store the new name to the storage, and if the transaction succeeds, then update the variable. It will trigger the necessary recomposition.
Next up, the reason why the text does not update is because the method you are using to get the name from storage returns a primitive type, not a LiveData
. Hence, instead of manually triggering a recompostion, if you are implementing the method yourself, try reading about LiveData
first. It should not be difficult to implement.
HOWEVER (not recommended), if all you want is a method to manually re-trigger a composition, then here it is.
As you might know, Compose uses MutableState
objects to observe data, which you seem to be very familiar with, since you are already using that. Hence, all you need to do is just add a 'dummy' variable of type MutableState
, and then modify it from the onClick
s of the buttons in concern. Also, you have to give Compose a message that the dummy variable is being read in the Text
, so it would trigger recompostions.
@Composable
fun MainScreen(context: Context) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
)
{
var name by remember { mutableStateOf("")}
var dummy by mutableStateOf(false) // don't even need to remember, long as it compiles
if(checkIfNameIsEmpty(readNameFromInternalStorage(context))) {
dummy
Text(text="Hello, give me your name :)")
} else {
dummy
Text(text="welcome back ${readNameFromInternalStorage(context)}")
}
Spacer(modifier = Modifier
.height(10.dp)
.fillMaxWidth())
OutlinedTextField(
value=name,
onValueChange={ name = it },
label={Text(text="Name")},
singleLine = true,
keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Words)
)
Spacer(modifier = Modifier
.height(10.dp)
.fillMaxWidth())
Button(
onClick = {
dummy = !dummy // modify to trigger recomposition
name = name.replace(" ", "".replace("\n", ""))
if(name == "") {
Toast.makeText(context, "name invalid", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, "name (${name}) saved :D", Toast.LENGTH_SHORT).show()
saveNameToInternalStorage(name, context)
}
},
)
{
Text(text="save")
}
Spacer(modifier = Modifier
.height(10.dp)
.fillMaxWidth())
Button(
onClick = {
dummy = !dummy // The same here
if(checkIfNameIsEmpty(readNameFromInternalStorage(context))) {
Toast.makeText(context, "you need to give me a name first ;)", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(
context,
"the name is: '${readNameFromInternalStorage(context)}'",
Toast.LENGTH_SHORT
).show()
}
},
)
{
Text(text="check")
}
Spacer(modifier = Modifier
.height(10.dp)
.fillMaxWidth())
Button(onClick = {
dummy = !dummy //That's all
cleanNameData(context)
}) {
Text("Remove name")
}
}
}
Yeah this oughta do it.
Hey also, you do not need to paste the entire codebase. Just provide the necessary bits and we'll ask for more if required. Thanks,
Answered By - MARSK
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.