Button is not reacting as expected, I am trying to see if the function is not working or if there some other error - android

The function should use user input to do math and output into a text box for the user. On clicking the button, absolutely nothing happens. LogCat isn't showing me anything, so I'm not sure how to Troubleshoot this issue. I've got two similiar activities in the same project that are working fine, so I suspect I may not being doing the math correctly but can't find any other information. Any advice is appreciated.
package com.example.awcc
import android.content.Intent
import android.os.Bundle
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main3.*
class Setup : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
editTextNumber120.text.toString().toInt()
}
}
class ThirdActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main3)
val button2 = findViewById< Button >(R.id.button2)
button2.setOnClickListener {
val intent = Intent(this, SecondActivity::class.java)
startActivity(intent)
}
val button3 = findViewById< Button >(R.id.button3)
button3.setOnClickListener {
val intent2 = Intent(this, MainActivity::class.java)
startActivity(intent2)
val button7 = findViewById< Button >(R.id.button35)
button7.setOnClickListener {
var value1 = editTextNumber120.text.toString().toInt()
fun accessory(): Int {
return when {
value1 > 10 -> value1 * 0
value1 in 10..20 -> value1 * 1
value1 in 21..40 -> value1 * 2
value1 in 40..50 -> value1 * 3
value1 in 51..75 -> value1 * 4
value1 < 75 -> value1 * 5
else -> value1
}
}
val complete = accessory().toString()
try {
editTextNumber19?.setText(complete)
} catch (e: NumberFormatException) {
Toast.makeText(
applicationContext,
"Please enter a 0 in the blank field",
Toast.LENGTH_LONG
).show()
}
}
}
}
}

where is editTextNumber120 defined? what is this text being used for?
If you're expecting the value to carry over from the setup activity to activity3, that's not how it works. Each activity uses it's own data and if you need data to be shared across Activities and Fragments, then you need to create a data model for that information.
Example:
public class MyDataModel {
protected MutableLiveData<Int> editTextData;
public MyDataModel() {
editTextData = new MutableLiveData<>();
}
// setter/getter - returns a LiveData object that allows
// you to observe the value for any changes
// If you don't need to observe changes, then just keep it as an int/string
}
This way your other Activity can access the same data being used in the Setup activity. Also note, this doesn't persist across reboots, so if you want the setup to contain the previous data used in the last boot of the app, i'd look into SharedPreferences.
Also I don't know the structure of your app, but I would question why you need so many Activities? My app I'm working on is fairly robust but even I only have one activity (with a couple fragments), and another activity for the settings, and that's it.

I found the answer if anyone runs into this issue behind me. I was using Var instead of Val so it wasn't actually multiplying or changing the var at all. Changed to Val and it works like a charm!

Related

How to update data with SharedFlow in Android

In my application I want update data with SharedFlow and my application architecture is MVI .
I write below code, but just update one of data!
I have 2 spinners and this spinners data fill in viewmodel.
ViewModel code :
class MyViewModel #Inject constructor(private val repository: DetailRepository) : ViewModel() {
private val _state = MutableStateFlow<MyState>(MyState.Idle)
val state: StateFlow<MyState> get() = _state
fun handleIntent(intent: MyIntent) {
when (intent) {
is MyIntent.CategoriesList -> fetchingCategoriesList()
is MyIntent.PriorityList -> fetchingPrioritiesList()
}
}
private fun fetchingCategoriesList() {
val data = mutableListOf(Car, Animal, Color, Food)
_state.value = DetailState.CategoriesData(data)
}
private fun fetchingPrioritiesList() {
val data = mutableListOf(Low, Normal, High)
_state.value = DetailState.PriorityData(data)
}
}
With below codes I filled spinners in fragment :
lifecycleScope.launch {
//Send
viewModel.handleIntent(MyIntent.CategoriesList)
viewModel.handleIntent(MyIntent.PriorityList)
//Get
viewModel.state.collect { state ->
when (state) {
is DetailState.Idle -> {}
is DetailState.CategoriesData -> {
categoriesList.addAll(state.categoriesData)
categorySpinner.setupListWithAdapter(state.categoriesData) { itItem ->
category = itItem
}
Log.e("DetailLog","1")
}
is DetailState.PriorityData -> {
prioritiesList.addAll(state.prioritiesData)
prioritySpinner.setupListWithAdapter(state.prioritiesData) { itItem ->
priority = itItem
}
Log.e("DetailLog","2")
}
}
When run application not show me number 1 in logcat, just show number 2.
Not call this line : is DetailState.CategoriesData
But when comment this line viewModel.handleIntent(MyIntent.PriorityList) show me number 1 in logcat!
Why when use this code viewModel.handleIntent(MyIntent.CategoriesList) viewModel.handleIntent(MyIntent.PriorityList) not show number 1 and 2 in logcat ?
The problem is that a StateFlow is conflated, meaning if you rapidly change its value faster than collectors can collect it, old values are dropped without ever being collected. Therefore, StateFlow is not suited for an event-like system like this. After all, it’s in the name that it is for states rather than events.
It’s hard to suggest an alternative because your current code looks like you shouldn’t be using Flows at all. You could simply call a function that synchronously returns data that you use synchronously. I don’t know if your current code is a stepping stone towards something more complicated that really would be suitable for flows.

Kotlin random() always generates the same "random" numbers

I have created an app which should choose an image randomly from an array of images. On my Emulator Nexus 5X Android 5.1 everything works as expected. As soon as I try the same on my real device Galaxy Note 10 Lite I always get the same "random" numbers in same order. I first need to restart my phone to generate a new list of "random" numbers which is then always the same. Example: My array contains 200 elements, I open the app on my Galaxy and it chooses the following random number for the image ids: 43, 12, 176, 33, 2, 78. Then I close the app and I open the app again, now it has the exact same "random" numbers again: 43, 12, 176, 33, 2, 78. I need to restart my phone to get new random numbers, which will stay the same until I restart my phone again. On my emulator everything works fine and I get new random numbers always when I restart the app as expected.
Here is my full code of my app without array list of images:
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val imageList = arrayOf(Image(R.drawable.image1, false),
Image(R.drawable.image2, false),
Image(R.drawable.image3, false))
val imageViewMain = findViewById<ImageView>(R.id.imageViewMain)
loadNextImage(imageViewMain, imageList)
imageViewMain.setOnClickListener {
val dialogClickListener =
DialogInterface.OnClickListener { _, which ->
when (which) {
DialogInterface.BUTTON_POSITIVE -> {
loadNextImage(imageViewMain, imageList)
}
DialogInterface.BUTTON_NEGATIVE -> { }
}
}
val builder: AlertDialog.Builder = AlertDialog.Builder(this)
builder.setMessage("Nächstes Bild?").setPositiveButton("Ja", dialogClickListener)
.setNegativeButton("Nein", dialogClickListener).show()
}
}
private fun getNextChoice(): Int {
return (0..1).random()
}
private fun getNextImage(imageList: Array<Image>): Int {
val listSize = imageList.size
var imageId: Int
do {
imageId = (0 until listSize).random()
} while (imageList[imageId].played)
imageList[imageId].played = true
return imageList[imageId].image
}
private fun loadNextImage(imageViewMain: ImageView, imageList: Array<Image>) {
val imageQuestionmark = R.drawable.questionmark
val nextChoice = getNextChoice()
if (nextChoice == 0) {
imageViewMain.load(imageQuestionmark)
} else if (nextChoice == 1) {
imageViewMain.load(getNextImage(imageList))
}
Toast.makeText(this, "Bild hat geladen", Toast.LENGTH_SHORT).show()
}
}
Image:
data class Image(
val image: Int,
var played: Boolean
)
EDIT:
I tried what cactustictacs suggested in the comment and create a simple app, once with the kotlin random function and once with the java random function. here is the code I used:
Kotlin:
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.Toast
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val buttonTest = findViewById<Button>(R.id.buttonTest)
buttonTest.setOnClickListener {
val getRandomNumber = (0..999).random()
Toast.makeText(this, getRandomNumber.toString(), Toast.LENGTH_SHORT).show()
}
}
}
Java:
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Button;
import android.widget.Toast;
import java.util.Random;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button buttonTest = (Button) findViewById(R.id.buttonTest);
buttonTest.setOnClickListener(v -> {
int randomNumber = new Random().nextInt(999);
Toast.makeText(this, "" + randomNumber, Toast.LENGTH_SHORT).show();
});
}
}
on Kotlin I get the same behavior as with my inital problem, doesnt matter what I do with the app (I CAN EVEN UNINSTALL AND INSTALL AGAIN) I always get the same set of numbers. On Java its working as exptected, as soon as I close the app I get a new set of numbers. So the error definetly lays in kotlin.
Maybe it helps, my Android version is 12 and my phone Galaxy Note 10 Lite.
I once had this issue too. My solution is to use a seeded random object instead of calling the function Random.nextSomething(). Then I seed currentTimeInMillis as the seed.
var randomGenerator = Random(System.currentTimeMillis())
var result = randomGenerator.nextInt(30, 50)
This is quite a terrible implementation of Kotlin defaul Random class. Java Random class tries a lot to always use a different seed on every new instance whereas Kotlin hardcoded the same seed through the whole device. How can this be a default behavior for a Random implementation. It took me quite a lot to understand it.
See Java implementation:
public Random() {
this(seedUniquifier() ^ System.nanoTime());
}
private static long seedUniquifier() {
// L'Ecuyer, "Tables of Linear Congruential Generators of
// Different Sizes and Good Lattice Structure", 1999
for (;;) {
long current = seedUniquifier.get();
long next = current * 181783497276652981L;
if (seedUniquifier.compareAndSet(current, next))
return next;
}
}
private static final AtomicLong seedUniquifier
= new AtomicLong(8682522807148012L);
Aaaaaand here comes Kotlin one:
companion object Default : Random(), Serializable {
private val defaultRandom: Random = defaultPlatformRandom()
private object Serialized : Serializable {
private const val serialVersionUID = 0L
private fun readResolve(): Any = Random
}
private fun writeReplace(): Any = Serialized
override fun nextBits(bitCount: Int): Int = defaultRandom.nextBits(bitCount)
override fun nextInt(): Int = defaultRandom.nextInt()
override fun nextInt(until: Int): Int = defaultRandom.nextInt(until)
override fun nextInt(from: Int, until: Int): Int = defaultRandom.nextInt(from, until)
defaultRandom is a singleton always initiates with same seed...
(I took this code through the Android Studio sources...)
Note: So it was a bug on kotlin version 1.7.10 and Android api less than 33-34 something. Fixed on 1.7.20...
It looks like you're calling loadNextImage() from activity's onCreate(). That means that unless the activity is destroyed, it'll never re-generate the random IDs that you want. What happens if you force-stop the activity, and then relaunch it? I would expect that you get a new set of IDs.
If I'm right, and you want a new set of IDs every time you open the activity, then the solution is to call loadNextImage() from onResume() (and if you don't want it generated every time the activity is resumed, you'll need to include some logic that decides when to regenerate those IDs)

Saving CheckBox States When Switching Between Activities (Kotlin)

I'm making an Android app in Android Studio for my girlfriend to help her keep track of her tasks at work. The app has multiple to-do lists using RecyclerView. Since each checklist has their own activity, and she will be switching between them, how do I save which boxes have been checked so that they remained checked when switching between activities? Here is the RecyclerView adapter code for one of the to-do lists that also contains the Checkbox code.
package com.mattkalichman.coffee
import android.util.SparseBooleanArray
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.CheckBox
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.checkbox_row.view.*
class ThreeHourPullAdapter(var list: ArrayList<Data>) :
RecyclerView.Adapter<ThreeHourPullAdapter.ViewHolder>() {
private var titles =
arrayOf(
"Log into the handheld under Pull to Thaw.",
"Count anything left on pastry cart (BOH).",
"Count everything on front pastry cart (FOH).",
"Put remaining pastries from BOH on FOH cart. Remember to FIFO!",
"Pull from freezer to BOH cart. Adjust number of pulled items according to inventory.",
"When pull is done, press complete pull at the bottom of the screen.",
"Date all pastries with sticker gun by standards. (Marshmallow Dream Bars, Madelines, Chocolate Madelines, All Other Pastries)",
"Make sure to hang sticker gun back on cart & plug handheld back in."
)
var checkBoxStateArray = SparseBooleanArray()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val context = parent.context
val inflater = LayoutInflater.from(context)
val view = inflater.inflate(R.layout.checkbox_row, parent, false)
return ViewHolder(view)
}
override fun getItemCount(): Int = list.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.checkbox.isChecked = checkBoxStateArray.get(position, false)
holder.checkbox.text = titles[position]
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var checkbox: CheckBox = itemView.checkbox
init {
checkbox.setOnClickListener {
if (!checkBoxStateArray.get(adapterPosition, false)) {
checkbox.isChecked = true
checkBoxStateArray.put(adapterPosition, true)
} else {
checkbox.isChecked = false
checkBoxStateArray.put(adapterPosition, false)
}
}
}
}
}
Edit: I FIGURED IT OUT!! I'll post the code here in case anyone has the same problem I did, but this portion works perfectly now:
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var checkbox: CheckBox = itemView.checkbox
var sharedPreferences: SharedPreferences =
itemView.getContext().getSharedPreferences("ThreeHourPull", Context.MODE_PRIVATE)
// Create the SharedPreferences ^^^
val editor: SharedPreferences.Editor = sharedPreferences.edit()
// Create the editor ^^^
init {
for (i in 1..11) {
checkBoxStateArray[i - 1] =
sharedPreferences.getBoolean("Checkbox" + i, false)
}
// When launching the activity, the appropriate boxes will be checked ^^^
checkbox.setOnClickListener {
if (!checkBoxStateArray.get(adapterPosition, false)) {
checkbox.isChecked = true
checkBoxStateArray.put(adapterPosition, true)
} else {
checkbox.isChecked = false
checkBoxStateArray.put(adapterPosition, false)
}
editor.apply { // Save the states of each checkbox in the SharedPreferences
for (i in 1..11) {
editor.putBoolean("Checkbox" + i,
checkBoxStateArray[i - 1])
}
editor.apply()
}
}
}
}
You'll need to persist that data somehow, and restore it when the activity starts. There's lots of ways to do it - a modern approach would be something like a ViewModel handling the internal state (list items and whether they're checked) and pushing that to the UI layer (the fragment, setting those items on the adapter). And whenever you check a box, that event is sent to the VM so it can update the "what's checked" state, and save it to a database, SharedPreferences, DataStore etc.
But if you want to keep it simple, you could just use SharedPreferences and serialise the indices of the checked items. Store them in something like onStop, and restore them during setup, when you're providing data to the adapter. I already wrote an answer about this so I'll just link that, but if there's anything you're not sure of you can just ask here!
You can use SharedPreferences or the SQLite database.
You can also just use a Singleton object class (a static object) with all the ids checked.
HOW TO DECLARE SINGLETON IN KOTLIN
Or you can also pass the checked ids between activities using Intents.
Or you can use LiveData:
LINK

I want to make this boolean value true when I click this button. And pass it to a function to display something on screen

This is the code for the button
class NewGame : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_new_game)
val results = findViewById<TextView>(R.id.textView5)
val results2 = findViewById<TextView>(R.id.textView6)
val submit = findViewById<Button>(R.id.button7)
var hasclicked = false
submit.setOnClickListener {
hasclicked = true
}
game (result, hasclicked)
This is the code for the function
fun game(
result: TextView, hasclicked : Boolean ) {
if (hasclicked == true){
result.setText("correct")
}
But when I run this and click on the button nothing appears on screen. Can someone help me with this.
On clicking, you are only assigning true to boolean but not calling the game(result, hasclicked) function:
It should be like this:
submit.setOnClickListener {
hasclicked = true
game(result, hasclicked)
}
We get this error when we just set a value but do not try using it.
Here if you see your code, you are setting the value. But, not getting it.
By accessed it means that you are not getting it. Only setting it.
You can just choose to ignore it in some cases where it actually make sense,
What you can do is hover the affected area and press Alt + Enter in "Windows" or alt/option/⌥ + Enter in "Mac", and chose to suppress it.
You'll see,

Kotlin- Function runs after next lines ended

Updated- The code in the question works now
I'm trying to run a function after clicking on a button. The function updates an array and I want to run the next lines (The next lines transfer me to another activity) if the array isn't empty.
I tried to open the new activity within the filterPlaces function but with no success, startActivity and Intent don't work.
This is the function that updates the array:
var places = ArrayList<Place>() //Global place array
class MainActivity : AppCompatActivity() {
fun filterPlaces(types: ArrayList<String>, foods: ArrayList<String>, maxPrice: Int, maxProximity: Int) {
var typesList = types
val foodList = foods
if (types.isEmpty()) {
typesList = arrayListOf("Restaurant", "Hangouts")
if (foods.isEmpty()) {
foodList.add("Pizza")
}
}
val db = FirebaseFirestore.getInstance()
db.collection("places").get().addOnSuccessListener { result ->
for (document in result) {
val typeMatches = document.data["Type"].toString() in typesList
val foodMatches = document.data["Food"].toString() in foodList
var price = 0
when (document.data["Price"].toString()) {
"Very cheap" -> price = 0
"Cheap" -> price = 1
"Average" -> price = 2
"Far" -> price = 3
"Very far" -> price = 4
}
val priceMatches = price <= maxPrice
var proximity = 0
when (document.data["Proximity"].toString()) {
"Very close" -> proximity = 0
"Close" -> proximity = 1
"Far" -> proximity = 2
"Very far" -> proximity = 3
}
val proximityMatches = proximity <= maxProximity
if (typeMatches and foodMatches and priceMatches and proximityMatches) {
val place = Place(
document.data["Place"].toString(),
document.data["Type"].toString(),
document.data["Food"].toString(),
document.data["Price"].toString(),
document.data["Proximity"].toString()
)
places.add(place)
Log.d("name", "Place added successfully")
}
}
//Openning the results activity
if (places.isNotEmpty()) {
val i = Intent(this, RelevantPlaces::class.java)
val b = Bundle()
b.putParcelableArrayList("places", places)
i.putExtra("bundle", b)
startActivity(i)
}
}
.addOnFailureListener { exception ->
Log.d("name", "Error getting documents.")
}
}
This is the on click function:
fun onSortFilterClicked(view: View) {
if (places.isEmpty()) filterPlaces(types, foods, priceRange.progress, proximityRange.progress)
}
I want to run filterPlaces first, update the places array while I run it, and only than check if the array is still empty and if not open the new activity.
What actually happens is that it calls the filterPlaces but doesn't do it, instead it checks the places array (The if condition in the code) and only than goes into filterPlaces and run what's in it, resulted in me need to press twice on the button and only than the array has values.
I'm running this on Android Studio and I'm new at this Kotlin world and android developing in general.
Is there a solution for this? Either open the activity within the function or make the function run first?
What is happening?
An asynchronous request is made in filterPlaces, this is why the method itself returns immediately and passes control to the next code block.
How to fix this?
Move your code starting another Activity into the scope of your success listener. A better approach is to place this code into a separate method and just call it as needed.
Placed the filterPlace function outside the Main Activity class. Moved the function in the Main Activity class and it worked.

Categories

Resources