Android
View.GONE
View.INVISIBLE
UI development
Android programming

Android Difference between View.GONE and View.INVISIBLE?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

View.GONE removes the view from the layout entirely, so other views fill the vacated space. View.INVISIBLE hides the view visually but keeps its space reserved, so the rest of the layout does not shift. This distinction affects layout performance, animation behavior, and user interaction handling.

The Three Visibility Constants

Android defines three visibility states on every View:

kotlin
view.visibility = View.VISIBLE    // Drawn on screen, occupies space
view.visibility = View.INVISIBLE  // Not drawn, but still occupies space
view.visibility = View.GONE       // Not drawn, does not occupy space

Internally, these map to integer constants (0, 4, and 8 respectively), and they control two independent concerns: whether the view is drawn and whether it participates in layout measurement.

How View.INVISIBLE Works

When you set a view to INVISIBLE, the view still goes through the onMeasure() and onLayout() passes. It occupies the same width and height in its parent container. It simply skips the onDraw() call.

kotlin
val loadingIndicator: ProgressBar = findViewById(R.id.loading)
loadingIndicator.visibility = View.INVISIBLE
// The space is still reserved. Surrounding views do not move.

This is useful when you need to toggle a view on and off without causing the rest of the UI to jump around. A common example is a status label that appears and disappears while the form layout stays stable.

xml
1<LinearLayout
2    android:layout_width="match_parent"
3    android:layout_height="wrap_content"
4    android:orientation="vertical">
5
6    <EditText
7        android:layout_width="match_parent"
8        android:layout_height="wrap_content"
9        android:hint="Enter email" />
10
11    <TextView
12        android:id="@+id/error_label"
13        android:layout_width="match_parent"
14        android:layout_height="wrap_content"
15        android:text="Invalid email format"
16        android:visibility="invisible" />
17
18    <Button
19        android:layout_width="match_parent"
20        android:layout_height="wrap_content"
21        android:text="Submit" />
22
23</LinearLayout>

The button stays in the same position regardless of whether the error label is visible or invisible.

How View.GONE Works

When you set a view to GONE, it is excluded from layout measurement entirely. The parent layout acts as if the view does not exist, and sibling views fill the space.

kotlin
val promoCard: CardView = findViewById(R.id.promo_card)
promoCard.visibility = View.GONE
// The space is reclaimed. Views below shift upward.

This is the correct choice when a UI section is conditionally present, such as a promotional banner that only appears for certain users, or an optional detail section that loads asynchronously.

kotlin
1fun showPromoIfEligible(user: User) {
2    val promoCard: CardView = findViewById(R.id.promo_card)
3    if (user.isPromoEligible) {
4        promoCard.visibility = View.VISIBLE
5        promoCard.findViewById<TextView>(R.id.promo_text).text = user.promoMessage
6    } else {
7        promoCard.visibility = View.GONE
8    }
9}

Comparison Table

BehaviorView.VISIBLEView.INVISIBLEView.GONE
Drawn on screenYesNoNo
Occupies layout spaceYesYesNo
Participates in onMeasure()YesYesNo
Triggers layout pass on toggleNo (already laid out)NoYes
Receives touch eventsYesNoNo
Accessible to screen readersYesNoNo
findViewById() returns itYesYesYes

A key point: findViewById() returns the view regardless of visibility state. The view object exists in memory in all three cases. Only its participation in layout and drawing changes.

Layout Performance Implications

Toggling between VISIBLE and GONE triggers a full layout pass because the parent needs to remeasure and reposition its children. Toggling between VISIBLE and INVISIBLE does not trigger a layout pass because the space allocation remains unchanged. It only triggers an invalidation (redraw).

For views that toggle frequently (multiple times per second, like in an animation loop), INVISIBLE is significantly cheaper than GONE.

kotlin
1// Efficient: no layout recalculation
2blinkingDot.visibility = if (isOn) View.VISIBLE else View.INVISIBLE
3
4// Expensive: triggers layout pass every toggle
5expandableSection.visibility = if (expanded) View.VISIBLE else View.GONE

In ConstraintLayout, views set to GONE have special behavior: their constraints are still respected for positioning other views, but their dimensions collapse to zero. This is different from LinearLayout or RelativeLayout, where GONE views are simply removed from the layout calculation.

xml
1<!-- In ConstraintLayout, view_b is still anchored to view_a's position,
2     even when view_a is GONE. view_a just collapses to 0x0 size. -->
3<TextView
4    android:id="@+id/view_a"
5    android:visibility="gone"
6    app:layout_constraintStart_toStartOf="parent"
7    app:layout_constraintTop_toTopOf="parent" />
8
9<TextView
10    android:id="@+id/view_b"
11    app:layout_constraintStart_toEndOf="@id/view_a"
12    app:layout_constraintTop_toTopOf="@id/view_a" />

Animations and Visibility

When animating a view's appearance or disappearance, visibility state matters:

kotlin
1// Fade out and then remove space
2view.animate()
3    .alpha(0f)
4    .setDuration(300)
5    .withEndAction {
6        view.visibility = View.GONE
7    }
8    .start()
9
10// Fade in from invisible (space was already reserved)
11view.alpha = 0f
12view.visibility = View.VISIBLE
13view.animate()
14    .alpha(1f)
15    .setDuration(300)
16    .start()

If you use GONE and then animate to VISIBLE, there will be a visible layout jump as sibling views shift to make room. To avoid this, set the view to INVISIBLE first, then animate it to full alpha. The space is already reserved, so the layout does not shift.

kotlin
1// Smooth: reserve space first, then animate
2view.visibility = View.INVISIBLE
3view.alpha = 0f
4// ... later when ready to show
5view.visibility = View.VISIBLE
6view.animate().alpha(1f).setDuration(300).start()

Interaction with Data Binding and Compose

In data binding, visibility can be controlled declaratively:

xml
1<TextView
2    android:layout_width="wrap_content"
3    android:layout_height="wrap_content"
4    android:text="@{viewModel.errorMessage}"
5    android:visibility="@{viewModel.hasError ? View.VISIBLE : View.GONE}" />

In Jetpack Compose, the concept is handled differently. There is no direct equivalent of INVISIBLE. Instead, you control visibility with conditional composition and the Modifier.alpha() modifier:

kotlin
1// Equivalent of GONE: don't compose the element at all
2if (showBanner) {
3    BannerCard(message = promoText)
4}
5
6// Equivalent of INVISIBLE: compose but make transparent
7Box(modifier = Modifier.alpha(if (showLabel) 1f else 0f)) {
8    Text("Status label")
9}

Common Pitfalls

  • Using View.GONE for views that toggle rapidly (like blinking indicators). Each toggle triggers a layout pass, causing unnecessary CPU work and potential jank.
  • Assuming View.INVISIBLE views cannot intercept touches. While they do not receive standard click events, custom touch handlers on the parent can still hit-test against invisible children depending on implementation.
  • Forgetting that ConstraintLayout treats GONE views differently from other layouts. Constraints to a GONE view are preserved but resolve to the view's position with zero dimensions.
  • Setting a view to GONE and then immediately calling getWidth() or getHeight(). The view has not been remeasured yet. Use a ViewTreeObserver.OnGlobalLayoutListener or post {} to read dimensions after the layout pass.
  • Animating alpha to 0 but forgetting to set visibility afterward. The view is still VISIBLE with alpha 0, so it still intercepts touch events and occupies space.
  • Using INVISIBLE when GONE is correct, leaving unexplained blank gaps in the UI that confuse users.

Summary

  • View.INVISIBLE hides a view but keeps its space. Use it when the layout should remain stable during visibility toggles.
  • View.GONE hides a view and releases its space. Use it when the view is conditionally present and other content should fill the gap.
  • INVISIBLE is cheaper to toggle because it skips layout recalculation. GONE triggers a full layout pass.
  • In ConstraintLayout, GONE views collapse to zero size but their constraints remain active.
  • For animations, use INVISIBLE to reserve space before fading in, preventing layout jumps.
  • In Jetpack Compose, use conditional composition for GONE behavior and Modifier.alpha() for INVISIBLE behavior.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.