好友邀請、群邀請、群申請,每類又有「新邀請、已接受、已拒絕、對方接受、對方拒絕」等多種狀態。UI 需動態顯示原因、顯示/隱藏接受拒絕按鈕,點擊後需調用環信 API、更新本地數據庫、刷新列表。本文完整拆解 InviteAdapter 狀態機設計、OnInviteListener 回調鏈、LocalBroadcastManager 跨組件刷新機制。
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.hyphenate.easeui.widget.EaseTitleBar
app:titleBarTitle="邀請信息列表"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ListView
android:id="@+id/lv_invite"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
public class InviteAdapter extends BaseAdapter {
private final Context context;
private final List<InvationInfo> data = new ArrayList<>();
private final OnInviteListener listener;
public InviteAdapter(Context context, OnInviteListener listener) { ... }
public void refresh(List<InvationInfo> newData) {
if (newData != null) {
data.clear();
data.addAll(newData);
notifyDataSetChanged();
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
convertView = View.inflate(context, R.layout.item_invite, null);
holder.name = convertView.findViewById(R.id.tv_invite_name);
holder.reason = convertView.findViewById(R.id.tv_invite_reason);
holder.accept = convertView.findViewById(R.id.bt_invite_accept);
holder.reject = convertView.findViewById(R.id.bt_invite_reject);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
InvationInfo info = data.get(position);
bindView(holder, info); // 核心:狀態機綁定邏輯
return convertView;
}
// ...
}
InvationInfo 區分 聯系人邀請(user ≠ null)與 群邀請/申請(group ≠ null),每類有獨立狀態枚舉:
| 狀態 | 顯示原因 | 按鈕 | 操作回調 |
|---|---|---|---|
| NEW_INVITE | 理由或「添加好友」 | 顯示接受/拒絕 | onAccept / onReject |
| INVITE_ACCEPT | 理由或「接受邀請」 | 隱藏 | 無 |
| INVITE_ACCEPT_BY_PEER | 理由或「邀請被接受」 | 隱藏 | 無 |
| 狀態常量 | 場景 | 顯示文本 | 按鈕/回調 |
|---|---|---|---|
| NEW_GROUP_INVITE | 收到群邀請 | 「您收到了群邀請」 | 接受/拒絕 → onInviteAccept/Reject |
| NEW_GROUP_APPLICATION | 收到入群申請 | 「您收到了群申請」 | 接受/拒絕 → onApplicationAccept/Reject |
| GROUP_INVITE_ACCEPTED | 我的邀請被接受 | 「您的群邀請已被接收」 | 隱藏 |
| GROUP_APPLICATION_ACCEPTED | 我的申請被接受 | 「您的群申請已被接受」 | 隱藏 |
| GROUP_INVITE_DECLINED | 我的邀請被拒絕 | 「您的群邀請已被拒絕」 | 隱藏 |
| GROUP_APPLICATION_DECLINED | 我的申請被拒絕 | 「您的群申請已被拒絕」 | 隱藏 |
| GROUP_ACCEPT_INVITE | 我接受了邀請 | 「你接受了群邀請」 | 隱藏 |
| GROUP_REJECT_INVITE | 我拒絕了邀請 | 「您拒絕了群邀請」 | 隱藏 |
| GROUP_ACCEPT_APPLICATION | 我批准了申請 | 「您批准了群申請」 | 隱藏 |
| GROUP_REJECT_APPLICATION | 我拒絕了申請 | 「您拒絕了群申請」 | 隱藏 |
holder.accept.setOnClickListener(v -> listener.onAccept(info));
holder.reject.setOnClickListener(v -> listener.onReject(info));
// 群邀請按鈕綁定對應回調...
// 注意:Java 7 需用 final InvationInfo finalInfo = info; 匿名內部類捕獲
public interface OnInviteListener {
// 聯系人
void onAccept(InvationInfo info);
void onReject(InvationInfo info);
// 群邀請
void onInviteAccept(InvationInfo info);
void onInviteReject(InvationInfo info);
// 群申請
void onApplicationAccept(InvationInfo info);
void onApplicationReject(InvationInfo info);
}
private final OnInviteListener inviteListener = new OnInviteListener() {
@Override
public void onAccept(final InvationInfo info) {
Model.getInstance().getGlobalThreadPool().execute(() -> {
try {
// 1. 調用環信服務端接受好友邀請
EMClient.getInstance().contactManager().acceptInvitation(info.getUser().getHxid());
// 2. 更新本地數據庫狀態
Model.getInstance().getDbManager().getInviteTableDao()
.updateInvitationStatus(InvitationStatus.INVITE_ACCEPT, info.getUser().getHxid());
// 3. UI 線程刷新
runOnUiThread(() -> {
Toast.makeText(this, "接受了邀請", Toast.LENGTH_SHORT).show();
refresh(); // 重新查詢 DB 並 adapter.refresh()
});
} catch (HyphenateException e) {
runOnUiThread(() -> Toast.makeText(this, "接受失敗", Toast.LENGTH_SHORT).show());
}
});
}
// 其他 5 種回調結構完全一致...
// 群邀請用 groupManager().acceptInvitation/declineInvitation
// 群申請用 groupManager().acceptApplication/declineApplication
// 狀態更新為對應 GROUP_* 枚舉
};
// Activity onCreate 註冊
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this);
lbm.registerReceiver(inviteChangedReceiver,
new IntentFilter(Constant.CONTACT_INVITE_CHANGED));
private final BroadcastReceiver inviteChangedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// 收到邀請變化廣播(如收到新邀請推送、其他端操作同步)
adapter.refresh(Model.getInstance().getDbManager().getInviteTableDao().getInvitations());
}
};
// 發送廣播的時機(例如 ContactManager 的邀請監聽器中)
LocalBroadcastManager.getInstance(context).sendBroadcast(
new Intent(Constant.CONTACT_INVITE_CHANGED));
InvationInfo 封裝 user/group、狀態枚舉、原因BaseAdapter + ViewHolder + 狀態機綁定邏輯 集中在 bindViewOnInviteListener 定義 6 種操作語義,Activity 實現異步執行鏈refresh()LocalBroadcastManager 解耦推送接收/多端同步與 UI 刷新這套模式可直接複用到「系統通知」、「好友動態」等列表頁,實現高內聚、低耦合、易維護的 IM 交互頁面開發。