2025-12-17 08:47:15 +08:00
|
|
|
|
package com.example.livestreaming;
|
|
|
|
|
|
|
|
|
|
|
|
import android.view.LayoutInflater;
|
|
|
|
|
|
import android.view.View;
|
|
|
|
|
|
import android.view.ViewGroup;
|
|
|
|
|
|
import android.widget.ImageView;
|
|
|
|
|
|
import android.widget.LinearLayout;
|
|
|
|
|
|
import android.widget.TextView;
|
|
|
|
|
|
|
|
|
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
|
|
import androidx.recyclerview.widget.DiffUtil;
|
|
|
|
|
|
import androidx.recyclerview.widget.ListAdapter;
|
|
|
|
|
|
import androidx.recyclerview.widget.RecyclerView;
|
|
|
|
|
|
|
2025-12-23 15:38:35 +08:00
|
|
|
|
import com.bumptech.glide.Glide;
|
2025-12-17 08:47:15 +08:00
|
|
|
|
import com.example.livestreaming.net.Room;
|
|
|
|
|
|
|
|
|
|
|
|
public class WaterfallRoomsAdapter extends ListAdapter<Room, WaterfallRoomsAdapter.RoomVH> {
|
|
|
|
|
|
|
|
|
|
|
|
public interface OnRoomClickListener {
|
|
|
|
|
|
void onRoomClick(Room room);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private final OnRoomClickListener onRoomClick;
|
|
|
|
|
|
|
|
|
|
|
|
public WaterfallRoomsAdapter(OnRoomClickListener onRoomClick) {
|
|
|
|
|
|
super(DIFF);
|
|
|
|
|
|
this.onRoomClick = onRoomClick;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@NonNull
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public RoomVH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
|
|
|
|
|
View view = LayoutInflater.from(parent.getContext())
|
|
|
|
|
|
.inflate(R.layout.item_room_waterfall, parent, false);
|
|
|
|
|
|
return new RoomVH(view, onRoomClick);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public void onBindViewHolder(@NonNull RoomVH holder, int position) {
|
|
|
|
|
|
holder.bind(getItem(position));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static class RoomVH extends RecyclerView.ViewHolder {
|
|
|
|
|
|
|
|
|
|
|
|
private final ImageView coverImage;
|
|
|
|
|
|
private final TextView liveBadge;
|
|
|
|
|
|
private final LinearLayout viewerCountLayout;
|
|
|
|
|
|
private final TextView viewerCount;
|
|
|
|
|
|
private final TextView roomTitle;
|
|
|
|
|
|
private final ImageView streamerAvatar;
|
|
|
|
|
|
private final TextView streamerName;
|
|
|
|
|
|
private final TextView hotBadge;
|
|
|
|
|
|
private final OnRoomClickListener onRoomClick;
|
|
|
|
|
|
|
|
|
|
|
|
RoomVH(View itemView, OnRoomClickListener onRoomClick) {
|
|
|
|
|
|
super(itemView);
|
|
|
|
|
|
this.onRoomClick = onRoomClick;
|
|
|
|
|
|
|
|
|
|
|
|
coverImage = itemView.findViewById(R.id.coverImage);
|
|
|
|
|
|
liveBadge = itemView.findViewById(R.id.liveBadge);
|
|
|
|
|
|
viewerCountLayout = itemView.findViewById(R.id.viewerCountLayout);
|
|
|
|
|
|
viewerCount = itemView.findViewById(R.id.viewerCount);
|
|
|
|
|
|
roomTitle = itemView.findViewById(R.id.roomTitle);
|
|
|
|
|
|
streamerAvatar = itemView.findViewById(R.id.streamerAvatar);
|
|
|
|
|
|
streamerName = itemView.findViewById(R.id.streamerName);
|
|
|
|
|
|
hotBadge = itemView.findViewById(R.id.hotBadge);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void bind(Room room) {
|
2025-12-23 18:09:56 +08:00
|
|
|
|
// TODO: 接入后端接口 - 从后端获取房间封面图片URL
|
|
|
|
|
|
// 接口路径: GET /api/rooms/{roomId}/cover
|
|
|
|
|
|
// 请求参数: roomId (路径参数)
|
|
|
|
|
|
// 返回数据格式: ApiResponse<{coverUrl: string}>
|
|
|
|
|
|
// 或者Room对象应包含coverUrl字段,直接从room.getCoverUrl()获取
|
|
|
|
|
|
// TODO: 接入后端接口 - 从后端获取主播头像URL
|
|
|
|
|
|
// 接口路径: GET /api/user/profile/{streamerId}
|
|
|
|
|
|
// 请求参数: streamerId (路径参数,从Room对象中获取streamerId)
|
|
|
|
|
|
// 返回数据格式: ApiResponse<{avatarUrl: string}>
|
|
|
|
|
|
// TODO: 接入后端接口 - 获取房间观看人数
|
|
|
|
|
|
// 接口路径: GET /api/rooms/{roomId}/viewers/count
|
|
|
|
|
|
// 请求参数: roomId (路径参数)
|
|
|
|
|
|
// 返回数据格式: ApiResponse<{viewerCount: number}>
|
|
|
|
|
|
// 或者Room对象应包含viewerCount字段,直接从room.getViewerCount()获取
|
2025-12-17 08:47:15 +08:00
|
|
|
|
if (room == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
// 设置标题
|
|
|
|
|
|
roomTitle.setText(room.getTitle() != null ? room.getTitle() : "(无标题)");
|
|
|
|
|
|
|
|
|
|
|
|
// 设置主播名称
|
|
|
|
|
|
streamerName.setText(room.getStreamerName() != null ? room.getStreamerName() : "");
|
|
|
|
|
|
|
2025-12-23 15:38:35 +08:00
|
|
|
|
// 加载封面图片
|
|
|
|
|
|
String seed = room.getId() != null ? room.getId() : String.valueOf(getBindingAdapterPosition());
|
|
|
|
|
|
int h = Math.abs(seed.hashCode());
|
|
|
|
|
|
int imgIndex = (h % 10);
|
|
|
|
|
|
String[] colors = {"ff6b6b", "4ecdc4", "45b7d1", "96ceb4", "ffeaa7", "dfe6e9", "fd79a8", "a29bfe", "00b894", "e17055"};
|
|
|
|
|
|
String color = colors[imgIndex];
|
|
|
|
|
|
String imageUrl = "https://placehold.co/600x450/" + color + "/ffffff?text=LIVE";
|
|
|
|
|
|
Glide.with(coverImage)
|
|
|
|
|
|
.load(imageUrl)
|
|
|
|
|
|
.placeholder(R.drawable.bg_cover_placeholder)
|
|
|
|
|
|
.centerCrop()
|
|
|
|
|
|
.into(coverImage);
|
|
|
|
|
|
|
2025-12-17 08:47:15 +08:00
|
|
|
|
// 设置直播状态
|
|
|
|
|
|
if (room.isLive()) {
|
|
|
|
|
|
liveBadge.setVisibility(View.VISIBLE);
|
|
|
|
|
|
viewerCountLayout.setVisibility(View.VISIBLE);
|
2025-12-17 16:33:59 +08:00
|
|
|
|
int viewers = getViewerCount(room);
|
|
|
|
|
|
viewerCount.setText(String.valueOf(viewers));
|
2025-12-17 08:47:15 +08:00
|
|
|
|
|
|
|
|
|
|
// 如果观看人数超过100,显示热门标签
|
2025-12-17 16:33:59 +08:00
|
|
|
|
if (viewers > 100) {
|
2025-12-17 08:47:15 +08:00
|
|
|
|
hotBadge.setVisibility(View.VISIBLE);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
hotBadge.setVisibility(View.GONE);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
liveBadge.setVisibility(View.GONE);
|
|
|
|
|
|
viewerCountLayout.setVisibility(View.GONE);
|
|
|
|
|
|
hotBadge.setVisibility(View.GONE);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 点击事件
|
|
|
|
|
|
itemView.setOnClickListener(v -> {
|
|
|
|
|
|
if (onRoomClick != null) {
|
|
|
|
|
|
onRoomClick.onRoomClick(room);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2025-12-17 16:33:59 +08:00
|
|
|
|
|
|
|
|
|
|
private int getViewerCount(Room room) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
String seed = room.getId() != null ? room.getId() : String.valueOf(getBindingAdapterPosition());
|
|
|
|
|
|
int h = Math.abs(seed.hashCode());
|
|
|
|
|
|
return (h % 380) + 5;
|
|
|
|
|
|
} catch (Exception ignored) {
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-12-17 08:47:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static final DiffUtil.ItemCallback<Room> DIFF = new DiffUtil.ItemCallback<Room>() {
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public boolean areItemsTheSame(@NonNull Room oldItem, @NonNull Room newItem) {
|
|
|
|
|
|
String o = oldItem.getId();
|
|
|
|
|
|
String n = newItem.getId();
|
|
|
|
|
|
return o != null && o.equals(n);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public boolean areContentsTheSame(@NonNull Room oldItem, @NonNull Room newItem) {
|
|
|
|
|
|
return oldItem.equals(newItem);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|