Kotlin Change ViewText with an ID provided by a String - android

Goal: To get a ViewText resource and edit it from an activity, using a mutable string (because then the string can be changed to alter other ViewTexts in the same function).
Context: I'm making a grid using TableRows and TextViews that can be altered to form a sort of map that can be generated from an array.
Issue: The binding command does not recognise strings. See my comment "PROBLEM HERE".
Tried: getResources.getIdentifier but I've been told that reduces performance drastically.
An excerpt from gridmap.xml
<TextView
android:id="#+id/cell1"/>
GridMap.kt
package com.example.arandomadventure
import android.R
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.arandomadventure.databinding.GridmapBinding
class GridMap : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//sets the binding and assigns it to view
val binding: GridmapBinding = GridmapBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
//creates a string variable
var cellID = "cell1"
//uses binding to set the background colour to teal
binding.cellID.setBackgroundResource(R.color.teal_200) //<- PROBLEM HERE (Unresolved Reference)
//getResources.getIdentifier is not an option as it degrades performance on a larger scale
}
}

A binding object is just an autogenerated class, whose class members are defined by the views in your layout XML. You can't add or access a field on a class with the syntax you showed - binding classes are no different from any other class. If you wanted to be able to access them by name, you could load them into a map
val viewMap = mapOf(
"cell1" to binding.cell1,
"cell2" to binding.cell2,
"cell3" to binding.cell3
)
then you can use the map to access them by name
var cellID = "cell1"
viewMap[cellID].setBackgroundResource(R.color.teal_200)
If you want the map to be a class member, you can set it like this
private lateinit var viewMap: Map<String,View>
override fun onCreate(savedInstanceState: Bundle?) {
//...
viewMap = mapOf(
"cell1" to binding.cell1,
"cell2" to binding.cell2,
"cell3" to binding.cell3
)
}
If your layout has hundreds of views and this becomes cumbersome, you may want to consider adding the views programmatically instead.
Edit
If you want to do this a more ugly, but more automatic way you can use reflection. To do this you need to add this gradle dependency:
implementation "org.jetbrains.kotlin:kotlin-reflect:1.7.0"
then you can build up the map programmatically with all views in the binding.
val viewMap = mutableMapOf<String,View>()
GridmapBinding::class.members.forEach {
try {
val view = it.call(binding) as? View
view?.let { v ->
viewMap[it.name] = v
}
}
catch(e: Exception) {
// skip things that can't be called
}
}
Or you can use this to call a method (keep in mind this will throw if no such class member exists):
var cellID = "cell1"
val view = GridmapBinding::class.members.filter { it.name == cellID }[0].call(binding)

Related

Can't change the value of MutableList in a Kotlin class

I want to add the random generated integer into my MutableList in Player class when I use the random integer generator method located in Player class in my fragment then I want to pass this MutableList to Fragment with using Livedata(I'm not sure if i'm doing right with using livedata).Then show the MutableList in TextView.But MutableList returns the default value not after adding.
So what am i doing wrong ? What should i do ?
Thank you
MY CLASS
open class Player {
//property
private var playerName : String
private var playerHealth : Int
var playerIsDead : Boolean = false
//constructor
constructor(playerName:String,playerHealth:Int){
this.playerName = playerName
this.playerHealth = playerHealth
}
var numberss: MutableList<Int> = mutableListOf()
fun attack(){
//Create a random number between 1 and 10
var damage = (1..10).random()
//Subtract health points from the opponent
Log.d("TAG-DAMAGE-WRITE","$damage")
numberss.add(damage)
Log.d("TAG-DAMAGE-ADD-TO-LIST","$numberss")
Log.d("TAG-NUMBER-LIST-LATEST-VERSION","$numberss")
}
}
MY FRAGMENT
class ScreenFragment : Fragment() {
var nickname : String? = null
private lateinit var viewModel : DenemeViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_screen, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(this).get(DenemeViewModel::class.java)
arguments?.let {
nickname = ScreenFragmentArgs.fromBundle(it).nickName
}
sFtextView.text = "Player : $nickname"
action()
}
private fun action(){
val health1 = (1..50).random()
val health2 = (1..50).random()
val superMan = Superman("Dusman",health1,)
while(superMan.playerIsDead == false){
//attack each other
superMan.attack()
sFtextsonuc.text = "Superman oldu"
viewModel.setData()
observeLiveData()
superMan.playerIsDead = true
}
}
fun observeLiveData(){
viewModel.damageList.observe(viewLifecycleOwner, Observer { dmgList ->
dmgList?.let {
sFtextsonuc.text = it.toString()
Log.d("TAG-THE-LIST-WE'VE-SENT-TO-FRAGMENT","$it")
}
})
}
}
MY VIEWMODEL
class DenemeViewModel : ViewModel() {
val damageList:MutableLiveData<MutableList<Int>> = MutableLiveData()
fun setData(){
damageList.value = Superman("",2).numberss
Log.d("TAG-VIEWMODEL","${damageList.value}")
}
}
MY LOG
PHOTO OF THE LOGS
Your Superman is evidently part of some ongoing game, not something that should be created and then destroyed inside the action function. So you need to store it in a property. This kind of state is usually stored in a ViewModel on Android so it can outlive the Fragment.
Currently, you are creating a Superman in your action function, but anything created in a function is automatically sent do the garbage collector (destroyed) if you don't store the reference in a property outside the function.
Every time you call the Superman constructor, such as in your line damageList.value = Superman("",2).numberss, you are creating a new instance of a Superman that has no relation to the one you were working with in the action() function.
Also, I recommend that you do not use LiveData until you fully grasp the basics of OOP: what object references are, how to pass them around and store them, and when they get sent to the garbage collector.
So, I would change your ViewModel to this. Notice we create a property and initialize it with a new Superman. Now this Superman instance will exist for as long as the ViewModel does, instead of only inside some function.
class DenemeViewModel : ViewModel() {
val superman = Superman("Dusman", (1..50).random())
}
Then in your frgament, you can get this same Superman instance anywhere you need to use it, whether it be to deal some damage to it or get the current value of its numberss to put in an EditText.
Also, I noticed in your action function that you have a while loop that repeatedly deals damage until Superman is dead. (It also incorrectly observes live data over and over in the loop, but we'll ignore that.) The problem with this is that the while loop is processed to completion immediately, so you won't ever see any of the intermediate text. You will only immediately see the final text. You probably want to put some delays inside the loop to sort of animate the series of events that are happening. You can only delay easily inside a coroutine, so you'll need to wrap the while loop in a coroutine launch block. In a fragment when working with views, you should do this with viewLifecycleOwner.lifecycleScope.launch.
Finally, if you set playerIsDead to true on the first iteration of the loop, you might as well remove the whole loop. I'm guessing you want to wrap an if-condition around that line of code. But since your code above doesn't modify player health yet, there's no sensible way to determine when a player should be dead, so I've commented out the while loop
private fun action() = viewLifecycleOwner.lifecycleScope.launch {
val superMan = viewModel.superman
//while(superMan.playerIsDead == false){
//attack each other
superMan.attack()
sFtextsonuc.text = "Superman oldu"
delay(300) // give user a chance to read the text before changing it
sFtextsonuc.text = superMan.numberss.joinToString()
delay(300) // give user a chance to read the text before changing it
// TODO superMan.playerIsDead = true
//}
}

How can I iterate over all views known to the data binder?

I have three TextInputEditText views in my layout where the user can type in specific information.
On the click of a Button this information is stored in my database.
After the user clicks this Button, I want to clear all TextInputEditText fields.
Right now, I am doing this by hardcoding:
private fun clearAllEditTextFields() {
Timber.d("clearAllEditTextFields: called")
binding.bookTitleEditText.text = null
binding.bookAuthorEditText.text = null
binding.bookPageCountEditText.text = null
}
Since this is bad, I would like to use a dynamic for each loop to identify all views of type TextInputEditText known to binding and clear their content:
private fun clearAllEditTextFields() {
Timber.d("clearAllEditTextFields: called")
for (view in binding.views) {
if (view is TextInputEditText) {
view.text = null
}
}
Unfortunately, there is no such field binding.views.
Is there still a way to achieve this or something with the same properties?
What I have tried so far
I have used a BindingAdapter. In my Util class, where all my extension functions go, I have created an EditText extension function clearText annotated as BindingAdapter and JvmStatic:
#JvmStatic
#BindingAdapter("clearText")
fun EditText.clearText(#NotNull shouldClear: Boolean) {
Timber.d("clearText: called")
if (shouldClear) text = null
}
In XML:
<com.google.android.material.textfield.TextInputEditText
android:id="#+id/book_title_edit_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:imeActionId="100"
android:imeOptions="actionNext"
android:inputType="text"
android:text="#={viewModel.bookTitle}"
app:clearText="#{viewModel.clearAllEditTextFields}"
/>
In my ViewModel class, I have created a var clearAllEditTextFields = false which is modified in the clearAllEditTextFields() function which gets called inside my ViewModel:
...
var clearAllEditTextFields = false
clearAllEditTextFields()
...
private fun clearAllEditTextFields() {
Timber.d("clearAllEditTextFields: called")
clearAllEditTextFields = true
}
According to Logcat, my extension function is called when my ViewModel is initialized. However, when clearAllEditTextFields() gets called, it does not trigger a new call to the extension function.
A simple for loop doesn't exist to loop over the views in the binding object and you can try the following to keep your code conscice.
Scope Functions
binding.apply{
bookTitleEditText.text = null
bookAuthorEditText.text = null
bookPageCountEditText.text = null
}
scope functions are a good go iff there are few views and we end up with quite a boiler-plate code if the number of views is large, in which cases I think Binding-Adapter would be a good choice
#BindingAdapter("clear_text")
fun EditText.clearText(shouldClear : Boolean?){
shouldClear?.apply{
if(shouldClear)
text = null
}
}
ViewModel
private val _shouldClear = MutableLiveData<Boolean>()
val shouldClear : LiveData<Boolean>
get() = _shouldClear
fun setClearStatus(status : Boolean){
_shouldClear.value = status
}
//since clearing a text is an event and not state, reset the clear_status once it's done
fun resetClearStatus(){
_shouldClear.value = nul
}
XML
<EditText
......
app:clear_text = "#{yourViewModel.shouldClear}"
...... />
ActivityClass
...
binding.lifecycleOwner = this
...
private fun clearAllEditTextFields() {
yourViewModel.setClearStatus(true)
yourViewModel.resetClearStatus()
}
Edit:
add binding.lifecycleOwner = this in your activity class and its used for observing LiveData with data binding. The view will observe for text changes at runtime.
Create a linearlayout (or similar) called, for example, text_fields_linear layout enclosing all of your textfields. then do:
private fun clearAllEditTextFields() {
for (item in binding.textFieldsLinearLayout) {
item.text = null
}
}

AirBnb Epoxy - Views are duplicated instead of replaced

I am rendering a form based on JSON response that I fetch from the server.
My use case involves listening to a click from a radio button, toggling the visibility of certain text fields based on the radioButton selection, and refreshing the layout with the visible textView.
The expected output should be to update the same view with the textView now visible, but I'm now seeing the same form twice, first with default state, and second with updated state.
Have I somehow created an entirely new model_ class and passing it to the controller? I just want to change the boolean field of the existing model and update the view.
My Model Class
#EpoxyModelClass(layout = R.layout.layout_panel_input)
abstract class PanelInputModel(
#EpoxyAttribute var panelInput: PanelInput,
#EpoxyAttribute var isVisible: Boolean,
#EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var context: Context,
#EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var textChangedListener: InputTextChangedListener,
#EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var radioButtonSelectedListener: RadioButtonSelectedListener,
#EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var validationChangedListener: ValidationChangedListener
) : EpoxyModelWithHolder<PanelInputModel.PanelInputHolder>() {
#EpoxyAttribute var imageList = mutableListOf<ImageInput>()
override fun bind(holder: PanelInputHolder) {
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
generateViews(holder, inflater, panelInput.elements) // Generates textViews, radioButtons, etc, based on ElementType enum inside Panel input
}
fun generateRadioButtonView(element: Element) {
// Created a custom listener and calling its function
radioButtonSelectedListener.radioButtonSelected(chip.id, chip.text.toString())
}
fun generateTextView() {
// Show/hide textView based on isVisible value
}
My Controller Class
class FormInputController(
var context: Context,
var position: Int, // Fragment Position in PagerAdapter
var textChangedListener: InputTextChangedListener,
var radioButtonSelectedListener: RadioButtonSelectedListener,
var validationChangedListener: ValidationChangedListener
) : TypedEpoxyController<FormInput>() {
override fun buildModels(data: FormInput?) {
val panelInputModel = PanelInputModel_(
data as PanelInput,
data.isVisible,
context,
textChangedListener,
radioButtonSelectedListener,
validationChangedListener
)
panelInputModel.id(position)
panelInputModel.addTo(this)
}
}
My fragment implements the on radio button checked listener, modifies the formInput.isVisible = true and calls formInputController.setData(componentList)
Please help me out on this, thanks!
I don't think you are using Epoxy correctly, that's not how it's supposed to be.
First of all, let's start with the Holder: you should not inflate the view inside of bind/unbind, just set your views there. Also, the view is inflated for you from the layout file you are specifying at R.layout.layout_panel_input, so there is no need to inflate at all.
You should copy this into your project:
https://github.com/airbnb/epoxy/blob/master/kotlinsample/src/main/java/com/airbnb/epoxy/kotlinsample/helpers/KotlinEpoxyHolder.kt
And create your holder in this way:
class PanelInputHolder : KotlinHolder() {
val textView by bind<TextView>(R.id.your_text_view_id)
val button by bind<Button>(R.id.your_button_id)
}
Let's move to your model class: you should remove these variables from the constructor as they are going to be a reference for the annotation processor to create the actual class.
Also, don't set your layout res from the annotation as that will not be allowed in the future.
Like so:
#EpoxyModelClass
class PanelInputModel : EpoxyModelWithHolder<PanelInputHolder>() {
#EpoxyAttribute
lateinit var text: String
#EpoxyAttribute(DoNotHash)
lateinit var listener: View.OnClickListener
override fun getDefaultLayout(): Int {
return R.layout.layout_panel_input
}
override fun bind(holder: PanelInputHolder) {
// here set your views
holder.textView.text = text
holder.textView.setOnClickListener(listener)
}
override fun unbind(holder: PanelInputHolder) {
// here unset your views
holder.textView.text = null
holder.textView.setOnClickListener(null)
}
}
Loop your data inside the controller not inside the model:
class FormInputController : TypedEpoxyController<FormInput>() {
override fun buildModels(data: FormInput?) {
data?.let {
// do your layout as you want, with the models you specify
// for example a header
PanelInputModel_()
.id(it.id)
.text("Hello WOrld!")
.listener { // do something here }
.addTo(this)
// generate a model per item
it.images.forEach {
ImageModel_()
.id(it.imageId)
.image(it)
.addTo(this)
}
}
}
}
When choosing your id, keep in mind that Epoxy will keep track of those and update if the attrs change, so don't use a position, but a unique id that will not get duplicated.

How to reduce a healthlevel by 1 when a button is clicked in kotlin?

I'm starting out with kotlin. and I'm having trouble understanding OnClickListener. Here After setting the initial health level to 10, I need to reduce the health level by 1 and display it. So far I have initiliased the healthlevel and set the onclick, but How do declare the function to reduce it by 1 and call it when the button is clicked?
val TAG = "MyMessage"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.i("LIFECYCLE", "OnCreate")
}
private var healthLevel: Int = 10 //Set the initial health level to 10
private lateinit var healthLevelTextView: TextView
private lateinit var sneezeBtn: Button
private lateinit var takeMedicationButton: Button
private lateinit var blowNoseButton: Button
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt("Answer", healthLevel)
Log.i(TAG, "Called SaveInstanceState()")
}
sneezeBtn.setOnClickListener{ _ ->
// the function goes here
}
Vitor has the answer, but just as a couple of alternatives...
You might want to create a function to update and display your value together:
fun setHealth(health: Int) {
healthLevel = health
healthLevelTextView.text = healthLevel
}
That couples the update with the display change, so the two things always happen together. And if you always set healthLevel with this function (instead of setting the variable directly), the display and value will always be in sync
override fun onCreate(savedInstanceState: Bundle?) {
// set your view variables, so the TextView is ready to update
setHealth(INITIAL_HEALTH_VALUE)
And if you like, Kotlin lets us put a setter function on the variable itself
var healthLevel: Int = 10
set(value) {
field = value
healthLevelTextView.text = health
}
so every time you change the value of healthLevel, it also updates the display. You can use the observable delegate too
var healthLevel: Int by Delegates.observable(10) { _, oldValue, newValue ->
healthLevelTextView.text = newValue
}
These are more advanced than just setting the value and updating the view yourself, just pointing out that they exist as alternatives and ways to keep your logic in one place. Also with these examples, they only run the code after you first set a value - they have a default of 10 in both cases, but initialising that default won't run the setter code or the observable function. So you'd still need to go healthLevel = 10 to get the text to display the initial value.
You can change your healthLevel variable by using:
sneezeBtn.setOnClickListener {
healthLevel--
healthLevelTextView.setText(healthLevel.toString())
}
As a side note, if you use lateinit you lose one of the best features of Kotlin, which is Null Safety. If you don't know how to use that yet, I recommend you to start learning it as soon as possible.
You can also use a very nice feature of Android with kotlin, where the objects for your views are generated automatically in your Activity, you just have to type your XML views id, instead of having to use findViewById everywhere.
I assume, your code sample is within an Activity class and you have a layout file activity_main.xml which looks something like this:
...
<TextView
android:id="#+id/healthLevelTextViewId"
...
/>
<Button
android:id="#+id/sneezeBtnId"
...
/>
...
This would be the code with your desired functionality:
class YourActivity : Activity {
private var healthLevel: Int = 10
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// retrieve references to the text view and button
val healthLevelTextView = findViewById<TextView>(R.id.healthLevelTextViewId)
val sneezeBtn = findViewById<Button>(R.id.sneezeBtnId)
// the OnClickListener must be initialized within a method body, as it is not a method itself.
sneezeBtn.setOnClickListener { _ ->
// implementation from the answer from Vitor Ramos
healthLevel--
healthLevelTextView.setText(healthLevel.toString())
}
}
}
You can further improve the code:
Add apply plugin: 'kotlin-android-extensions' to your app's build.gradle file. Then you can reference your views directly using their IDs. So instead of
val sneezeBtn = findViewById<Button>(R.id.sneezeBtnId)
sneezeBtn.setOnClickListener { ... }
you can use the Id directly like this:
sneezeBtnId.setOnClickListener { ... }
AndroidStudio will give you a hint to add an import for sneezeBtnId.
Use View Models, LiveData and Data Binding. This is the recommended, but a little bit more advanced technique.

TextView text changes but does not update in layout (Kotlin - Android)

TextView text changes but does not update in layout. I tried every method I could find but nothing worked. I have a very basic application with a single activity and 3 layouts*.
*This is the first app I make so I tought it would have been simpler this way
The main problems I am facing are two: almost all the informations around are old and in java, and my textView text does not change.. The app is a simple Rock-Paper-Scissor game I'm trying to make as an exercise.
The textViews.text values get updated but the layout always shows the same text...
I have no idea what could be the problem. I am also struggling to understand exactly how all of this is working exactly...like InflateLayout, Context and Android in general. I do not understand much from android's reference.
THERE IS NO INFLATE(), POSTINFLATE(), FORCELAYOUT(), VISIBILITY TOGGLES BECAUSE NONE OF THEM WORKED :(
Excerpt of the code
class MainActivity : AppCompatActivity() {
lateinit var TITLE:TextView
lateinit var PARAGRAPH:TextView
override fun onCreate(savedInstanceState :Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val InflaterInitializer = LayoutInflater.from(applicationContext) as LayoutInflater
val inflater = InflaterInitializer.inflate(R.layout.activity_2, null, false)
TITLE= inflater.findViewById(R.id.title) as TextView
PARAGRAPH= inflater.findViewById(R.id.paragraph) as TextView
}
There are three functions like this:
fun FUNCTION(v :View) {
val userChoice = "XXX"
val computerChoice = getComputerChoice()
if (userChoice == computerChoice) {
FUNCTION_2(computerChoice)
} else {
runOnUiThread {
TITLE.text =
if (computerChoice == "YYY") getString(R.string.YOU_WON) else getString(R.string.YOU_LOSE);
PARAGRAPH.text = getString(R.string.STRING, computerChoice)
}
}; resultScreen()
}
Function_2...
private fun FUNCTION_2(cc :String) {
runOnUiThread {
TITLE.text = getString(R.string.STRING)
PARAGRAPH.text = getString(R.string.STRING, cc)
}; resultScreen()
}
resultScreen() is just a call to setContentView(LAYOUT)
Here's a video of the app and the update problem:
https://imgur.com/a/iWCRMkq
Code complete here: https://github.com/noiwyr/MorraCinese
EDIT
Unfortunately none of the answers actually worked as I hoped, however redesigning the app and using multiple activities with some tweaks solved the issue. You may find the new code in the github repo.
However I would be curious to know if there is a working solution for this question :)
By calling InflaterInitializer.inflate(R.layout.activity_2, null, false) you inflate a new view hierarchy from the specified xml resource, which is not attached to any of your views (these new views are not shown on your screen). Then you found text views from that new view hierarchy and changed their titles.
So, your onCreate method have to look like this:
override fun onCreate(savedInstanceState :Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_2)
TITLE = findViewById(R.id.title)
PARAGRAPH = findViewById(R.id.paragraph)
}
Also, it's redundant to use methods runOnUiThread() (your code already runs on Ui thread) and resultScreen().
You no need anything , you creat over code no problem I suggest you
val InflaterInitializer = LayoutInflater.from(applicationContext) as LayoutInflater val inflater = InflaterInitializer.inflate(R.layout.activity_outcome, null, false)
Comment this above code no need in kotlin
motivoRisultato.text = getString(R.string.scelta_pc, computerChoice)
Simpaly make this type of code
There are quite a few errors in your code, so I'm going to break down the answer with your code. Do find the Comments inline
class MainActivity : AppCompatActivity() {
/**
* First, Follow conventions for naming variables, they are usually in camelcase for variables and functions, Capitalized for Constants.
* Second, lateinit is used to defer the initialization of a variable, for views, such as
* TextView's, you could use the Kotlin Synthentic library which automatically references the Views of your layout.
*/
lateinit var TITLE:TextView
lateinit var PARAGRAPH:TextView
override fun onCreate(savedInstanceState :Bundle?) {
super.onCreate(savedInstanceState)
/**
* Set content view, internally set's the layout file after inflation using the Activity context. Which means, that you do not
* need to specifically inflate the view.
*/
setContentView(R.layout.activity_main)
/**
* This is the reason why your layout doesn't know refresh, what you're doing here is inflating another layout, but not setting it to your activity.
* This is not required as explained above
*/
val InflaterInitializer = LayoutInflater.from(applicationContext) as LayoutInflater
/**
* Inflater inflates a View Object. one would use this approach if they were programatically adding Views
*/
val inflater = InflaterInitializer.inflate(R.layout.activity_2, null, false)
/**
* the below views are pointing to a reference of TextView for the non visible inflated view. Which is the reason why the text is not updated.
*/
TITLE= inflater.findViewById(R.id.title) as TextView
PARAGRAPH= inflater.findViewById(R.id.paragraph) as TextView
}
}
Here's the code to make things work
class MainActivity : AppCompatActivity() {
private var title:TextView? = null
private var paragraph:TextView? = null
override fun onCreate(savedInstanceState :Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
title= inflater.findViewById(R.id.title) as TextView
paragraph= inflater.findViewById(R.id.paragraph) as TextView
}
fun function(v :View) {
val userChoice = "XXX"
val computerChoice = getComputerChoice()
if (userChoice == computerChoice) {
function2(computerChoice)
} else {
title.text = if (computerChoice == "YYY") getString(R.string.YOU_WON) else getString(R.string.YOU_LOSE);
paragraph.text = getString(R.string.STRING, computerChoice)
}
resultScreen()
}
private fun function2(cc :String) {
title.text = getString(R.string.STRING)
paragraph.text = getString(R.string.STRING, cc)
resultScreen()
}
}
If your use case is to show different screens, look at starting more than one Activity and transitioning between them using Intents

Categories

Resources