Controlling View Visibility: Mastering VISIBLE, INVISIBLE, GONE in Kotlin and XML

In Android development, controlling the visibility of UI elements is a fundamental aspect of creating dynamic and responsive user interfaces. Whether you’re working with Kotlin or XML layouts, understanding the different visibility states—VISIBLE, INVISIBLE, and GONE—is crucial for building flexible and interactive apps.

Understanding View Visibility in Android

The visibility property in Android determines whether a UI element is displayed on the screen. There are three main options for this property, each serving a distinct purpose:

  • VISIBLE: The element is fully visible on the screen.
  • INVISIBLE: The element is hidden, but it still occupies its space in the layout. This means other elements are positioned as if the invisible element were still there.
  • GONE: The element is hidden and does not take up any space in the layout. Other elements are positioned as if the gone element never existed.

Let’s explore how to implement and use these visibility states in both Kotlin and XML.

Setting View Visibility in XML

In XML, you can set the initial visibility of a View using the android:visibility attribute. This is particularly useful for defining the default state of UI elements when the activity or fragment is created.

Setting the Visibility Attribute

To set the visibility of a View in XML, use the following attribute within the View’s XML definition:

<TextView
    android:id="@+id/myTextView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hello, World!"
    android:visibility="visible" />

In this example, android:visibility is set to visible, ensuring that the TextView is displayed by default.

Visibility Options

Here’s how you can use the different visibility options in XML:

  • VISIBLE:
android:visibility="visible"
  • INVISIBLE:
android:visibility="invisible"
  • GONE:
android:visibility="gone"

When an element is set to invisible, it disappears from the screen but still occupies its original space, affecting the layout of other elements. In contrast, when set to gone, it completely disappears and other elements reposition themselves to fill the void.

Controlling View Visibility in Kotlin

In Kotlin, you can dynamically change the visibility of UI elements at runtime. This is essential for responding to user interactions, data changes, or other events that require updating the UI.

Accessing Views in Kotlin

Before changing the visibility in Kotlin, you need to access the View. If you’re using View Binding (recommended), you can directly access the Views from your binding object. If you’re using findViewById, you’ll need to ensure you have the correct view ID.

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

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

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

        // Example: Setting visibility in Kotlin
        binding.myButton.setOnClickListener {
            if (binding.myTextView.visibility == View.VISIBLE) {
                binding.myTextView.visibility = View.INVISIBLE
            } else {
                binding.myTextView.visibility = View.VISIBLE
            }
        }
    }
}

Visibility Constants in Kotlin

In Kotlin, the View class provides constants for the visibility states:

  • View.VISIBLE: The view is visible.
  • View.INVISIBLE: The view is invisible but still takes up space.
  • View.GONE: The view is invisible and does not take up space.

Changing Visibility Dynamically

To dynamically change the visibility of a View, simply assign one of these constants to its visibility property:

binding.myTextView.visibility = View.GONE

Practical Examples and Use Cases

Example 1: Toggle Visibility of a TextView

This example demonstrates how to toggle the visibility of a TextView when a button is clicked:

binding.toggleButton.setOnClickListener {
    if (binding.myTextView.visibility == View.VISIBLE) {
        binding.myTextView.visibility = View.INVISIBLE
    } else {
        binding.myTextView.visibility = View.VISIBLE
    }
}

Each time the button is clicked, the TextView switches between VISIBLE and INVISIBLE states.

Example 2: Displaying a Loading Indicator

In many apps, you need to display a loading indicator while data is being fetched. Here’s how you can use visibility to show and hide a ProgressBar:

binding.progressBar.visibility = View.VISIBLE // Show the loading indicator
// Simulate data loading
Handler(Looper.getMainLooper()).postDelayed({
    binding.progressBar.visibility = View.GONE // Hide the loading indicator
    binding.dataTextView.text = "Data loaded!"
}, 2000) // Simulate 2 seconds loading time

This code shows the ProgressBar when data loading starts and hides it after the data is loaded, providing visual feedback to the user.

Example 3: Conditional Display Based on Data

Sometimes, you might want to display certain UI elements only when specific data is available. Here’s an example:

fun updateUI(data: String?) {
    if (data.isNullOrEmpty()) {
        binding.noDataTextView.visibility = View.VISIBLE
        binding.dataTextView.visibility = View.GONE
    } else {
        binding.noDataTextView.visibility = View.GONE
        binding.dataTextView.visibility = View.VISIBLE
        binding.dataTextView.text = data
    }
}

In this scenario, the noDataTextView is displayed when there’s no data, and the dataTextView is shown when data is available.

Best Practices

  • Use View Binding: View Binding eliminates findViewById calls, reducing boilerplate and preventing null pointer exceptions.
  • Consider Performance: Frequent visibility changes can impact performance, especially in complex layouts. Use these changes judiciously.
  • Choose the Right Visibility: Understand the difference between INVISIBLE and GONE. Use GONE when you want to reclaim space and INVISIBLE when you need the element to retain its layout space.
  • Keep UI Logic in the ViewModel or Presenter: Decouple UI logic from your Activities or Fragments by handling visibility changes in a ViewModel or Presenter. This makes your code more testable and maintainable.

Conclusion

Controlling View visibility is an essential skill in Android development. Whether setting initial states in XML or dynamically modifying them in Kotlin, understanding the differences between VISIBLE, INVISIBLE, and GONE allows you to create flexible, responsive, and user-friendly applications. By following best practices and utilizing these techniques effectively, you can build more robust and maintainable Android apps.