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

Cách tùy chỉnh giao diện thông báo Android (Custom Notification Layout)

Giới thiệu

Tạo thông báo với layout tùy chỉnh (custom view) bằng RemoteViews. Cho phép thay đổi background color, font, layout phức tạp, marquee text...

Yêu cầu & Hạn chế

  • Chỉ hỗ trợ các View cơ bản: TextView, ImageView, Button, ProgressBar, Chronometer, ViewFlipper...
  • Không hỗ trợ: RecyclerView, WebView, EditText, custom View class.
  • Layout được.inflate trong process hệ thống (SystemUI), không truy cập được resource app trực tiếp.
  • Từ Android 12 (API 31), custom notification bị hạn chế, khuyến nghị dùng Notification.Style chuẩn (BigText, BigPicture, Messaging, Media).

Code mẫu: Custom Layout với background màu & Marquee text

1. res/layout/custom_notification_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:id="@+id/layout"
    android:layout_width="match_parent"
    android:layout_height="64dp"
    android:background="@color/colorAccent"  <!-- Background màu tùy chỉnh -->
    android:padding="10dp">

    <ImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_alignParentStart="true"
        android:layout_marginEnd="10dp"
        android:contentDescription="@string/app_name"
        android:src="@mipmap/ic_launcher" />

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toEndOf="@id/image"
        android:text="Tiêu đề tùy chỉnh"
        android:textColor="#000"
        android:textSize="13sp"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/title"
        android:layout_toEndOf="@id/image"
        android:ellipsize="marquee"      <!-- Chữ chạy marquee -->
        android:singleLine="true"
        android:text="Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s..."
        android:textColor="#000"
        android:textSize="13sp" 
        android:focusable="true"
        android:focusableInTouchMode="true" />

</RelativeLayout>

2. MainActivity.java

package com.example.customnotification;

import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.NotificationCompat;
import android.view.View;
import android.widget.RemoteViews;

public class MainActivity extends AppCompatActivity {

    private static final String CHANNEL_ID = "custom_layout_channel";

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

    private void createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                    CHANNEL_ID, "Custom Layout", NotificationManager.IMPORTANCE_DEFAULT);
            getSystemService(NotificationManager.class).createNotificationChannel(channel);
        }
    }

    public void createNotification(View view) {
        // 1. Tạo RemoteViews trỏ đến layout custom
        RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.custom_notification_layout);
        
        // 2. Cập nhật nội dung động (tùy chọn)
        contentView.setTextViewText(R.id.title, "Tiêu đề động từ code");
        contentView.setTextViewText(R.id.text, "Nội dung marquee cập nhật lúc " + System.currentTimeMillis());
        // contentView.setImageViewResource(R.id.image, R.drawable.custom_icon);

        // 3. Build Notification với setContent(RemoteViews)
        NotificationManager manager = getSystemService(NotificationManager.class);
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
                .setSmallIcon(R.drawable.ic_launcher_foreground) // Vẫn cần small icon
                .setCustomContentView(contentView)      // Layout thu gọn (collapsed)
                .setCustomBigContentView(contentView)   // Layout mở rộng (expanded) - có thể dùng layout khác
                .setAutoCancel(true);

        manager.notify((int) System.currentTimeMillis(), builder.build());
    }
}

3. activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:padding="16dp"
    tools:context=".MainActivity">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_margin="16dp"
        android:onClick="createNotification"
        android:text="Tạo thông báo custom layout" />

</RelativeLayout>

Các phương thức setCustomView

Phương thứcHiển thị khiChiều cao tối đa
setCustomContentView()Thu gọn (collapsed)~64dp (1 dòng)
setCustomBigContentView()Mở rộng (expanded)~256dp (tùy thiết bị)
setCustomHeadsUpContentView()Heads-up (nổi lên màn hình)~64dp

Tương tác với View trong RemoteViews

// Gán click listener cho button trong custom layout
Intent intent = new Intent(this, ActionReceiver.class);
intent.setAction("CUSTOM_CLICK");
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
contentView.setOnClickPendingIntent(R.id.btn_custom, pendingIntent);

// Cập nhật ProgressBar
contentView.setProgressBar(R.id.progress, 100, 50, false);

// Cập nhật Chronometer
contentView.setChronometer(R.id.chrono, SystemClock.elapsedRealtime(), null, true);

Thay thế hiện đại: Notification.Style (Khuyên dùng API 31+)

Google khuyến nghị dùng các Style có sẵn thay vì custom layout:

  • NotificationCompat.BigTextStyle(): Text dài, tự cuộn.
  • NotificationCompat.BigPictureStyle(): Hình ảnh lớn.
  • NotificationCompat.MessagingStyle(): Chat, hội thoại.
  • NotificationCompat.MediaStyle(): Media player điều khiển.
  • NotificationCompat.DecoratedCustomViewStyle(): Custom nhưng giữ decor hệ thống (header, actions).
// Ví dụ BigTextStyle - đơn giản hơn custom layout
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.ic_launcher)
        .setContentTitle("Tiêu đề")
        .setStyle(new NotificationCompat.BigTextStyle()
                .bigText("Nội dung rất dài... sẽ tự động cuộn/hiển thị đầy đủ khi kéo mở rộng")
                .setBigContentTitle("Tiêu đề khi mở rộng")
                .setSummaryText("Tóm tắt"))
        .setAutoCancel(true);