Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Android

สร้าง Notification ด้วย NotificationCompat.Builder ใน Android


Notification ใน Android

Notification คือข้อความแจ้งเตือนที่แสดงนอกแอป (Status Bar, Shade, Lock Screen) ใช้สำหรับแจ้งข่าวสาร, ข้อความ, Download Progress, Media Playback, ฯลฯ

NotificationCompat.Builder: API รองรับทุกเวอร์ชัน

ใช้ androidx.core.app.NotificationCompat.Builder เพื่อรองรับ API เก่าและใหม่ร่วมกัน

Dependency

dependencies {
    implementation 'androidx.core:core:1.12.0'
    // Material Design Notification (Optional)
    implementation 'com.google.android.material:material:1.11.0'
}

ขั้นตอนสร้าง Notification (Modern: API 26+ Requires Channel)

1. สร้าง Notification Channel (API 26+)

private void createNotificationChannel() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        String channelId = "default_channel";
        CharSequence name = "General Notifications";
        String description = "General app notifications";
        int importance = NotificationManager.IMPORTANCE_DEFAULT;
        
        NotificationChannel channel = new NotificationChannel(channelId, name, importance);
        channel.setDescription(description);
        channel.enableLights(true);
        channel.setLightColor(Color.BLUE);
        channel.enableVibration(true);
        channel.setVibrationPattern(new long[]{0, 500, 200, 500});
        channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
        
        NotificationManager manager = getSystemService(NotificationManager.class);
        manager.createNotificationChannel(channel);
    }
}

2. สร้างและแสดง Notification

private void showNotification() {
    String channelId = "default_channel";
    
    // Intent เมื่อกด Notification
    Intent intent = new Intent(this, MainActivity.class);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
    PendingIntent pendingIntent = PendingIntent.getActivity(
        this, 0, intent, 
        PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT
    );

    // Action Buttons
    Intent replyIntent = new Intent(this, ReplyReceiver.class);
    PendingIntent replyPendingIntent = PendingIntent.getBroadcast(
        this, 1, replyIntent, 
        PendingIntent.FLAG_IMMUTABLE
    );

    // Build Notification
    NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelId)
        .setSmallIcon(R.drawable.ic_notification) // Required: Small Icon (White/Transparent)
        .setContentTitle("New Message")
        .setContentText("You have a new message from John")
        .setSubText("2 minutes ago")
        .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_foreground))
        .setStyle(new NotificationCompat.BigTextStyle()
            .bigText("This is a longer message that will be shown when expanded..."))
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .setCategory(NotificationCompat.CATEGORY_MESSAGE)
        .setAutoCancel(true) //Dismiss when clicked
        .setContentIntent(pendingIntent)
        .addAction(R.drawable.ic_reply, "Reply", replyPendingIntent)
        .addAction(R.drawable.ic_delete, "Delete", deletePendingIntent)
        .setColor(Color.BLUE) // Accent color
        .setOnlyAlertOnce(true) // Alert sound/viberate only first time
        .setWhen(System.currentTimeMillis());

    // Show
    NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
    
    // Permission Check (Android 13+)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) 
                == PackageManager.PERMISSION_GRANTED) {
            notificationManager.notify(1, builder.build());
        }
    } else {
        notificationManager.notify(1, builder.build());
    }
}

Notification Styles (Style)

1. BigTextStyle (ข้อความยาว)

.setStyle(new NotificationCompat.BigTextStyle()
    .bigText("Very long text that will be fully displayed when expanded...")
    .setBigContentTitle("Expanded Title")
    .setSummaryText("Summary text"))

2. BigPictureStyle (ภาพใหญ่)

.setStyle(new NotificationCompat.BigPictureStyle()
    .bigPicture(bitmap)
    .bigLargeIcon(null) // Hide large icon when expanded
    .setSummaryText("Photo shared"))

3. InboxStyle (รายการหลายบรรทัด)

.setStyle(new NotificationCompat.InboxStyle()
    .addLine("Message 1")
    .addLine("Message 2")
    .addLine("Message 3")
    .setBigContentTitle("3 New Messages")
    .setSummaryText("+2 more"))

4. MessagingStyle (Chat/Conversation - API 24+)

NotificationCompat.MessagingStyle style = new NotificationCompat.MessagingStyle(
    new Person.Builder().setName("Me").setIcon(icon).build()
);
style.setConversationTitle("Group Chat");
style.addMessage("Hello!", System.currentTimeMillis(), 
    new Person.Builder().setName("John").setIcon(johnIcon).build());
style.addMessage("Hi there!", System.currentTimeMillis(), 
    new Person.Builder().setName("Jane").setIcon(janeIcon).build());

builder.setStyle(style);

5. MediaStyle (Media Playback)

.setStyle(new NotificationCompat.MediaStyle()
    .setMediaSession(mediaSession.getSessionToken())
    .setShowActionsInCompactView(0, 1, 2) // Show Play/Pause, Next in compact
    .setShowCancelButton(true)
    .setCancelButtonIntent(cancelPendingIntent))

Notification Permissions (Android 13+ / API 33+)




// Runtime Permission Request (Activity/Fragment)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
            != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this, 
            new String[]{Manifest.permission.POST_NOTIFICATIONS}, 
            REQUEST_CODE_NOTIFICATION_PERMISSION);
    }
}

PendingIntent Flags (สำคัญมาก API 23+)

Flagคำอธิบาย
FLAG_IMMUTABLEบังคับ API 23+ ป้องกันการ Mutate Intent (Security)
FLAG_MUTABLEอนุญาตให้แก้ไข Intent ได้ (ใช้เมื่อต้อง Fill ใน ClipData, etc.)
FLAG_UPDATE_CURRENTUpdate Extra Data ถ้ามี PendingIntent เดียวกันอยู่แล้ว
FLAG_CANCEL_CURRENTCancel เก่า สร้างใหม่
FLAG_NO_CREATEไม่สร้างใหม่ คืนค่า null ถ้ายังไม่มี
FLAG_ONE_SHOTใช้ครั้งเดียว แล้ว Cancel อัตโนมัติ

แนะนำ: PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT

จัดการ Notification (Update, Cancel, Group)

Update Notification

// ใช้ ID เดียวกัน (1) และ Notify ใหม่
builder.setContentText("Updated message");
notificationManager.notify(1, builder.build());

Cancel Notification

notificationManager.cancel(1); // Cancel by ID
notificationManager.cancelAll(); // Cancel All

Group Notifications (Stack)

String GROUP_KEY = "group_key_messages";

NotificationCompat.Builder builder1 = new NotificationCompat.Builder(this, channelId)
    .setGroup(GROUP_KEY)
    .setContentTitle("Message 1")
    .setContentText("First message");

NotificationCompat.Builder builder2 = new NotificationCompat.Builder(this, channelId)
    .setGroup(GROUP_KEY)
    .setContentTitle("Message 2")
    .setContentText("Second message");

// Summary Notification (Required for Group)
NotificationCompat.Builder summaryBuilder = new NotificationCompat.Builder(this, channelId)
    .setGroup(GROUP_KEY)
    .setGroupSummary(true)
    .setContentTitle("Messages")
    .setContentText("2 new messages")
    .setStyle(new NotificationCompat.InboxStyle()
        .addLine("Message 1")
        .addLine("Message 2")
        .setBigContentTitle("2 New Messages"));

notificationManager.notify(1, builder1.build());
notificationManager.notify(2, builder2.build());
notificationManager.notify(3, summaryBuilder.build()); // Summary ID ต่างจาก Item

ตัวอย่างครบถ้วน: MainActivity.java

public class MainActivity extends AppCompatActivity {
    private static final String CHANNEL_ID = "default_channel";
    private static final int NOTIFICATION_ID = 1;
    private NotificationManagerCompat notificationManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        createNotificationChannel();
        notificationManager = NotificationManagerCompat.from(this);
        
        findViewById(R.id.btnNotify).setOnClickListener(v -> showBasicNotification());
        findViewById(R.id.btnBigText).setOnClickListener(v -> showBigTextNotification());
        findViewById(R.id.btnProgress).setOnClickListener(v -> showProgressNotification());
    }

    private void createNotificationChannel() { /* ... โค้ดด้านบน ... */ }

    private void showBasicNotification() { /* ... โค้ดด้านบน ... */ }

    private void showBigTextNotification() {
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_notification)
            .setContentTitle("Long Article")
            .setContentText("Tap to read more...")
            .setStyle(new NotificationCompat.BigTextStyle()
                .bigText("Very long article content goes here..."))
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)
            .setAutoCancel(true)
            .setContentIntent(createPendingIntent());
        
        notify(builder);
    }

    private void showProgressNotification() {
        // สำหรับ Download/Upload Progress
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_download)
            .setContentTitle("Downloading...")
            .setContentText("0%")
            .setPriority(NotificationCompat.PRIORITY_LOW)
            .setOnlyAlertOnce(true)
            .setProgress(100, 0, false); // max, progress, indeterminate

        notificationManager.notify(NOTIFICATION_ID, builder.build());

        // Simulate Progress
        new Thread(() -> {
            for (int i = 0; i <= 100; i += 10) {
                try { Thread.sleep(500); } catch (InterruptedException ignored) {}
                builder.setProgress(100, i, false)
                       .setContentText(i + "%");
                notificationManager.notify(NOTIFICATION_ID, builder.build());
            }
            builder.setContentText("Complete")
                   .setProgress(0, 0, false)
                   .setSmallIcon(R.drawable.ic_done)
                   .setContentIntent(createPendingIntent())
                   .setAutoCancel(true);
            notificationManager.notify(NOTIFICATION_ID, builder.build());
        }).start();
    }

    private PendingIntent createPendingIntent() {
        Intent intent = new Intent(this, MainActivity.class);
        return PendingIntent.getActivity(this, 0, intent, 
            PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
    }

    private void notify(NotificationCompat.Builder builder) {
        if (hasNotificationPermission()) {
            notificationManager.notify(NOTIFICATION_ID, builder.build());
        }
    }

    private boolean hasNotificationPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            return ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
                    == PackageManager.PERMISSION_GRANTED;
        }
        return true;
    }
}

Best Practices

  • Channel ID คงที่ ต่อประเภท Notification (ไม่สร้าง Channel ใหม่ทุกครั้ง)
  • Small Icon: White/Transparent Only (Color จะถูก Tint ให้)
  • Priority/Importance: ตรงกันระหว่าง Builder และ Channel
  • Action Buttons: ไม่เกิน 3 ปุ่ม (Compact View)
  • Large Icon: ใช้สำหรับ User Avatar/Contact Photo (Circle)
  • Grouping: รวม Notification ประเภทเดียวกัน
  • Respect User Settings: ไม่ Spam, ให้ Importance ต่ำถ้าไม่สำคัญ

สรุป

  1. สร้าง NotificationChannel (API 26+)
  2. สร้าง NotificationCompat.Builder พร้อม Channel ID
  3. ตั้งค่า Icon, Title, Text, Style, Action, Intent
  4. ขอ Permission POST_NOTIFICATIONS (API 33+)
  5. แสดงด้วย NotificationManagerCompat.notify(id, notification)
  6. Update/Cancel/Group ด้วย ID เดียวกัน/Group Key