This is my first post and I am brand new to coding so please let me know if I've missed anything for getting some help.
I'm taking the Google Android Dev tutorials. The tutorial is walking me through creating a dice roll app. I completed that and for an extra challenge practice at the end it recommends getting two results from one button click.
I tried doing that in this code:
package com.example.diceroller
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val rollButton: Button = findViewById(R.id.button)
rollButton.setOnClickListener { rollDice() }
rollButton.setOnClickListener { rollDice2() }
}
private fun rollDice() {
val dice = Dice(6)
val diceRoll = dice.roll()
val resultTextView: TextView = findViewById(R.id.textView)
resultTextView.text = diceRoll.toString()
}
private fun rollDice2() {
val dice2 = Dice2(6)
val diceRoll2 = dice2.roll2()
val resultTextView: TextView = findViewById(R.id.textView2)
resultTextView.text = diceRoll2.toString()
}
}
class Dice(private val numSides: Int) {
fun roll(): Int {
return (1..numSides).random()
}
}
class Dice2(private val numSides: Int) {
fun roll2(): Int {
return (1..numSides).random()
}
}
I don't get any errors, but when I run the app it only shows one result (the second result). Again, I'm new to all this and maybe I'll learn it later, but looking for some help on why it only spits out one result. Any help is greatly appreciated and thank you in advance.
It's not made clear by the documentation for setOnClickListener, but a View (which a Button is a type of) can only have one click listener. So when you do this:
rollButton.setOnClickListener { rollDice() }
rollButton.setOnClickListener { rollDice2() }
you're setting a listener that calls rollDice(), and then replacing it with another one that calls rollDice2(). You need to do everything in a single listener!
rollButton.setOnClickListener {
rollDice()
rollDice2()
}
so when you click the button, it'll run the code in that lambda function you're passing in as your listener, so it'll call rollDice() and then rollDice2().
As a general rule, if a function is named like setListener, with a set, then it's usually a single listener that you can set (or unset, usually with null). If it's named something like addListener, with an add, that implies you can add more to what's already there, i.e. you can have multiple listeners or whatever.
I'm not saying this will always be true (always check the documentation, or the source code if you can! See what it's actually doing) but it's a good rule of thumb in my experience - but you should always check if you're unsure!
You can also achieve the same result by rolling the same dice twice
package com.example.diceroller
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val rollButton: Button = findViewById(R.id.button)
// setOnClickListner -> defines what to execute on button click
rollButton.setOnClickListener { rollDice() }
}
private fun rollDice() {
// create two dice's each with '6' sides
var dice_1 = Dice(6)
var dice_2 = Dice(6)
// roll the two dice's
val dice_1_roll = dice_1.roll()
val dice_2_roll = dice_2.roll()
// bind the obtained result to the corresponding 'textView'
val resultTextView_1: TextView = findViewById(R.id.textView)
val resultTextView_2: TextView = findViewById(R.id.textView)
// fun roll() in Dice: Class return 'Int' so convert into 'String'
resultTextView_1.text = dice_1_roll.toString()
resultTextView_2.text = dice_2_roll.toString()
}
}
class Dice(private val numSides: Int) {
fun roll(): Int {
return (1..numSides).random()
}
}
package com.example.diceroller
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
/**
* This activity allows the user to roll a dice and view the result
* on the screen.
*/
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val rollButton: Button = findViewById(R.id.button)
rollButton.setOnClickListener { rollDice() }
}
/**
* Roll the dice and update the screen with the result.
*/
private fun rollDice() {
// Create new Dice object with 6 sides and roll it
val myFirstDice = Dice(6)
val diceRollFirst = myFirstDice.roll()
// Update the screen with the dice roll
val resultTextView: TextView = findViewById(R.id.textView)
resultTextView.text = diceRollFirst.toString()
val mySecondDice = Dice(6)
val diceRollSecond = mySecondDice.roll()
val resultTextView2: TextView = findViewById(R.id.textView2)
resultTextView2.text = diceRollSecond.toString()
}
class Dice(private val numSides: Int) {
fun roll(): Int {
return (1..numSides).random()
}
}
}
Related
I have 2 activities with similar UI layouts, which contain some TextViews in the same place, that receive some text. I want to avoid writing this code twice, so I would like to create a class that will do the writing for both activities. The problem is that I need to pass the ViewBinding pointer to this class and then based on the type write either to Activity1 or Activity2. How can I do this?
Here is a solution that works but I am having to write the same code twice.
Assume there are three TextViews.
// Activity1
class MainActivity : AppCompatActivity(), UiCommon {
private lateinit var uib: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
uib = ActivityMainBinding.inflate(layoutInflater)
setContentView(uib.root)
// write common part
val draw = DrawUiCommon(uib)
draw.draw("a1_text1", "a1_text2", "a1_text3")
}
}
// Activity2
class MainActivity2 : AppCompatActivity(), UiCommon {
lateinit var uib: ActivityMain2Binding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
uib = ActivityMain2Binding.inflate(layoutInflater)
setContentView(uib.root)
// write common part
val draw = DrawUiCommon(uib)
draw.draw("a2_text1", "a2_text2","a3_text3")
}
}
// Common part
class DrawUiCommon(val pt: androidx.viewbinding.ViewBinding){
fun draw(t1: String, t2: String, t3: String){
if (pt is ActivityMainBinding){
pt.textView1.text = t1
pt.textView2.text = t2
pt.textView3.text = t3
}
else if (pt is ActivityMain2Binding){
pt.textView1.text = t1
pt.textView2.text = t2
pt.textView3.text = t3
}
}
}
As #sashabeliy said, if the ui is exactly the same and the only difference is the data to show, then you can receive the extra data in the intent. Is possible to create a method to do the navigation:
companion object {
private const val ARG_TEXT_LIST = "ARG_TEXT_LIST"
fun navigate(context: Context, data: Array<String>) {
val intent = Intent(context, MainActivity::class.java).apply {
putExtra(ARG_TEXT_LIST, data)
}
context.startActivity(intent)
}
}
And then you can fetch the data in the onCreate lifecycle to populate the views:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(yourBinding.root)
val textList = intent.getStringArrayExtra(ARG_TEXT_LIST)
yourBinding.apply {
textView1.text = textList[0]
textView2.text = textList[1]
textView3.text = textList[2]
}
}
You didn't show your layout code, but based on your description, it sounds like you have some views in each activity layout that are the same.
Step 1 - extract those common views into it's own layout that you can resuse.
Step 2 - Reference the included layout:
// Activity2
class MainActivity2 : AppCompatActivity(), UiCommon {
lateinit var uib: ActivityMain2Binding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
uib = ActivityMain2Binding.inflate(layoutInflater)
setContentView(uib.root)
// write common part
val draw = DrawUiCommon(uib.includedLayout) // <-- Use included layout that has the comment views
draw.draw("a2_text1", "a2_text2","a3_text3")
}
}
// Common part
class DrawUiCommon(val pt: IncludedLayoutViewBinding ){ // <---Now we know the type
fun draw(t1: String, t2: String, t3: String){
//if (pt is ActivityMainBinding){ // <-- No if check needed
pt.textView1.text = t1
pt.textView2.text = t2
pt.textView3.text = t3
//}
//else if (pt is ActivityMain2Binding){ // <-- No branch needed
// pt.textView1.text = t1
// pt.textView2.text = t2
// pt.textView3.text = t3
//}
}
}
OPTIONAL BONUS:
Create a Custom View that encapsulates this common layout and behavior.
Sample activity layout:
<LinearLayour>
<MyCustomView>
// OTHER THINGS
</LinearLayour>
Sample Activity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
uib = ActivityMain2Binding.inflate(layoutInflater)
setContentView(uib.root)
// write common part
uib.myCustomView.setValues("a2_text1", "a2_text2","a3_text3")
}
Sample Custom View:
class MyCustomView(...) {
// Important init stuff up here ...
fun setValues(t1: String, t2: String, t3: String) {
// Custom View creates and has its own binding
binding.textView1.text = t1
binding.textView2.text = t2
binding.textView3.text = t3
}
}
I am new to kotlin and I am trying an execise.
I want to calculate the tip as % of the cost of the service.I enter my cost of service in an EditText... so far so good.
now I want to convert this the value I entered to double, which the datatype I need for my calculation.
this is my code:
package com.example.tiptime2
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.example.tiptime2.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.calculate.setOnClickListener { calculateTip() }
}
fun calculateTip(){
val myTextString= binding.costOfService.text.toString()
val myCost = myTextString.toString()
val cost = myCost.toDouble()
}
}
androidStudio doesn't recognize toDouble()
why??
You're casting to .toString() twice and you need to update the data-binding. Roughly alike:
val cost = Double.parseDouble(binding.costOfService.text.toString())
// some calculations
binding.costOfService.setText(String.valueOf(cost))
Instead of handling single values, it's common to bind a view-model, which has all of them.
I suggest you explicit define variables type for debugging
fun calculateTip() {
val costText: String = binding.costOfService.text.toString()
val cost: Double = costText.toDouble()
}
Still your code looks valid!
This is my MainActivity.kt code
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val rollButton : Button= findViewById(R.id.roll_button)
rollButton.setOnClickListener {
rollDice()
}
}
private fun rollDice() {
val resultText: TextView = findViewById(R.id.result_text)
val randomInt = Random().nextInt(6) + 1
resultText.text = randomInt.toString()
}
}
In this code, this is the error
Cannot create an instance of an abstract class
This error is for Random() I also imported import kotlin.random.Random
If someone could please help me?
Kotlin random is
Random
So Random.nextInt() will fix it.
If you go to the import and then click ctrl+b or cmd+b it will take you to the Random file. Scroll down and you will find the companion object, there all of the methods are exposed for simplicity. So you are basically saying Random file dot use the companion object method nextInt()
https://stackoverflow.com/a/60124584/10012330 <-- check here
If you want to use the Kotlin Random class then use like this. with below import.
import kotlin.random.Random
val random = Random(12)
The problem at hand is pretty straight forward I just have a button that generates and displays a number 1-9. This consists of a button, a seek bar and a text view.
I'd like to hide these activities once a 9 is rolled, but I'm not sure how to toggle the visibility of the button, seek bar and text view.
val rollButton = findViewById<Button>(R.id.rollButton)
val resultsTextView = findViewById<TextView>(R.id.resultsTextView)
val seekBar = findViewById<SeekBar>(R.id.seekBar)
seekBar.setVisibility(View.INVISIBLE)
rollButton.setVisibility(View.INVISIBLE)
resultsTextView.setVisibility(View.INVISIBLE)
package com.example.randomizer
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.SeekBar
import android.widget.TextView
import android.widget.VideoView
import java.util.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val rollButton = findViewById<Button>(R.id.rollButton)
val resultsTextView = findViewById<TextView>(R.id.resultsTextView)
val seekBar = findViewById<SeekBar>(R.id.seekBar)
seekBar.setVisibility(View.INVISIBLE)
rollButton.setVisibility(View.INVISIBLE)
resultsTextView.setVisibility(View.INVISIBLE)
rollButton.setOnClickListener {
val rand = Random().nextInt(seekBar.progress)
resultsTextView.text = rand.toString()
if rand == 9)
}
}
}
Just use if else to toggle view visibility
fun View.toggleVisibility() {
if (this.isVisible()) {
this.visibility = View.INVISIBLE
} else {
this.visibility = View.VISIBLE
}
}
then call this function
seekBar.toggleVisibility()
val rollButton = findViewById<Button>(R.id.rollButton)
val resultsTextView = findViewById<TextView>(R.id.resultsTextView)
val seekBar = findViewById<SeekBar>(R.id.seekBar)
seekBar.setVisibility(View.INVISIBLE)
rollButton.setVisibility(View.INVISIBLE)
resultsTextView.setVisibility(View.INVISIBLE)
by the way you dont need to do fineViewById in kotlin, you can import them directly with synthetics.
fun hideViews() {
rollButton.visibility = View.GONE
...
...
}
fun showViews() {
rollButton.visibility = View.VISIBLE
...
...
}
I have 4 fragments that all use the same code with the exception of a few variables being different.
the variables differ in name and then there are functions that call those variables. but all the rest of the code is copy and pasted. 99% the same.
How do i properly create a class that other classes can inherit from and override the names of those 2-3 variables and function calls
for example:
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView
import kotlinx.android.synthetic.main.activity_multiplication_game.*
class MultiplicationGame : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_multiplication_game)
supportActionBar!!.hide() // REMOVES ACTION BAR
val prefs = this.getSharedPreferences("com.QuickMaths", MODE_PRIVATE) // preference to keep track of ad number
val insertPoint = insert_point as ViewGroup
val ed: TextView = editTextAnswers as TextView
val context: Context = this#MultiplicationGame
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
// gets the number of rows and digits. Now implement the game
val rowNumber = intent.getStringExtra("rowNumber").toInt()
val digitNumber = intent.getStringExtra("digitNumber").toInt()
var numberOfQuestions = intent.getStringExtra("NumberOfQuestions").toInt()
val atLeastOrAtMostDigits = intent.getStringExtra("NumberOfAtLeastDigits").toInt()
val atLeastOrAtMostRows = intent.getStringExtra("NumberOfAtLeastRows").toInt()
//Main menu button
mainMenu.setOnClickListener {
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
}
val game = CreateLayout(rowNumber, digitNumber, insertPoint, context, atLeastOrAtMostDigits,atLeastOrAtMostRows)
Log.d("poopAdditionGame", atLeastOrAtMostDigits.toString())
val timeKeeper = KeepTime()
game.generateViewsWithNumbers(2)
//gets the number of questions for the counter that sits on top
val totalQuestions = numberOfQuestions
showQuestionsLeft.text = numberOfQuestions.toString() + "/" + totalQuestions.toString()
//checks the answers to see if they are correct
ed.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
val aftertextchanged = ed.text.toString()
if (game.totalProduct.toString() == aftertextchanged) {
//things happen here
}
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
//To change body of created functions use File | Settings | File Templates.
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
//Nothing
}
})
}
// the keyboard
fun numberButton(view: View) {
val buttonClicked: Button = view as Button
val viewtext: TextView = editTextAnswers as TextView
var buttonClickedValue = viewtext.text.toString()
when (buttonClicked.id) {
button8.id -> buttonClickedValue += "1"
button7.id -> buttonClickedValue += "2"
button6.id -> buttonClickedValue += "3"
button9.id -> buttonClickedValue += "4"
button10.id -> buttonClickedValue += "5"
button11.id -> buttonClickedValue += "6"
button12.id -> buttonClickedValue += "7"
button13.id -> buttonClickedValue += "8"
button14.id -> buttonClickedValue += "9"
button16.id -> buttonClickedValue += "0"
}
viewtext.text = buttonClickedValue
}
fun clearButton(view: View) {
val viewtext: TextView = editTextAnswers as TextView
viewtext.text = ""
}
fun delButton(view: View) {
val viewtext: TextView = editTextAnswers as TextView
var mutList = viewtext.text.split("")
viewtext.text = mutList.toMutableList().dropLast(2).joinToString("")
}
}
this code is the same exact code for 4 different activities EXCEPT the variable totalSum is changed to product and difference. and game.generateViewsWithNumbers(2) 2 should be a 1 or 3 depending on the activity
the point being how can i recycle the code with just the small differences in mind and change those.
You just need to create a base fragment/activity.
abstract class MultiplicationGame : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_multiplication_game)
supportActionBar!!.hide() // REMOVES ACTION BAR
val prefs = this.getSharedPreferences("com.QuickMaths", MODE_PRIVATE) // preference to keep track of ad number
//Rest of the code
And implement it in the activities that will be using the same code. i didn't understand 100% what you need to be different regarding totalSum, because i couldn't find one it in your code.
Regarding this:
game.generateViewsWithNumbers(2)
If you just need a int value in each activity you can do something like this:
abstract intValue: Int
game.generateViewsWithNumbers(intValue)
And then the child activity just needs to give a value to the variable.