save Instance State in Android studio: Kotlin

12.3k Views Asked by At

I was doing this program and tried to run an app as an output. However, when I turned the app in Landscape mode, my text output didn't work. I realized that I need to add onSaveInstanceState in the Kotlin code, which I don't know how to do.

MainActivity.kt:

class MainActivity : AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        var count = 10
        val textCount = findViewById<View>(R.id.textView) as TextView
        val buttonred = findViewById<View>(R.id.injury) as Button
        val buttoning = findViewById<View>(R.id.vial) as Button

        buttonred.setOnClickListener {
            if (count >= 0) {
                count--
                textCount.text = count.toString()
            }
        }

        buttoning.setOnClickListener {
            if (count <= 10)
                count += 3
            textCount.text = count.toString()
        }
    }
}

XML Code

LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

<TextView
        android:id="@+id/textView"
        android:layout_width="60dp"
        android:layout_height="78dp"
        android:layout_marginStart="145dp"
        android:layout_marginTop="68dp"
        android:layout_marginEnd="145dp"
        android:textSize="50dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
<Button
        android:id="@+id/injury"
        android:layout_width="106dp"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="150dp"
        android:text="injury"
         />
<Button
        android:id="@+id/vial"
        android:layout_width="197dp"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="109dp"
        android:text="vial" />
</LinearLayout>

I would like to know how to add the onSaveInstanceState

2

There are 2 best solutions below

6
On BEST ANSWER

Here is the full code, You didn't add many details but this is how the sample project looks, it enables to save count and then set it to TextView:

MainActivity.kt:

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    companion object {
        const val COUNT_KEY = "COUNT_KEY" // const key to save/read value from bundle
    }
    
    private var count = 0 // count value with setter. It will be easier, You can change this value and don't have to think about setting TextView.text
        set(value) {
            field = value
            txtCount.text = value.toString()
        }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.i("MyTag", "onCreate")

        txtCount.text = count.toString()

        butIncrement.setOnClickListener {
            count++
        }

    }

    override fun onSaveInstanceState(outState: Bundle) { // Here You have to save count value
        super.onSaveInstanceState(outState)
        Log.i("MyTag", "onSaveInstanceState")
        
        outState.putInt(COUNT_KEY, count)
    }

    override fun onRestoreInstanceState(savedInstanceState: Bundle) { // Here You have to restore count value
        super.onRestoreInstanceState(savedInstanceState)
        Log.i("MyTag", "onRestoreInstanceState")

        count = savedInstanceState.getInt(COUNT_KEY)
    }
}

activity_main.xml:

<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/butIncrement"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Increment" />

    <TextView
        android:id="@+id/txtCount"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textSize="40sp" />

</androidx.appcompat.widget.LinearLayoutCompat>

You edited the question and added more details, here is the full Kotlin code, I tested it and it seems to work.

import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {

    lateinit var textCount: TextView
    lateinit var buttonred: Button
    lateinit var buttoning: Button

    companion object {
        const val COUNT_KEY = "COUNT_KEY"
    }

    private var count = 0
        set(value) {
            field = value
            textCount.text = value.toString()
        }


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.i("MyTag", "onCreate")


        textCount = findViewById<View>(R.id.textView) as TextView
        buttonred = findViewById<View>(R.id.injury) as Button
        buttoning = findViewById<View>(R.id.vial) as Button

        count = 10

        buttonred.setOnClickListener {
            if (count >= 0) {
                count--
                textCount.text = count.toString()
            }
        }

        buttoning.setOnClickListener {
            if (count <= 10)
                count += 3
            textCount.text = count.toString()
        }
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        Log.i("MyTag", "onSaveInstanceState")

        outState.putInt(COUNT_KEY, count)
    }

    override fun onRestoreInstanceState(savedInstanceState: Bundle) {
        super.onRestoreInstanceState(savedInstanceState)
        Log.i("MyTag", "onRestoreInstanceState")

        count = savedInstanceState.getInt(COUNT_KEY)
    }
}

ViewModel implementation:

To Your Gradle (module.app) file add:

dependencies {
    def lifecycle_version = "2.2.0"

    // ViewModel
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
    // LiveData
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
}

MainActivityViewModel class:

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel


internal class MainActivityViewModel : ViewModel() {

    private val _count: MutableLiveData<Int> = MutableLiveData()
    val count: LiveData<Int>
        get() = _count

    init {
        _count.value = START_VALUE
    }

    fun increment() {
        _count.value = _count.value!! + 1
    }

    fun decrement() {
        _count.value = _count.value!! - 1

    }

    companion object {
        private const val START_VALUE = 10
    }
}

MainActivity class:

import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider

class MainActivity : AppCompatActivity()
{
    private lateinit var viewModel: MainActivityViewModel

    lateinit var textCount: TextView
    lateinit var butIncrement: Button
    lateinit var butDecrement: Button

    override fun onCreate(savedInstanceState: Bundle?)
    {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        textCount = findViewById<View>(R.id.textView) as TextView
        butIncrement = findViewById<View>(R.id.injury) as Button
        butDecrement = findViewById<View>(R.id.vial) as Button

        viewModel = ViewModelProvider(this).get(MainActivityViewModel::class.java)

        viewModel.count.observe(this, {
            textCount.text = it.toString()
        })

        butIncrement.setOnClickListener {
            viewModel.increment()
        }

        butDecrement.setOnClickListener {
            viewModel.decrement()
        }
    }
}
3
On

You need to override onSaveInstanceState and onRestoreInstanceState example follows

class MainActivity : AppCompatActivity() { private lateinit var resultTv: TextView private var count = 0

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    val buttonIncrease = findViewById<Button>(R.id.button_increase)
    val buttonDecrease = findViewById<Button>(R.id.button_decrease)
    resultTv = findViewById(R.id.result)
    buttonIncrease.setOnClickListener { view ->
        updateValue(view.id)
        resultTv.text = "" + count
    }
    buttonDecrease.setOnClickListener { view ->
        updateValue(view.id)
        resultTv.text = "" + count
    }
}

private fun updateValue(id: Int) {
    if (id == R.id.button_increase) {
        if (count < 10) count++
    } else {
        if (count > 0) count--
    }
}

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
    outState.putInt("count", count)
}

override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
    super.onRestoreInstanceState(savedInstanceState)
    savedInstanceState?.let {
       count = it.get("count") as Int
        resultTv.text = "" + count
    }
}

}