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ức | Hiển thị khi | Chiề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);