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

Tùy chỉnh màu sắc Button Android bằng Drawable Selector

Thay vì dùng background color đơn sắc, Android cho phép định nghĩa State List Drawable (Selector) để Button thay đổi màu sắc, gradient, viền, bo góc tùy theo trạng thái: normal, pressed, focused, disabled.

1. Tạo Drawable Selector (res/drawable/btn_custom_background.xml)

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="https://schemas.android.com/apk/res/android">
    <!-- Trạng thái nhấn (pressed) -->
    <item android:state_pressed="true">
        <shape android:shape="rectangle">
            <gradient
                android:startColor="#FFEB3B"
                android:endColor="#FDD835"
                android:angle="270" />
            <stroke android:width="2dp" android:color="#F9A825" />
            <corners android:radius="8dp" />
            <padding android:left="16dp" android:top="12dp" android:right="16dp" android:bottom="12dp" />
        </shape>
    </item>

    <!-- Trạng thái focus (điều hướng bàn phím/TV) -->
    <item android:state_focused="true">
        <shape android:shape="rectangle">
            <gradient
                android:startColor="#FF9800"
                android:endColor="#F57C00"
                android:angle="270" />
            <stroke android:width="2dp" android:color="#E65100" />
            <corners android:radius="8dp" />
            <padding android:left="16dp" android:top="12dp" android:right="16dp" android:bottom="12dp" />
        </shape>
    </item>

    <!-- Trạng thái disabled -->
    <item android:state_enabled="false">
        <shape android:shape="rectangle">
            <solid android:color="#E0E0E0" />
            <stroke android:width="1dp" android:color="#BDBDBD" />
            <corners android:radius="8dp" />
            <padding android:left="16dp" android:top="12dp" android:right="16dp" android:bottom="12dp" />
        </shape>
    </item>

    <!-- Trạng thái mặc định (normal) -->
    <item>
        <shape android:shape="rectangle">
            <gradient
                android:startColor="#2196F3"
                android:endColor="#1976D2"
                android:angle="270" />
            <stroke android:width="2dp" android:color="#1565C0" />
            <corners android:radius="8dp" />
            <padding android:left="16dp" android:top="12dp" android:right="16dp" android:bottom="12dp" />
        </shape>
    </item>
</selector>

2. Áp dụng vào Button (Layout XML)

<Button
    android:id="@+id/btnCustom"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Nút tùy chỉnh"
    android:textColor="@android:color/white"
    android:textSize="16sp"
    android:background="@drawable/btn_custom_background"
    android:stateListAnimator="@null" />
💡 Lưu ý: android:stateListAnimator="@null" tắt hiệu ứng elevation mặc định (shadow nâng lên khi press) của Material Button, để selector drawable hiển thị trọn vẹn. Nếu dùng MaterialButton, hãy dùng app:backgroundTint="@null" và set background drawable.

3. Sử dụng trong Code (Dynamic)

Button btn = findViewById(R.id.btnCustom);
// Đổi màu động (ví dụ: theme ngày/đêm)
btn.setBackgroundResource(R.drawable.btn_custom_background_night);

4. Mở rộng: Ripple Effect (Android 5.0+)

Kết hợp Ripple với Selector cho cảm giác chạm hiện đại:

<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="https://schemas.android.com/apk/res/android"
    android:color="?attr/colorControlHighlight">
    <item android:id="@android:id/mask">
        <shape android:shape="rectangle">
            <corners android:radius="8dp" />
            <solid android:color="#2196F3" />
        </shape>
    </item>
    <item android:drawable="@drawable/btn_custom_background" />
</ripple>

Lưu file dưới res/drawable-v21/btn_custom_background.xml (thư mục v21 cho API 21+).

5. MaterialButton (Khuyên dùng cho app hiện đại)

<com.google.android.material.button.MaterialButton
    android:id="@+id/materialBtn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Material Button"
    app:backgroundTint="@color/selector_btn_bg"
    app:cornerRadius="8dp"
    app:strokeWidth="2dp"
    app:strokeColor="@color/selector_stroke" />

res/color/selector_btn_bg.xml:

<selector xmlns:android="https://schemas.android.com/apk/res/android">
    <item android:state_pressed="true" android:color="#FFEB3B" />
    <item android:state_focused="true" android:color="#FF9800" />
    <item android:state_enabled="false" android:color="#E0E0E0" />
    <item android:color="#2196F3" />
</selector>

So sánh các cách tùy chỉnh Button

Cách Độ phức tạp Tính năng Khuyên dùng
Selector Drawable (Shape) Trung bình Gradient, stroke, padding, corners tùy ý Custom UI đặc thù, legacy support
Ripple + Selector Cao Ripple effect + full custom App hiện đại, Material Design
MaterialButton + ColorStateList Thấp Background tint, stroke, icon, shape Đa số trường hợp, dễ maintain
Jetpack Compose Thấp (Kotlin) Declarative, state-based styling Project mới, full Compose

Best Practices

  • Định nghĩa màu trong res/values/colors.xml và res/color/selector_*.xml để dễ theme light/dark.
  • Dùng dp cho padding, margin, radius; sp cho text size.
  • Test trên cả Light/Dark theme, landscape, split-screen.
  • Accessibility: Đảm bảo contrast ratio ≥ 4.5:1 (text trên background).
  • Tránh hardcode màu trong Java/Kotlin code.

Kết luận

Drawable Selector là cách mạnh mẽ, linh hoạt nhất để tùy chỉnh Button truyền thống. Với app mới, ưu tiên MaterialButton + ColorStateList hoặc Jetpack Compose để code ngắn gọn, nhất quán, dễ bảo trì.