diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
index b268ef3..723499e 100644
--- a/.idea/deploymentTargetSelector.xml
+++ b/.idea/deploymentTargetSelector.xml
@@ -4,6 +4,14 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/com/example/tampopo_client/viewmodels/ActivityViewModel.java b/app/src/main/java/com/example/tampopo_client/viewmodels/ActivityViewModel.java
index 455723a..10e914d 100644
--- a/app/src/main/java/com/example/tampopo_client/viewmodels/ActivityViewModel.java
+++ b/app/src/main/java/com/example/tampopo_client/viewmodels/ActivityViewModel.java
@@ -146,17 +146,30 @@
MutableLiveData> userActivitiesLiveData = friendToActivitiesLiveData.get(userId);
assert userActivitiesLiveData != null;
+ // 常に最新のアクティビティで上書きする。
+ //
+ // 以前は「ID・テキスト・更新時刻が完全一致している場合は更新をスキップ」することで
+ // LiveData の無駄な更新を抑制していたが、バックエンドの実装やデータ形式の違いにより
+ // 期待どおりに差分検知ができていない可能性がある。
+ //
+ // ここでは挙動の確実性を優先し、サーバから最新アクティビティが取得できたタイミングでは
+ // 必ず LiveData を更新するようにする。
+ // (同じ内容であっても UI 側に再通知されるだけなので副作用は小さい)
+
List userActivities = userActivitiesLiveData.getValue();
if (userActivities == null || userActivities.isEmpty()) {
- userActivitiesLiveData.postValue(List.of(latestActivity));
+ // 初回またはこれまでアクティビティが無い場合はそのまま反映
+ Log.d(ActivityViewModel.class.getSimpleName(), "updateFriendToActivitiesLiveData: initial set for user=" + userId
+ + ", activityId=" + latestActivity.getActivityId() + ", text=" + latestActivity.getText());
} else {
- if (userActivities.get(0).getActivityId().equals(latestActivity.getActivityId())) {
- return;
- }
-
- userActivitiesLiveData.postValue(List.of(latestActivity));
+ Activity previousLatest = userActivities.get(0);
+ Log.d(ActivityViewModel.class.getSimpleName(), "updateFriendToActivitiesLiveData: overwrite for user=" + userId
+ + " from activityId=" + previousLatest.getActivityId() + " to activityId=" + latestActivity.getActivityId());
}
+
+ // 最新アクティビティのみを保持するリストとして常に上書きする
+ userActivitiesLiveData.postValue(List.of(latestActivity));
}
/**
diff --git a/app/src/main/java/com/example/tampopo_client/views/MainActivity.java b/app/src/main/java/com/example/tampopo_client/views/MainActivity.java
index 9911876..24ec48b 100644
--- a/app/src/main/java/com/example/tampopo_client/views/MainActivity.java
+++ b/app/src/main/java/com/example/tampopo_client/views/MainActivity.java
@@ -388,118 +388,86 @@
i = 0;
for (String friendId : friends) {
- FriendIconView userView = userViews.get(friendId);
- //userViews.put(friendId, null);
- MutableLiveData> activitiesLiveData = activityViewModel.getActivitiesLiveDataFromUserId(friendId);
+ // lambda から参照するため final 変数に退避
+ final String currentFriendId = friendId;
-// for (String friendId : sortedFriendUserIds) {
-// FriendIconView userView = userViews.get(friendId);
-// MutableLiveData> activitiesLiveData = activityViewModel.getActivitiesLiveDataFromUserId(friendId);
- //nattyもしnullだったらって処理入れた気がする
-// if (userView == null) {
-// userView = new FriendIconView(
-// MainActivity.this,
-// friendId,
-// userViewModel.getNickname(friendId),
-// chatViewModel
-// );
-// userViews.put(friendId, userView);
-// }
- // 新しいユーザなので、アイコン+コメントを作成
- //FriendIconView container = new FriendIconView(this);
+ // すでに View が存在する場合は再利用し、新規に作らない
+ FriendIconView container = userViews.get(currentFriendId);
+ if (container == null) {
+ // 初回のみ FriendIconView を生成して配置する
+ container = new FriendIconView(MainActivity.this, currentFriendId, userViewModel.getNickname(currentFriendId), chatViewModel);
+ container.setPadding(16, 16, 16, 16);
+ container.setId(View.generateViewId());
- new Thread(new Runnable() {
- @Override
- public void run() {
- FriendIconView container = new FriendIconView(MainActivity.this, friendId, userViewModel.getNickname(friendId), chatViewModel);
+ ConstraintLayout.LayoutParams params = new ConstraintLayout.LayoutParams(
+ ConstraintLayout.LayoutParams.WRAP_CONTENT,
+ ConstraintLayout.LayoutParams.WRAP_CONTENT
+ );
+ container.setLayoutParams(params);
+ layout.addView(container);
- Handler handler = new Handler(Looper.getMainLooper());
- handler.post(new Runnable() {
- @Override
- public void run() {
- container.setPadding(16, 16, 16, 16);
- container.setId(View.generateViewId());
+ // Mapに登録、画面に追加
+ userViews.put(currentFriendId, container);
-// // ユーザのアイコン(固定)
-// ShapeableImageView iconView = new ShapeableImageView(MainActivity.this);
-// iconView.setLayoutParams(new LinearLayout.LayoutParams(100, 100));
-// iconView.setScaleType(ImageView.ScaleType.CENTER_CROP);
-// iconView.setStrokeColor(ContextCompat.getColorStateList(MainActivity.this, R.color.red));
-// iconView.setStrokeWidth(2f);
-// iconView.setShapeAppearanceModel(
-// iconView.getShapeAppearanceModel().toBuilder()
-// .setAllCornerSizes(50) // 丸く
-// .build()
-// );
- ConstraintLayout.LayoutParams params = new ConstraintLayout.LayoutParams(
- ConstraintLayout.LayoutParams.WRAP_CONTENT,
- ConstraintLayout.LayoutParams.WRAP_CONTENT
- );
- container.setLayoutParams(params);
- layout.addView(container);
-//
-// // ユーザIDに応じてアイコンリソースを決定(仮にハードコード or マッピング)
-// iconView.setImageResource(getUserIconResource(friendId)); // ←ここがポイント
+ // 位置決め
+ ConstraintSet set = new ConstraintSet();
+ set.clone(layout);
+ int marginTopInPx = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ marginTopInDp[i],
+ getResources().getDisplayMetrics()
+ );
+ int marginStartInPx = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ marginStartInDp[i],
+ getResources().getDisplayMetrics()
+ );
- // Mapに登録、画面に追加
- userViews.put(friendId, container);
-// userView = container;
-// messageList.addView(container);
+ set.connect(container.getId(), ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP, marginTopInPx);
+ set.connect(container.getId(), ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START, marginStartInPx);
- ConstraintSet set = new ConstraintSet();
- set.clone(layout);
- int marginTopInPx = (int) TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP,
- marginTopInDp[i],
- getResources().getDisplayMetrics()
- );
- int marginStartInPx = (int) TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP,
- marginStartInDp[i],
- getResources().getDisplayMetrics()
- );
+ set.applyTo(layout);
- set.connect(container.getId(), ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP, marginTopInPx);
- set.connect(container.getId(), ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START, marginStartInPx);
-
- set.applyTo(layout);
-
- if (i < 6) {
- i++;
- }
-
- // TODO: CHANGE
- activitiesLiveData.observe(MainActivity.this, activities -> {
- if (activities == null || activities.isEmpty()) return;
-
- Activity latest = activities.get(activities.size() - 1);
-
- // UIスレッドで安全に更新
- FriendIconView friendView = userViews.get(friendId);
- if (friendView != null) {
- friendView.setComment(latest.getText());
- Log.d("ActivityUpdate", friendId + " のコメントを更新: " + latest.getText());
- }
- });
-
- List sortedFriendUserIds = activityViewModel.getSortedFriendUserIds();
- int size = sortedFriendUserIds.size();
- List latestSix = sortedFriendUserIds.subList(Math.max(size - 6, 0), size);
-
- synchronized (recentUpdatedFriends) {
- if (latestSix.contains(friendId)) {
- recentUpdatedFriends.remove(friendId);
- recentUpdatedFriends.add(0, friendId);
- if (recentUpdatedFriends.size() > 6) {
- recentUpdatedFriends.remove(recentUpdatedFriends.size() - 1);
- }
- }
- }
- }
- });
+ if (i < 6) {
+ i++;
}
- }).start();
+ }
+
+ // LiveData の取得は View の再利用/新規作成に関わらず行う
+ final FriendIconView finalContainer = container;
+
+ MutableLiveData> activitiesLiveData = activityViewModel.getActivitiesLiveDataFromUserId(currentFriendId);
+
+ // FriendIconView 自身が持つアクティビティ監視用 Observer を利用して
+ // 各フレンドの最新アクティビティが更新されたタイミングで
+ // 吹き出しコメントが自動的に変わるようにする
+ activitiesLiveData.observe(MainActivity.this, finalContainer.getActivitiesObserver());
+
+ // 念のため、MainActivity 側でも直接 FriendIconView のコメントを更新する
+ // (getActivitiesObserver() が正しく動作していれば二重更新になるが、副作用はない)
+ activitiesLiveData.observe(MainActivity.this, activities -> {
+ if (activities == null || activities.isEmpty()) return;
+
+ // ActivityViewModel 側では常に最新 1 件のみを保持しているため index 0 を参照する
+ Activity latest = activities.get(0);
+ finalContainer.setComment(latest.getText());
+ Log.d("ActivityUpdate", currentFriendId + " のコメントを MainActivity から更新: " + latest.getText());
+ });
+
+ List sortedFriendUserIds = activityViewModel.getSortedFriendUserIds();
+ int size = sortedFriendUserIds.size();
+ List latestSix = sortedFriendUserIds.subList(Math.max(size - 6, 0), size);
+
+ synchronized (recentUpdatedFriends) {
+ if (latestSix.contains(currentFriendId)) {
+ recentUpdatedFriends.remove(currentFriendId);
+ recentUpdatedFriends.add(0, currentFriendId);
+ if (recentUpdatedFriends.size() > 6) {
+ recentUpdatedFriends.remove(recentUpdatedFriends.size() - 1);
+ }
+ }
+ }
}
}
}