কম্পিউটার টিউটোরিয়াল

অ্যান্ড্রয়েডে স্ট্যান্ডার্ড বাটনে কাস্টম কালার এবং স্টেট-লিস্ট ড্রয়েবল ব্যবহার করা

অ্যান্ড্রয়েডের ডিফল্ট `Button` উইজেটে কাস্টম কালার, রিপল ইফেক্ট, এবং স্টেট-ভিত্তিক স্টাইলিং (Pressed, Focused, Default) দেওয়ার জন্য State List Drawable (Selector) ব্যবহার করা সবচেয়ে ভালো এবং পারফরম্যান্ট পদ্ধতি। এই টিউটোরিয়ালে একটি বাটনের জন্য প্রেসড, ফোকাসড, এবং নরমাল স্টেটে আলাদা আলাদা কালার এবং গ্রেডিয়েন্ট দেওয়ার পদ্ধতি দেখানো হলো।

কনসেপ্ট: State List Drawable (Selector)

একটি XML ফাইল (`drawable/background_button.xml`) এ `` রুট এলিমেন্ট ব্যবহার করে বিভিন্ন স্টেটের জন্য আলাদা `` ডিক্লেয়ার করা হয়। সিস্টেম অটোমেটিক্যালি বাটনের বর্তমান স্টেট অনুযায়ী সঠিক আইটেম বেছে নেয়।

১. drawable/background_button.xml (Selector)

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="https://schemas.android.com/apk/res/android">

    <!-- State: Pressed (User টাচ করছে) -->
    <item android:state_pressed="true">
        <shape android:shape="rectangle">
            <gradient
                android:startColor="#FFF000"  <!-- Bright Yellow -->
                android:endColor="#D9DD95"    <!-- Light Olive -->
                android:angle="270" />
            <stroke
                android:width="2dp"
                android:color="#D1D3E3" />
            <corners android:radius="8dp" />
            <padding
                android:left="16dp"
                android:top="12dp"
                android:right="16dp"
                android:bottom="12dp" />
        </shape>
    </item>

    <!-- State: Focused (Keyboard/Gamepad Navigation) -->
    <item android:state_focused="true">
        <shape android:shape="rectangle">
            <gradient
                android:startColor="#ECA680"  <!-- Light Orange -->
                android:endColor="#FD5900"    <!-- Deep Orange -->
                android:angle="270" />
            <stroke
                android:width="2dp"
                android:color="#D1D3E3" />
            <corners android:radius="8dp" />
            <padding
                android:left="16dp"
                android:top="12dp"
                android:right="16dp"
                android:bottom="12dp" />
        </shape>
    </item>

    <!-- State: Enabled but not pressed/focused (Default) -->
    <item android:state_enabled="true">
        <shape android:shape="rectangle">
            <gradient
                android:startColor="#A1AAEE"  <!-- Light Blue -->
                android:endColor="#4356E7"    <!-- Deep Blue -->
                android:angle="270" />
            <stroke
                android:width="2dp"
                android:color="#D1D3E3" />
            <corners android:radius="8dp" />
            <padding
                android:left="16dp"
                android:top="12dp"
                android:right="16dp"
                android:bottom="12dp" />
        </shape>
    </item>

    <!-- State: Disabled -->
    <item android:state_enabled="false">
        <shape android:shape="rectangle">
            <solid android:color="#CCCCCC" />
            <stroke android:width="1dp" android:color="#999999" />
            <corners android:radius="8dp" />
            <padding
                android:left="16dp"
                android:top="12dp"
                android:right="16dp"
                android:bottom="12dp" />
        </shape>
    </item>

</selector>

২. 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:gravity="center"
    android:padding="24dp"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="State List Drawable Demo"
        android:textSize="20sp"
        android:textStyle="bold"
        android:layout_marginBottom="32dp" />

    <Button
        android:id="@+id/btnCustom"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Custom Styled Button"
        android:textSize="16sp"
        android:textColor="@android:color/white"
        android:textAllCaps="false"
        android:background="@drawable/background_button" />

    <Button
        android:id="@+id/btnDisabled"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="Disabled Button"
        android:textSize="16sp"
        android:textAllCaps="false"
        android:enabled="false"
        android:background="@drawable/background_button" />

</LinearLayout>

৩. MainActivity.java (Optional - Interaction Demo)

package com.example.custombuttoncolor;

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

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button btnCustom = findViewById(R.id.btnCustom);
        btnCustom.setOnClickListener(v -> 
            Toast.makeText(this, "Button Clicked! State: Pressed → Normal", Toast.LENGTH_SHORT).show()
        );
    }
}

৪. Kotlin Version (Modern)

// MainActivity.kt
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        findViewById

৫. MaterialButton (Recommended for Modern Apps)

নতুন প্রজেক্টে `MaterialButton` ব্যবহার করা উচিত, যেটি বিল্ট-ইন রিপল, কালার থিমিং, এবং শেপ সাপোর্ট করে।

XML Style (res/values/themes.xml)

<style name="CustomButton" parent="Widget.Material3.Button">
    <item name="backgroundTint">@color/button_bg_selector</item>
    <item name="rippleColor">@color/ripple_selector</item>
    <item name="cornerRadius">8dp</item>
    <item name="android:textColor">@android:color/white</item>
    <item name="android:insetTop">0dp</item>
    <item name="android:insetBottom">0dp</item>
</style>

Color State List (res/color/button_bg_selector.xml)

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="https://schemas.android.com/apk/res/android">
    <item android:state_pressed="true" android:color="#D9DD95" />
    <item android:state_focused="true" android:color="#FD5900" />
    <item android:state_enabled="false" android:color="#CCCCCC" />
    <item android:color="#4356E7" /> <!-- Default -->
</selector>

Ripple Color (res/color/ripple_selector.xml)

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="https://schemas.android.com/apk/res/android">
    <item android:state_pressed="true" android:color="#FFF000" />
    <item android:color="@android:color/transparent" />
</selector>

Usage in Layout

<com.google.android.material.button.MaterialButton
    android:id="@+id/materialBtn"
    style="@style/CustomButton"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Material Button" />

Jetpack Compose Approach (Modern Standard)

@Composable
fun CustomButton(
    text: String,
    onClick: () -> Unit,
    enabled: Boolean = true
) {
    val colors = ButtonDefaults.buttonColors(
        containerColor = MaterialTheme.colorScheme.primary,
        disabledContainerColor = MaterialTheme.colorScheme.surfaceContainerHighest,
        contentColor = MaterialTheme.colorScheme.onPrimary,
        disabledContentColor = MaterialTheme.colorScheme.onSurfaceVariant
    )
    
    Button(
        onClick = onClick,
        enabled = enabled,
        colors = colors,
        shape = RoundedCornerShape(8.dp),
        modifier = Modifier.fillMaxWidth()
    ) {
        Text(text = text, fontWeight = FontWeight.Medium)
    }
}

রান করুন এবং পর্যবেক্ষণ করুন

  1. অ্যাপ চালান।
  2. নরমাল স্টেট: নীল গ্রেডিয়েন্ট ব্যাকগ্রাউন্ড।
  3. প্রেসড স্টেট: বাটনে হোল্ড করলে হলদে/অলিভ গ্রেডিয়েন্ট।
  4. ফোকাসড স্টেট: ট্যাব কী বা গেমপ্যাড দিয়ে ফোকাস করলে نارাঞ্জি গ্রেডিয়েন্ট।
  5. ডিজেবল্ড স্টেট: দ্বিতীয় বাটনটি গ্রে আউট থাকবে।

সেরা অনুশীলন (Best Practices)

প্যрадаইমরিকমেন্ডেশন
নতুন প্রজেক্টMaterialButton + ColorStateList বা Jetpack Compose
লিগ্যাসি প্রজেক্টAppCompatButton + Selector Drawable (এই টিউটোরিয়াল)
থিমিংHardcoded কালারের বদলে `?attr/colorPrimary` ব্যবহার করুন
অ্যাক্সেসিবিলিটিফোকাস স্টেট অবশ্যই ডিফাইন করুন (TalkBack/Keyboard nav)
পারফরম্যান্সSelector XML ক্যাশ হয়, রানটাইমে অ্যালোকেশন নেই

সাধারণ ভুলগুলো এড়িয়ে চলুন

  • ❌ `android:background` এ সরাসরি কালার দিয়ে স্টেট হ্যান্ডল করা যায় না (Selector লাগবে)।
  • ❌ `state_pressed` এবং `state_focused` এর অর্ডার মেনেই রাখুন (স্পেসিফিক স্টেট আগে, জেনেরিক পরে)।
  • ❌ `android:state_enabled="false"` আইটেম যোগ না করলে ডিজেবল্ড স্টেটে অপ্রত্যাশিত UI দেখায়।
  • ❌ বাটনের `android:textColor` স্টেট-লিস্ট দিতে চাইলে `ColorStateList` ব্যবহার করুন (আলাদা ফাইল)।

State List Drawable ব্যবহার করা অ্যান্ড্রয়েডে বাটন (এবং যেকোনো View) স্টাইল করার সবচেয়ে ক্লাসিক, লাইটওয়েট, এবং কম্প্যাটিবল পদ্ধতি। মডার্ন অ্যাপের জন্য MaterialButton বা Compose ব্যবহার করুন, কিন্তু এই কনসেপ্টটি বুঝতে থাকা অপরিহার্য।

  1. অ্যান্ড্রয়েডে বাটন কাস্টমাইজ করে টেক্সট এবং রঙ সেট করার পদ্ধতি

  2. অ্যান্ড্রয়েডে ডাইনামিক্যালি বাটন যোগ করা

  3. Android-এ বিভিন্ন অ্যাক্টিভিটির মধ্যে স্যুইচ করার পদ্ধতি

  4. স্ক্রিনশট নেওয়ার ৫টি সেরা ফ্রি অ্যান্ড্রয়েড অ্যাপ