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

Tạo Custom Push Notification cho ứng dụng Android

Giới thiệu

Ví dụ này minh họa cách tạo Custom Push Notification với layout tùy chỉnh (RemoteViews) bao gồm EditText cho phép người dùng nhập văn bản trực tiếp trên thông báo. Đây là nền tảng cho các tính năng như "Trả lời nhanh", "Tìm kiếm", "Feedback" ngay trên thông báo.

Kiến trúc

  • Custom Layout: XML layout với EditText, TextView, ImageView
  • RemoteViews: Bọc layout để hiển thị trong Notification
  • NotificationCompat.Builder: Xây dựng notification với custom content

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

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

Tạo project Android Studio mới.

Bước 2: Layout chính (res/layout/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"
    tools:context=".MainActivity">

    <Button
        android:onClick="createNotification"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_margin="16dp"
        android:text="Tạo Custom Push Notification" />

</RelativeLayout>

Bước 3: Custom Notification Layout (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="96dp"
    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="Custom Push Notification"
        android:textColor="#000"
        android:textSize="18sp" />

    <EditText
        android:id="@+id/etInput"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/title"
        android:layout_marginTop="8dp"
        android:layout_toEndOf="@id/image"
        android:hint="Nhập phản hồi tại đây..."
        android:inputType="text"
        android:textSize="14sp"
        android:background="@android:drawable/edit_text"
        android:padding="8dp" />

</RelativeLayout>

Bước 4: MainActivity (src/MainActivity.java)

package app.tutorialspoint.com.notifyme;

import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.RemoteViews;

public class MainActivity extends AppCompatActivity {
    public static final String NOTIFICATION_CHANNEL_ID = "10001";
    private final static String default_notification_channel_id = "default";

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

    public void createNotification(View view) {
        RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.custom_notification_layout);

        // CÓ THỂ SET GIÁ TRỊ BAN ĐẦU CHO EDITTEXT
        contentView.setTextViewText(R.id.title, "Phản hồi nhanh");
        contentView.setTextViewHint(R.id.etInput, "Gõ tin nhắn...");

        NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
            MainActivity.this, default_notification_channel_id);

        mBuilder.setContent(contentView);
        mBuilder.setSmallIcon(R.drawable.ic_launcher_foreground);
        mBuilder.setAutoCancel(true);
        mBuilder.setPriority(NotificationCompat.PRIORITY_HIGH);

        // THÊM HÀNH ĐỘNG "GỬI" (CẦN BROADCASTRECEIVER ĐỂ XỬ LÝ)
        // Xem phần mở rộng bên dưới

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            int importance = NotificationManager.IMPORTANCE_HIGH;
            NotificationChannel notificationChannel = new NotificationChannel(
                NOTIFICATION_CHANNEL_ID, "Custom Push Channel", importance);
            mBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
            assert mNotificationManager != null;
            mNotificationManager.createNotificationChannel(notificationChannel);
        }

        assert mNotificationManager != null;
        mNotificationManager.notify((int) System.currentTimeMillis(), mBuilder.build());
    }
}

Bước 5: AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="app.tutorialspoint.com.notifyme">

    <uses-permission android:name="android.permission.VIBRATE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>
</manifest>

Mở rộng: Xử lý nhập liệu từ EditText trên Notification

Lưu ý: RemoteViews KHÔNG hỗ trợ listener cho EditText. Để lấy dữ liệu, cần dùng:

Cách 1: BroadcastReceiver + Action Button

// Trong custom layout thêm Button "Gửi"
<Button
    android:id="@+id/btnSend"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Gửi" />

// Trong MainActivity
Intent sendIntent = new Intent(this, NotificationActionReceiver.class);
sendIntent.setAction("ACTION_SEND_REPLY");
PendingIntent sendPendingIntent = PendingIntent.getBroadcast(
    this, 0, sendIntent, PendingIntent.FLAG_IMMUTABLE);
contentView.setOnClickPendingIntent(R.id.btnSend, sendPendingIntent);

// BroadcastReceiver
public class NotificationActionReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // KHÔNG THỂ LẤY TEXT TỪ EDITTEXT TRỰC TIẾP
        // Cần dùng workaround: InputMethodService hoặc mở Activity
    }
}

Cách 2: Dùng RemoteInput (API 20+) - KHUYẾN NGHỊ

// Cho phép nhập text trực tiếp từ notification (Reply action)
RemoteInput remoteInput = new RemoteInput.Builder("reply_key")
    .setLabel("Trả lời...")
    .build();

NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(
    R.drawable.ic_reply, "Trả lời", replyPendingIntent)
    .addRemoteInput(remoteInput)
    .setAllowGeneratedReplies(true)
    .build();

mBuilder.addAction(replyAction);

// Trong Activity/Service nhận kết quả:
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
if (remoteInput != null) {
    String reply = remoteInput.getCharSequence("reply_key").toString();
}

Cách 3: Mở Activity khi nhấn EditText (giả lập)

Set PendingIntent cho EditText area → mở Activity có EditText thật → xử lý bình thường.

Hạn chế RemoteViews

  • Không hỗ trợ EditText interaction (không gõ được, không lấy text)
  • Chỉ hỗ trợ: TextView, ImageView, ProgressBar, Chronometer, Button, ImageButton
  • Không thể set OnClickListener cho View con (chỉ setOnClickPendingIntent)
  • Layout phức tạp có thể bị crop hoặc không render đúng

Chạy thử nghiệm

Chạy app → Tạo thông báo → Kéo shade → Thấy custom layout với EditText (chỉ hiển thị, không nhập được) → Cần dùng RemoteInput cho tính năng thực tế.

Tạo Custom Push Notification cho ứng dụng Android