Dynamically Changing Android View Properties from Kotlin

In Android development, especially when using Kotlin and XML, it’s essential to dynamically update view properties such as text, visibility, and background. This allows your app to respond to user input, data changes, or other dynamic events. In this post, we’ll explore how to dynamically modify these view properties from Kotlin in XML-based Android layouts, complete with code samples and best practices.

Why Dynamically Change View Properties?

  • Improved User Experience: Dynamically updating UI elements allows you to provide immediate feedback to users.
  • Data-Driven UI: Adjusting views based on real-time data or API responses ensures that the UI reflects the current state.
  • Adaptable Layouts: Modifying visibility and backgrounds can create different states within the same activity or fragment, reducing redundancy.

Setting Up the Project

First, ensure that you have an Android project set up with Kotlin enabled. For this demonstration, we will focus on an Activity layout defined in XML and modify its properties from Kotlin code.

Step 1: Create a New Android Project

If you don’t already have a project, create one in Android Studio. Choose Kotlin as your programming language.

Step 2: Add View Elements to the XML Layout

In your layout XML file (e.g., activity_main.xml), add the necessary view elements:


<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/myTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Initial Text"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <Button
        android:id="@+id/myButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Change Properties"
        app:layout_constraintTop_toBottomOf="@id/myTextView"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="16dp" />

    <View
        android:id="@+id/myView"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="#CCCCCC"
        app:layout_constraintTop_toBottomOf="@id/myButton"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="16dp" />

</androidx.constraintlayout.widget.ConstraintLayout>

Step 3: Access Views in Kotlin

In your MainActivity.kt file, use findViewById to get references to your views. Modern approaches might utilize View Binding or Data Binding for cleaner code.

Dynamically Changing Text

To change the text of a TextView, obtain a reference to it and use the text property.


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

class MainActivity : AppCompatActivity() {

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

        val myTextView: TextView = findViewById(R.id.myTextView)
        val myButton: Button = findViewById(R.id.myButton)

        myButton.setOnClickListener {
            myTextView.text = "New Text from Kotlin!"
        }
    }
}

Dynamically Changing Visibility

To modify the visibility of a view, use the visibility property, setting it to View.VISIBLE, View.INVISIBLE, or View.GONE.


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

class MainActivity : AppCompatActivity() {

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

        val myTextView: TextView = findViewById(R.id.myTextView)
        val myButton: Button = findViewById(R.id.myButton)
        val myView: View = findViewById(R.id.myView)

        myButton.setOnClickListener {
            // Toggle visibility
            myView.visibility = if (myView.visibility == View.VISIBLE) {
                View.GONE
            } else {
                View.VISIBLE
            }
        }
    }
}

Explanation:

  • View.VISIBLE: The view is visible on the screen.
  • View.INVISIBLE: The view is invisible, but still takes up space in the layout.
  • View.GONE: The view is invisible and does not take up any space in the layout.

Dynamically Changing Background

You can change the background of a view programmatically using setBackgroundColor, setBackgroundResource, or background properties.


import android.os.Bundle
import android.widget.TextView
import android.widget.Button
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import android.graphics.Color

class MainActivity : AppCompatActivity() {

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

        val myTextView: TextView = findViewById(R.id.myTextView)
        val myButton: Button = findViewById(R.id.myButton)
        val myView: View = findViewById(R.id.myView)

        myButton.setOnClickListener {
            // Change background color
            myView.setBackgroundColor(Color.BLUE) // Simple color

            // Change background resource (e.g., drawable)
            myTextView.setBackgroundResource(android.R.drawable.alert_dark_frame)

            // Using a color resource from colors.xml
             myButton.setBackgroundColor(ContextCompat.getColor(this, R.color.myColor))

        }
    }
}

To define R.color.myColor, add the following to your colors.xml:


<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="myColor">#FF4081</color>
</resources>

Modern Approach: View Binding

View Binding generates a binding class for each XML layout file present in the module. The binding class contains direct references to all views that have an ID in the corresponding layout. View Binding is simpler and more efficient than findViewById. Enable view binding in your build.gradle file:


android {
    //...
    buildFeatures {
        viewBinding true
    }
}

Here’s how to use View Binding in your activity:


import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.example.myapp.databinding.ActivityMainBinding // Replace with your package

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.myButton.setOnClickListener {
            binding.myTextView.text = "Text Changed via ViewBinding"
            binding.myView.visibility = if (binding.myView.visibility == View.VISIBLE) {
                View.GONE
            } else {
                View.VISIBLE
            }
            binding.myView.setBackgroundColor(ContextCompat.getColor(this, R.color.myColor))
        }
    }
}

Best Practices

  • Use View Binding or Data Binding: These reduce boilerplate code and provide compile-time safety.
  • Handle Configuration Changes: Ensure your UI updates survive configuration changes like screen rotations. ViewModels are often used to manage UI-related data in a lifecycle-conscious way.
  • Avoid Heavy Operations on the UI Thread: When updating the UI based on long-running tasks, use Kotlin coroutines or other background threads.
  • Keep Logic in ViewModels: UI controllers (Activities/Fragments) should observe ViewModels and update the UI accordingly. This keeps the UI layer clean and testable.

Conclusion

Dynamically changing view properties is a fundamental aspect of Android development. By understanding how to modify text, visibility, and backgrounds programmatically in Kotlin using XML layouts, and by embracing modern approaches like View Binding, you can create highly interactive and adaptable user interfaces. Always prioritize efficient coding practices and architectural patterns to ensure maintainability and performance.