Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Android

Cách ẩn/hiện Button (Visibility) trên Android

Giới thiệu

Android cung cấp 3 trạng thái hiển thị cho View (kể cả Button) thông qua setVisibility() hoặc XML attribute android:visibility:

  • VISIBLE (0): Hiển thị bình thường, chiếm layout space.
  • INVISIBLE (4): Ẩn nhưng **vẫn chiếm layout space** (khoảng trống vẫn còn).
  • GONE (8): Ẩn và **không chiếm layout space** (các view khác co lại lấp đầy).

Các bước thực hiện

Bước 1: Tạo dự án mới

Android Studio → File → New Project → Empty View Activity.

Bước 2: Thiết kế giao diện (res/layout/activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="24dp"
    android:gravity="center"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Demo Button Visibility"
        android:textSize="20sp"
        android:textStyle="bold"
        android:layout_marginBottom="24dp" />

    <Button
        android:id="@+id/btnVisible"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button 1 (Luôn hiện)" />

    <Button
        android:id="@+id/btnToggleInvisible"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="12dp"
        android:text="Button 2 (Toggle INVISIBLE)" />

    <Button
        android:id="@+id/btnToggleGone"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="12dp"
        android:text="Button 3 (Toggle GONE)" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:orientation="horizontal"
        android:gravity="center">

        <Button
            android:id="@+id/btnShowAll"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="12dp"
            android:text="Hiện tất cả" />

        <Button
            android:id="@+id/btnHideAll"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Ẩn tất cả (GONE)" />
    </LinearLayout>

    <TextView
        android:id="@+id/tvStatus"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:textSize="14sp"
        android:textColor="#666"
        tools:text="Status: Button 2=VISIBLE, Button 3=VISIBLE" />
</LinearLayout>

Bước 3: MainActivity.kt

package com.example.buttonvisibility

import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.example.buttonvisibility.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

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

        binding.btnToggleInvisible.setOnClickListener { toggleInvisible() }
        binding.btnToggleGone.setOnClickListener { toggleGone() }
        binding.btnShowAll.setOnClickListener { showAll() }
        binding.btnHideAll.setOnClickListener { hideAllGone() }
        
        updateStatus()
    }

    private fun toggleInvisible() {
        val btn = binding.btnToggleInvisible
        btn.visibility = if (btn.visibility == View.VISIBLE) View.INVISIBLE else View.VISIBLE
        updateStatus()
    }

    private fun toggleGone() {
        val btn = binding.btnToggleGone
        btn.visibility = if (btn.visibility == View.VISIBLE) View.GONE else View.VISIBLE
        updateStatus()
    }

    private fun showAll() {
        binding.btnToggleInvisible.visibility = View.VISIBLE
        binding.btnToggleGone.visibility = View.VISIBLE
        updateStatus()
    }

    private fun hideAllGone() {
        binding.btnToggleInvisible.visibility = View.GONE
        binding.btnToggleGone.visibility = View.GONE
        updateStatus()
    }

    private fun updateStatus() {
        val s2 = when (binding.btnToggleInvisible.visibility) {
            View.VISIBLE -> "VISIBLE"
            View.INVISIBLE -> "INVISIBLE"
            View.GONE -> "GONE"
            else -> "UNKNOWN"
        }
        val s3 = when (binding.btnToggleGone.visibility) {
            View.VISIBLE -> "VISIBLE"
            View.INVISIBLE -> "INVISIBLE"
            View.GONE -> "GONE"
            else -> "UNKNOWN"
        }
        binding.tvStatus.text = "Status: Button 2=$s2, Button 3=$s3"
    }
}

XML Attribute: android:visibility


<Button
    android:id="@+id/btnInitiallyGone"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Ẩn ban đầu (GONE)"
    android:visibility="gone" />

<Button
    android:id="@+id/btnInitiallyInvisible"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Ẩn ban đầu (INVISIBLE)"
    android:visibility="invisible" />

Animation khi thay đổi Visibility

Mặc định visibility thay đổi tức thì. Để animate:

Cách 1: ViewPropertyAnimator (Đơn giản)


// Fade out + GONE
button.animate()
    .alpha(0f)
    .setDuration(300)
    .withEndAction { button.visibility = View.GONE }
    .start()

// Fade in từ GONE
button.visibility = View.VISIBLE
button.alpha = 0f
button.animate()
    .alpha(1f)
    .setDuration(300)
    .start()

Cách 2: AnimatedVisibility (Jetpack Compose)


var visible by remember { mutableStateOf(true) }
AnimatedVisibility(visible) {
    Button(onClick = { visible = false }) { Text("Click to hide") }
}

Cách 3: Transition API (XML Layout)


// res/transition/visibility_transition.xml
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="https://schemas.android.com/apk/res/android"
    android:transitionOrdering="together">
    <fade android:fadingMode="fade_out" />
    <changeBounds />
</transitionSet>

// Layout container
<LinearLayout
    android:id="@+id/container"
    android:animateLayoutChanges="true" 
     ... >

So sánh INVISIBLE vs GONE

Trường hợpDùng INVISIBLEDùng GONE
Muốn giữ khoảng trống✅ Có❌ Không
Muốn view khác lấp đầy❌ Không✅ Có
Animation slide/collapseKhóDễ (với animateLayoutChanges)
Accessibility (TalkBack)Vẫn announce (nếu focusable)Bỏ qua hoàn toàn
Touch eventKhông nhận touchKhông nhận touch
Layout passVẫn measure/layoutBỏ qua measure/layout

Best Practices

  • Dùng GONE cho điều kiện ẩn/hiện thường xuyên (loading, error state, conditional UI).
  • Dùng INVISIBLE khi cần giữ layout stable (placeholder, overlay, spacing cố định).
  • Kết hợp với animateLayoutChanges="true" trên parent ViewGroup để auto animate khi GONE.
  • Kiểm tra visibility trước khi thao tác: if (view.visibility == View.VISIBLE).
  • Trong RecyclerView: Dùng payload hoặc đổi visibility trong onBindViewHolder - tránh force layout.
  • ConstraintLayout: GONE view được treat như 0 size, constraints tới nó có thể bị ignore → dùng app:layout_goneMargin.

Lưu ý quan trọng

  • View.GONE và View.INVISIBLE đều **không nhận touch event**.
  • Visibility change trigger requestLayout() - tránh thay đổi liên tục trong loop/scroll.
  • Fragment/ViewPager: setVisibility trên root view của fragment không recommended - dùng FragmentTransaction.hide()/show().
  • Data Binding: android:visibility="@{viewModel.isLoading ? View.VISIBLE : View.GONE}" (cần import View).