Giới thiệu
Ví dụ này minh họa việc đặt EditText trong custom notification layout. Tuy nhiên, RemoteViews không hỗ trợ tương tác thực sự với EditText (không focus, không gõ, không lấy text). Bài viết giải thích hạn chế và đưa ra giải pháp thay thế RemoteInput.
Các bước thực hiện (Demo hiển thị)
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 thông báo có EditText" />
</RelativeLayout>
Bước 3: Custom Layout với EditText (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="Nhập văn bản"
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 nội dung..."
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);
// CHỈ SET HINT/TEXT BAN ĐẦU - KHÔNG THỂ LẤY DỮ LIỆU SAU KHI USER NHẬP
contentView.setTextViewHint(R.id.etInput, "Gõ tin nhắn tại đây...");
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);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel notificationChannel = new NotificationChannel(
NOTIFICATION_CHANNEL_ID, "Kênh EditText Demo", 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>
⚠️ VẤN ĐỀ: EditText trong RemoteViews KHÔNG HOẠT ĐỘNG
Tại sao không thể nhập liệu?
- RemoteViews chạy trong process SystemUI, không phải process app
- EditText cần focus, input connection, IME - không khả thi cross-process
- RemoteViews chỉ render tĩnh, không xử lý event touch/text change
- User thấy EditText nhưng không tap được, không gõ được
✅ GIẢI PHÁP CHUẨN: RemoteInput (API 20+)
Android cung cấp RemoteInput cho phép nhập văn bản trực tiếp từ notification shade:
// 1. Tạo RemoteInput
RemoteInput remoteInput = new RemoteInput.Builder("key_reply")
.setLabel("Nhập phản hồi")
.setChoices(new String[]{"Đồng ý", "Từ chối", "Sau"}) // Quick replies tùy chọn
.build();
// 2. Tạo PendingIntent cho action "Trả lời"
Intent replyIntent = new Intent(this, NotificationActionReceiver.class);
replyIntent.setAction("REPLY_ACTION");
PendingIntent replyPendingIntent = PendingIntent.getBroadcast(
this, 0, replyIntent, PendingIntent.FLAG_IMMUTABLE);
// 3. Tạo Action với RemoteInput
NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(
R.drawable.ic_reply, "Trả lời", replyPendingIntent)
.addRemoteInput(remoteInput)
.setAllowGeneratedReplies(true)
.build();
// 4. Thêm vào Builder
mBuilder.addAction(replyAction);
Nhận kết quả trong BroadcastReceiver/Service:
public class NotificationActionReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
if (remoteInput != null) {
CharSequence reply = remoteInput.getCharSequence("key_reply");
if (reply != null) {
// XỬ LÝ VĂN BẢN USER NHẬP
String userText = reply.toString();
Log.d("REPLY", "User nhập: " + userText);
// Gửi lên server, cập nhật DB, v.v.
}
}
}
}
So sánh các phương pháp
| Phương pháp | Có nhập được? | Lấy text được? | Độ phức tạp |
|---|---|---|---|
| EditText trong RemoteViews | ❌ Không | ❌ Không | Dễ (nhưng vô dụng) |
| RemoteInput + Action | ✅ Có | ✅ Có | Trung bình |
| Mở Activity khi nhấn | ✅ Có | ✅ Có | Dễ |
| Bubble Notification (API 30+) | ✅ Có | ✅ Có | Cao |
Chạy thử nghiệm (Demo)
Chạy app → Tạo thông báo → Kéo shade → Thấy EditText hiển thị nhưng không thể gõ. Đây là hành vi mong đợi của RemoteViews.
