Trying to display multiple Toast messages one after another, but only the last toast message gets displayed. I tried using Thread.sleep, and handlers to buffer the messages, but neither worked. Any other tips? Here's my code:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_cow)
// updates the variables with these values
Submit.setOnClickListener {
cowName = Cow_Name.text.toString()
cowWeight = Cow_Weight.text.toString().toIntOrNull()
cowSex = Cow_Sex.text.toString()
cowAge = Cow_Age.text.toString()
showToast(cowName.toString())
showToast(cowWeight.toString())
showToast(cowSex.toString())
showToast(cowAge.toString())
}
}
private fun showToast(text: String)
{
Toast.makeText(this, text, Toast.LENGTH_SHORT).show()
handler.postDelayed ({
// Do nothing
}, 2000)
}
At minimum you should add a "delay" variable to "showToast" and move the Toast call to inside the posted Runnable:
private fun showToast(text: String, delayInMilliseconds: Int) {
val context = this
handler.postDelayed (
{ Toast.makeText(context, text, Toast.LENGTH_SHORT).show() },
delayInMilliseconds
)
}
Then you'd call it like
showToast(cowName.toString(), 0)
showToast(cowWeight.toString(), 1000)
showToast(cowSex.toString(), 2000)
showToast(cowAge.toString(), 3000)
Or whatever delay values were required to make that work.
Related
I want the text view to be updated every second or less in a dynamic random way and at the end of the loop the text view show the last random number.
I tried to add a sleep method but it did not work every time I click the button the text view show the last random number directly.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val rollButton: Button = findViewById(R.id.button1)
var result: TextView = findViewById(R.id.textView)
rollButton.setOnClickListener {
for (i in 1..10){
result.text = "${(1..6).random()}"
}
Toast.makeText(this, "Dice Rolled!", Toast.LENGTH_SHORT).show()
}
}
}
Coroutine delay is a way to do that, which is suggested in ltp's answer,
Another way is Runnable and Handler, an example function:
fun animateTextView(handler: Handler, textView: TextView, animate: Boolean = true) {
val runnable: Runnable = object : Runnable {
var randomNumber = 0
override fun run() {
randomNumber = (1..100).random()
textView.text = "$randomNumber"
handler.postDelayed(this, 500L)
}
}
// animate == false -> end text updates
if (animate) handler.postDelayed(runnable, 500L)
else handler.removeCallbacks(runnable)
}
Example use of the function:
private val textAnimaterHandler = Handler(Looper.getMainLooper())
animateTextView(textAnimaterHandler, binding.textView)
// Stop updates:
animateTextView(textAnimaterHandler, binding.textView, animate = false)
You can use Coroutine delay
rollButton.setOnClickListener {
CoroutineScope(Dispatchers.Main).launch {
//Disable button temporarily to prevent multiple clicks
it.isEnabled = false
for (i in 1..10) {
binding.textviewFirst.text = "${(1..6).random()}"
//One second delay before the next
delay(1000)
//If you want random delay - say 100ms to 1s
//delay((100L..1000L).random())
}
it.isEnabled = true
Toast.makeText(this#MainActivity, "Dice Rolled!", Toast.LENGTH_SHORT).show()
}
}
I am new to Android Development... Sorry for asking something so trivial. I don't know how to google this problem.
So let me explain:
I am doing the Android Development Course from Google and in the exercise you have a TextView that is repeatedly changed by an easy mathematical function 4 times after setContentViewis called. Between the changes Thread.sleep(1000) is called to delay the changes.
Expected behavior:
The Main Activity starts and you can see the TextView changing 4 times.
What actually happens:
The App start is delayed for as long as the calculations are set and then afterwards it will display the Main Activity with only the very last calculated result. In this case I would wait 4 seconds (with Thread.sleep(1000) being called 4 times) until the App is completely up and then you only see the result 60 because of 60 / 1.
This is the code:
private const val TAG = "MainActivity"
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
division()
}
private fun division() {
val numerator = 60
var denominator = 4
repeat(4) {
Thread.sleep(1000)
Log.v(TAG, "${numerator / denominator}")
val view: TextView = findViewById(R.id.division_textview)
view.setText("${numerator / denominator}")
denominator--
}
}
Thanks in advance. I hope someone knows why Google is suggesting this code, but it does not work on my machine.
You need to make a delay by Timer instead of Thread.sleep()
For training you can try something like this.
private val timerHandler = Handler(Looper.getMainLooper())
private val timerRunnable = Runnable {
denominator--
if(demominator != 0) {
//UI changes here
decrementTimer()
}
}
private fun decrementTimer() {
timerHandler.postDelayed(timerRunnable, DELAY_TIME)
}
If you need to start first run immediately use timerRunnable.run() in other case call decrementTimer().
Also would be helpful to control handler with removeCallbacks function when activity goes to background
You can wait in another thread so you don't block the main thread,
try this code it should work correctly, the code inside the lifecycleScope.launch will be moved to another thread so it will not block the UI:
private const val TAG = "MainActivity"
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
division()
}
private fun division() {
val numerator = 60
var denominator = 4
lifecycleScope.launch {
repeat(4) {
delay(1000)
Log.v(TAG, "${numerator / denominator}")
val view: TextView = findViewById(R.id.division_textview)
view.setText("${numerator / denominator}")
denominator--
}
}
}
Note: You need to be sure that you add the lifecycle dependency in you app gradle
dependencies {
...
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1'
...
}
Thanks for your answers everyone. Each of them helped me searching for the right topics. I liked the Handler Class the most and after some further searching, I came up with this solution:
private fun division() {
val numerator = 60
var denominator = 4
val handler = Handler(Looper.getMainLooper())
val divRunnable = object: Runnable {
override fun run() {
if (denominator != 0) {
Log.v(TAG, "${numerator / denominator}")
val view: TextView = findViewById(R.id.division_textview)
view.text = "${numerator / denominator}"
denominator--
handler.postDelayed(this, 1000)
}
}
}
handler.post(divRunnable)
}
This is working exactly as I wanted it to work.
I have created a variable private var deals=ArrayList<Deals>() in a fragment and I have set a click listener in the onCerateView() like the following.
binding.tvAllDeals.setOnClickListener {
viewAllDeals()
}
So that it will trigger the following method
private fun viewAllDeals(){
val intent = Intent(context,ViewAllDealsActivity::class.java)
intent.putExtra("details",deals)
Log.d("Tag2", "Size is ${deals.size}")
startActivity(intent)
}
I have the following function to get the data from the firestore and then I save the result in the variable 'deals'. However, whenever I click the 'tvAllDeals' it shows many images, when I check the size of the 'deals' using Log.d 'Tag1' always shows the correct size, which is 3, whereas 'Tag2' show some random numbers like 6, 9, 24. I try to find out why this is happening but I didn't get any idea. The variable 'deals' is not used anywhere else other than declaring and initializing, to assign the value and to pass it in the 'viewAllDeals()'
private fun getDeals() {
FirestoreClass().getDeals(
onSuccess = { list ->
Result.success(list)
successDeals(list) ///// THIS FUNCTION WILL SHOW THE IMAGES IN A VIEWPAGER
deals.clear()
deals=list
Log.d("Tag1", "Size is ${deals.size}")
},
onFailure = {
}
)
}
Edit:
NOTE: 'Tag3' also shows correct array size like 'Tag1'. However,
private fun successDeals(list: ArrayList<Deals>) {
Log.d("Tag3", "Size is ${deals.size}")
if (list.size > 0) {
binding.vpDeals.visibility = View.VISIBLE
val adapter = DealsAdapter(binding.vpDeals,requireContext(), list)
binding.vpDeals.adapter = adapter
binding.vpDeals.orientation = ViewPager2.ORIENTATION_HORIZONTAL
sliderHandle= Handler()
sliderRun= Runnable {
binding.vpDeals.currentItem=binding.vpDeals.currentItem+1
}
binding.vpDeals.registerOnPageChangeCallback(
object :ViewPager2.OnPageChangeCallback(){
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
sliderHandle.removeCallbacks(sliderRun)
sliderHandle.postDelayed(sliderRun,4000)
}
}
)
} else {
binding.vpDeals.visibility = View.GONE
}
}
So im creating a health app and one of the features is to get words of inspiration. So on a button click it will display a random line from an array. So on the click it does display one quote but it doesnt update to a new quote every time you click. Am i using the wrong function or is there something im missing?
public override fun onStart() {
super.onStart()
val Words : Array<String> = arrayOf("Dont give up", "Cry daily", "Just do it")
val RandomInt = Random.nextInt(2)
PowerTxt.text = ""
NextWordBtn.setOnClickListener()
{
PowerTxt.text = ""
PowerTxt.append(Words [RandomInt])
}
}
The problem is that RandomInt never changes. You only assign it once, before setting the listener.
If you want to generate random index each time you click simply do:
public override fun onStart() {
super.onStart()
val Words = arrayOf("Dont give up", "Cry daily", "Just do it")
NextWordBtn.setOnClickListener() {
PowerTxt.text = Words[Random.nextInt(2)]
}
}
Please try below:
public override fun onStart() {
super.onStart()
val Words : Array<String> = arrayOf("Dont give up", "Cry daily", "Just do it")
PowerTxt.text = ""
NextWordBtn.setOnClickListener()
{
val RandomInt = Random.nextInt(2)
PowerTxt.text = ""
PowerTxt.append(Words [RandomInt])
}
}
I was an idiot and didnt put the random int inside the button click so it was being updated just with the same value
public override fun onStart() {
super.onStart()
val Words : Array<String> = arrayOf("Dont give up", "Cry daily", "Just do it")
PowerTxt.text = ""
NextWordBtn.setOnClickListener()
{
PowerTxt.text = ""
val RandomInt = Random.nextInt(2)
PowerTxt.append(Words [RandomInt])
}
}
I'm just starting with Kotlin to make Android Apps (it's my first programming language) and I'm using a CountDownTimer. I want to access the p0 parameter globally, but I don't know how to. Here's the code:
class MainActivity : AppCompatActivity() {
var score: Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var timeTimer = object : CountDownTimer(10000, 1000) {
override fun onFinish() {
timeView.text = "Time's up!"
}
override fun onTick(p0: Long) {
timeView.text = "Time's left: " + p0/1000
}
}.start()
}
fun point (view: View) {
score++
p0 = p0 + 1000
scoreView.text = "Score: $score"
}
}
Function point is called by clicking a button. The thing I'm trying to achieve is making CountDownTimer longer by 1 second every time a user clicks a button.
This line p0 = p0 + 1000 obviously won't work because p0 is inside another code block. Is there any way to make it globally accessible? I've thought about putting CountDownTimer outside onCreate and just starting it in the onCreate, but I think it still wouldn't work as p0 is still inside CountDownTimer code block.
var timeTimer = object : CountDownTimer(10000, 1000) {
override fun onFinish() {
timeView.text = "Time's up!"
}
override fun onTick(p0: Long) {
timeLeft = p0 //timeLeft is global --only way I think to keep track remaining time.
timeView.text = "Time's left: " + p0/1000
}
}.start()
This should be global (better at the bottom after all methods of the Activity)
fun resetTimer(time){
timeTimer.cancel()
timeTimer = object : CountDownTimer(time, 1000) {
override fun onFinish() {
timeView.text = "Time's up!"
}
override fun onTick(p0: Long) {
timeLeft = p0
timeView.text = "Time's left: " + p0/1000
}
}.start()
}
So finally
fun point (view: View) {
score++
resetTimer(timeLeft + 1000)
scoreView.text = "Score: $score"
}
I don't know what's the reach of your App, but you can do a couple of things:
Create a custom Application class for your App and check the variable there. You need to create a class that extends from Application and add that class into your manifest, in the <application/> tag under the android:name attribute. You can access your custom Application class from anywhere by using it like a Singleton, for example MyApp.getInstance().setPvalue(someIntValue).
Maybe you want to save that value in your SharedPreferences so It's kept saved if you close the App.
Maybe you want to have that value in a Service, so if you close the App the timer is still going to be counting down.
I answer a pretty similar question the other day:
How to keep the countDownTimer counts after i swap the activity?
Hope it helps.