I am trying to run a simple LazyColumn Object, but am unable to run it without this weird error.
Here is my code:
#Composable
fun Test(){
LazyColumn() {
Text(text = "Placeholder", fontSize= 30.sp)
Spacer(modifier = Modifier.padding(10.dp))
}
}
Here are the errors:
org.jetbrains.kotlin.diagnostics.SimpleDiagnostic#74c0fa2 (error: could not render message)
org.jetbrains.kotlin.diagnostics.SimpleDiagnostic#c077eec3 (error: could not render message)
Is it something wrong with my code, or is it a bug?
*I wanted to test the scroll function by copy and pasting the lines after the LazyColumn() statement over and over
With 1.0.0-beta04 you can use:
val itemsList = (0..50).toList()
LazyColumn() {
items(itemsList) {
Text(text = "Placeholder", fontSize = 30.sp)
Spacer(modifier = Modifier.padding(10.dp))
}
}
In the LazyListScope in order to display the items you have to use one the provided functions:item, items, itemsindexed and stickyHeader.
The error studio should be showing is #Composable invocations can only happen from the context of a #Composable function; this is the error you get when you would compile this function. That Studio shows (error: could not render message) is a known bug that the team is working on.
The reason the compose compiler plugin generates this error is the lambda expected by LazyColumn is not a composable lambda but is the LazyList DSL in which the column is described. For example, something like,
#Composable
fun Test(){
LazyColumn() {
items(10_000) {
Text(text = "Placeholder $it", fontSize = 30.sp)
Spacer(modifier = Modifier.padding(10.dp))
}
}
}
is probably what you wanted. It doesn't create 10,000 items, it only creates enough to fit on the screen and will create additional rows as needed (discarding rows as they are occluded) up to row 9,999.
Try this:
#Composable
fun Test(){
LazyColumn() {
for (i in 1..10) {
TestItem(i)
}
}
}
#Composable
fun TestItem(i: Int) {
Text(text = "Placeholder $i", fontSize = 30.sp)
Spacer(modifier = Modifier.padding(10.dp))
}
Related
I have looked for many solutions but I found it as a newbie very
complex on how to solve it properly without throwing away all my backend
code.
I want to get an Float value from my RoomDB into a composable
UI value but as far as we all know getting RoomDB values with queries
needs an asynchronus scope. And those aren't capable of returning values
because values stay within a scope and die there too. There is I think
no way to no use Coroutine Scopes or anything else that doesn't block the UI loading
so it can actually work.
What can I do? I don't want to throw away the entire RoomDB database
neither our Jetpack Compose base GUI?
I tried replacing the 0.8f with a method that calls a Coroutine Scope which
should idealistically return a Float value to this part of our code.
#Composable
fun ChargeScreen(){
val context = LocalContext.current
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.fillMaxSize()
){
Column {
ChargeTopText()
CircularChargeBar(percentage = 0.8f, number =100 )
}
}
}
I am also new at Android Jetpack Compose but I can give you a suggestion for your case.
Take a look at the code below
#Composable
fun YourComposable() {
//Use remember for state management
//Read more at https://developer.android.com/jetpack/compose/state
var floatData by remember { mutableStateOf(0F) }
var isLoading by remember { mutableStateOf(true) }
//Side-effects
//Read more at https://developer.android.com/jetpack/compose/side-effects
LaunchedEffect(Unit) {
isLoading = true
floatData = getFloatDataFromDB()
isLoading = false
}
//Just a way to show a progress indicator while we are getting the value from DB.
if(!isLoading) {
Text(text = "FloatData: $floatData")
} else {
CircularProgressIndicator(
modifier = Modifier.size(50.dp),
color = Color.Green,
strokeWidth = 5.dp)
}
}
suspend fun getFloatDataFromDB(): Float {
//Using withContext(Dispatchers.IO) so it will execute in IO Thread.
return withContext(Dispatchers.IO) {
//Pretend this will take 5 seconds to complete
Thread.sleep(5000)
//And return the value
return#withContext 0.9F
}
}
I hope this will help you out!
Is there a way to convert jetpack Compose's androidx.compose.ui.graphics.Color to android.graphics.drawable.ColorDrawable ?
I tried ColorDrawable(Color.Red.toArgb()) but its not working!
You tried it in the correct way.
Here is a sample that does work
#Composable
fun MyComposable() {
val color = androidx.compose.ui.graphics.Color.Red
AndroidView(
modifier = Modifier
.fillMaxWidth()
.height(20.dp),
factory = { context ->
View(context).apply {
background = ColorDrawable(color.toArgb())
}
}
)
}
I’m in Android Studio version Chipmunk with one patch. I’m writing in Kotlin, a language I find is just beautiful. I have a dialog box over a fragment where I want to capture the changes in the edit text box as they happen. I’ve tried with straightforward code found here in Stack Overflow but I think the fragment can’t view the dialog box.
Following is code used:
private fun dlgFind() {
try {
//val sTxt: EditText = findViewById(R.id.txtStock)
val sTxt = binding.txtMeds
val spannable: Spannable = SpannableString(sTxt.text.toString())
sTxt.setText(spannable.toString()) // clears highlighted text
val dialog = Dialog(requireContext(), R.style.RoundedCornersDialogFind)
dialog.window?.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) // makes frag text readable
dialog.setContentView(R.layout.dlg_find)
// tried to max dialog window to full width of screen
//dialog.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
val btnFind = dialog.findViewById(R.id.btnGo) as ImageButton
val txtFind = dialog.findViewById(R.id.editFind) as EditText
txtFind.isFocusableInTouchMode = true
txtFind.isFocusable = true
//txtFind.inputType = InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
txtFind.requestFocus()
txtFind.postDelayed({
txtFind.requestFocus()
val imm = context?.getSystemService(INPUT_METHOD_SERVICE) as? InputMethodManager
imm?.showSoftInput(txtFind, InputMethodManager.SHOW_IMPLICIT)
}, 1)
btnFind.setOnClickListener { dialog.dismiss()
gsFindBoxTxt = txtFind.text.toString()
sTxt.requestFocus()
//sTxt.setText(spannable.toString()) // clears highlighted text
findWithDlg() // method finds strings
}
//dialog.setOnCancelListener { editHours() }
dialog.window?.setGravity(Gravity.BOTTOM)
dialog.show()
} catch (e: Exception) {
val methodName = object {}.javaClass.enclosingMethod?.name
Toast.makeText(context, methodName.toString(), Toast.LENGTH_LONG).show()
}
}
Attempt to read from dialog box in fragment:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
try {
val sEditTxt = view.findViewById<EditText>(R.id.editFind)
sEditTxt?.doOnTextChanged { _, _, _, _ ->
Toast.makeText(context, "!", Toast.LENGTH_LONG).show() }
if (sEditTxt?.text != null) {
sEditTxt.doAfterTextChanged {
Toast.makeText(context, "!", Toast.LENGTH_LONG).show()
findWithDlg()
}
}
} catch (e: Exception) {
Toast.makeText(context, e.toString(), Toast.LENGTH_LONG).show()
}
Note I have tried boilerplate TextWatcher code too and I can't return view in onCreate because I'm using binding throughout the fragment. The return in onCreate is to the binding.root. And even in onDestroyView the last line is _binding = null. Maybe remove all binding and revert to using views?
I've also tried these in the dialog method (function) to no avail yet...
Yes! I figured out how to listen to the text changes. I set following in the dialog method:
dialog.setOnShowListener { txtFind.doOnTextChanged { _, _, _, _ ->
Toast.makeText(context, "Text Change", Toast.LENGTH_LONG).show()
}
}
I'm trying to pass a constantly updating variable "message" across my Jetpack Composables. I have a draggable box that tracks the coordinates of the box but I'm trying to send the real-time data through a TCP connection. However, I noticed that the current coordinate of the draggable box isn't passing through to the other Composable or the socket -only the same value is passed despite message changing continuously due to me dragging the box. Also, the moment dataSendButton() is pressed, the createDragImage() and its draggable box stops animating/running.
var message = "" // global Android send message
class MainActivity : ComponentActivity() {
private var textView: TextView? = null
dataSendButton()
createDragImage()
...
}
}
}
#Composable
fun createDragImage(){
val context = LocalContext.current
...
Box() {
var offsetX by remember { mutableStateOf(0f) }
var offsetY by remember { mutableStateOf(0f) }
Box(
Modifier
.offset { IntOffset(offsetX.roundToInt(), offsetY.roundToInt()) }
.background(Color.Transparent)
.size(150.dp)
.border(BorderStroke(4.dp, SolidColor(Color.Red)))
.pointerInput(Unit) {
detectDragGestures { change, dragAmount ->
change.consumeAllChanges()
offsetX = someConstantX
offsetY += dragAmount.y
message = offsetY.toString()
...
#Composable
fun dataSendButton() {
val context = LocalContext.current
...
Button(
onClick = {
// **ISSUE: message in this composable is not getting updated with message value from createDragImage()
val b1 = MainActivity.TCPconnector_client(context, message)
b1.execute()
},
{
Text(text = "Send Data", color = Color.White, fontSize = 20.sp)
}
}
}
}
}
It is because that is not how you store state in Compose.
Change the declaration of the variable.
var message by mutableStateOf(...)
Then the changes to it will trigger a recomposition, and so the rest of the code should remain the same. It is always recommended to store the state holders in a viewmodel, and pass the viewmodel around instead.
This is a working code with viewmodel
class MainActivity : ComponentActivity() {
private var textView: TextView? = null
val vm by viewmodels<MViewModel>()
dataSendButton(vm.message, vm:: onMessageChange)
createDragImage(vm.message)
...
}
}
}
#Composable
fun createDragImage(message: String, onMessageChange: (String) -> Unit){
val context = LocalContext.current
...
Box() {
var offsetX by remember { mutableStateOf(0f) }
var offsetY by remember { mutableStateOf(0f) }
Box(
Modifier
.offset { IntOffset(offsetX.roundToInt(), offsetY.roundToInt()) }
.background(Color.Transparent)
.size(150.dp)
.border(BorderStroke(4.dp, SolidColor(Color.Red)))
.pointerInput(Unit) {
detectDragGestures { change, dragAmount ->
change.consumeAllChanges()
offsetX = someConstantX
offsetY += dragAmount.y
onMessageChange (offsetY.toString())
...
#Composable
fun dataSendButton(message: String) {
val context = LocalContext.current
...
Button(
onClick = {
// **ISSUE: message in this composable is not getting updated with message value from createDragImage() // This seems to be an error. Calling a Composable from onClick?
val b1 = MainActivity.TCPconnector_client(context, message)
b1.execute()
},
{
Text(text = "Send Data", color = Color.White, fontSize = 20.sp)
}
}
}
}
}
class MViewModel: ViewModel(){
var message by mutableStateOf("")
private set //do not allow external modifications to ensure consistency
fun onMessageChange (newMessage: String){
message = newMessage
}
}
Note this is the ideal way of doing such implementation. However, for your specific case, if you do not need to access it anywhere else, only changing the declaration as described in the second line of the answer should do
Thanks
I want to show a modal dialogue for the user to select a Bluetooth device in the case that I can't guess it from the device names.
It appears that AlertDialogue has the facility to show a spinner / dropdown.
The alert dialogue builder has a method setItems which I seem to think is what I need, however, its parameter is CharSequence[] but I have some sort of array of strings (I can't tell exactly what I have because everything is just val).
private fun showDialog() {
val names = (bta!!.bondedDevices).map { z -> z.name };
// What is the type of names? How can you find this out?
// How can you make it into a CharSequence[]?
val ab = AlertDialog.Builder(this);
ab.setTitle("Select device");
ab.setIcon(android.R.drawable.ic_dialog_alert); // I'd prefer a question mark.
ab.setPositiveButton("Select"){dialogueInterface, which -> Toast.makeText(applicationContext, "Selected", Toast.LENGTH_LONG).show()};
ab.setNeutralButton("Cancel"){dialogueInterface, which -> Toast.makeText(applicationContext, "Cancelled", Toast.LENGTH_LONG).show()};
ab.setItems(names); // None of the following functions can be called with the arguments supplied.
val a = ab.create();
a.setCancelable(false);
a.show();
}
I think this works in Java, but it doesn't in kotlin
CharSequence[] cs = list.toArray(new CharSequence[list.size()]);
So:
In AndroidStudio how can you tell the type of a variable? (In VisualStudio if you hover over a var then the tooltip tells you.)
How in kotlin do you make a CharSequence[]?
fun elmFind() {
// Find device
val pairedDevices: Set<BluetoothDevice>? = bta!!.bondedDevices
// Try to guess.
for (device in pairedDevices!!) {
if (device.name.contains("obd")) {
elmConnect(device.name);
return;
}
}
// Still going therefore didn't find one therefore ask.
val cs: Array<CharSequence> = pairedDevices.map { z -> z.name }.toTypedArray()
var elmDeviceName: String = ""
val ab = AlertDialog.Builder(this);
ab.setTitle("Select device");
ab.setIcon(android.R.drawable.ic_dialog_alert);
ab.setPositiveButton("Select") { dialogueInterface, which ->
elmConnect(elmDeviceName);
};
ab.setNeutralButton("Cancel") { dialogueInterface, which ->
Toast.makeText(
applicationContext,
"Cancelled",
Toast.LENGTH_LONG
).show()
};
ab.setItems(cs) { dialog, which -> elmDeviceName = cs[which].toString() };
val a = ab.create();
a.setCancelable(false);
a.show();
}
Will this work with the local variable elmDeviceName being used to get the value of the selected item in the case when the OK button is pressed?