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-31 19:41:22 +08:00
|
|
|
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
|
|
|
|
|
import com.bumptech.glide.request.RequestOptions;
|
2025-12-17 08:47:15 +08:00
|
|
|
import com.example.livestreaming.net.Room;
|
|
|
|
|
|
2025-12-31 19:41:22 +08:00
|
|
|
import java.util.Random;
|
|
|
|
|
|
2025-12-17 08:47:15 +08:00
|
|
|
public class WaterfallRoomsAdapter extends ListAdapter<Room, WaterfallRoomsAdapter.RoomVH> {
|
|
|
|
|
|
|
|
|
|
public interface OnRoomClickListener {
|
|
|
|
|
void onRoomClick(Room room);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private final OnRoomClickListener onRoomClick;
|
2025-12-31 19:41:22 +08:00
|
|
|
|
|
|
|
|
// 预定义的图片高度比例,模拟瀑布流效果
|
|
|
|
|
private static final float[] HEIGHT_RATIOS = {1.0f, 1.2f, 0.9f, 1.3f, 1.1f, 0.85f, 1.15f, 1.25f};
|
2025-12-17 08:47:15 +08:00
|
|
|
|
|
|
|
|
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) {
|
2025-12-31 19:41:22 +08:00
|
|
|
holder.bind(getItem(position), position);
|
2025-12-17 08:47:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
2025-12-31 19:41:22 +08:00
|
|
|
private final ImageView likeIcon;
|
|
|
|
|
private final TextView likeCount;
|
2025-12-17 08:47:15 +08:00
|
|
|
private final TextView hotBadge;
|
2025-12-31 19:41:22 +08:00
|
|
|
private final ImageView playIcon;
|
2025-12-17 08:47:15 +08:00
|
|
|
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);
|
2025-12-31 19:41:22 +08:00
|
|
|
likeIcon = itemView.findViewById(R.id.likeIcon);
|
|
|
|
|
likeCount = itemView.findViewById(R.id.likeCount);
|
2025-12-17 08:47:15 +08:00
|
|
|
hotBadge = itemView.findViewById(R.id.hotBadge);
|
2025-12-31 19:41:22 +08:00
|
|
|
playIcon = itemView.findViewById(R.id.playIcon);
|
2025-12-17 08:47:15 +08:00
|
|
|
}
|
|
|
|
|
|
2025-12-31 19:41:22 +08:00
|
|
|
void bind(Room room, int position) {
|
2025-12-17 08:47:15 +08:00
|
|
|
if (room == null) return;
|
|
|
|
|
|
|
|
|
|
// 设置标题
|
2025-12-31 19:41:22 +08:00
|
|
|
String title = room.getTitle();
|
|
|
|
|
if (title == null || title.isEmpty()) {
|
|
|
|
|
title = generateRandomTitle(position);
|
|
|
|
|
}
|
|
|
|
|
roomTitle.setText(title);
|
2025-12-17 08:47:15 +08:00
|
|
|
|
|
|
|
|
// 设置主播名称
|
2025-12-31 19:41:22 +08:00
|
|
|
String name = room.getStreamerName();
|
|
|
|
|
if (name == null || name.isEmpty()) {
|
|
|
|
|
name = generateRandomName(position);
|
|
|
|
|
}
|
|
|
|
|
streamerName.setText(name);
|
2025-12-17 08:47:15 +08:00
|
|
|
|
2025-12-31 19:41:22 +08:00
|
|
|
// 计算随机高度,实现瀑布流效果
|
|
|
|
|
String seed = room.getId() != null ? room.getId() : String.valueOf(position);
|
2025-12-23 15:38:35 +08:00
|
|
|
int h = Math.abs(seed.hashCode());
|
2025-12-31 19:41:22 +08:00
|
|
|
float ratio = HEIGHT_RATIOS[h % HEIGHT_RATIOS.length];
|
|
|
|
|
int baseHeight = (int) (itemView.getContext().getResources().getDisplayMetrics().density * 150);
|
|
|
|
|
int imageHeight = (int) (baseHeight * ratio);
|
|
|
|
|
|
|
|
|
|
ViewGroup.LayoutParams params = coverImage.getLayoutParams();
|
|
|
|
|
params.height = imageHeight;
|
|
|
|
|
coverImage.setLayoutParams(params);
|
|
|
|
|
|
|
|
|
|
// 加载封面图片 - 使用更真实的图片
|
|
|
|
|
loadCoverImage(room, position, h);
|
|
|
|
|
|
|
|
|
|
// 加载主播头像
|
|
|
|
|
loadAvatarImage(position);
|
|
|
|
|
|
|
|
|
|
// 设置点赞数
|
|
|
|
|
int likes = (h % 500) + 10;
|
|
|
|
|
likeCount.setText(formatNumber(likes));
|
|
|
|
|
likeIcon.setVisibility(View.VISIBLE);
|
|
|
|
|
likeCount.setVisibility(View.VISIBLE);
|
2025-12-23 15:38:35 +08:00
|
|
|
|
2025-12-17 08:47:15 +08:00
|
|
|
// 设置直播状态
|
|
|
|
|
if (room.isLive()) {
|
|
|
|
|
liveBadge.setVisibility(View.VISIBLE);
|
|
|
|
|
viewerCountLayout.setVisibility(View.VISIBLE);
|
2025-12-31 19:41:22 +08:00
|
|
|
int viewers = (h % 380) + 5;
|
|
|
|
|
viewerCount.setText(formatNumber(viewers));
|
|
|
|
|
playIcon.setVisibility(View.GONE);
|
2025-12-17 08:47:15 +08:00
|
|
|
|
2025-12-31 19:41:22 +08:00
|
|
|
// 热门标签
|
|
|
|
|
if (viewers > 200) {
|
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);
|
2025-12-31 19:41:22 +08:00
|
|
|
playIcon.setVisibility(View.VISIBLE);
|
2025-12-17 08:47:15 +08:00
|
|
|
hotBadge.setVisibility(View.GONE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 点击事件
|
|
|
|
|
itemView.setOnClickListener(v -> {
|
|
|
|
|
if (onRoomClick != null) {
|
|
|
|
|
onRoomClick.onRoomClick(room);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-12-17 16:33:59 +08:00
|
|
|
|
2025-12-31 19:41:22 +08:00
|
|
|
private void loadCoverImage(Room room, int position, int hash) {
|
|
|
|
|
// 使用多种图片源,让内容更丰富
|
|
|
|
|
String[] imageUrls = {
|
|
|
|
|
"https://picsum.photos/seed/" + (hash % 1000) + "/400/500",
|
|
|
|
|
"https://picsum.photos/seed/" + ((hash + 100) % 1000) + "/400/600",
|
|
|
|
|
"https://picsum.photos/seed/" + ((hash + 200) % 1000) + "/400/450",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
String imageUrl = imageUrls[position % imageUrls.length];
|
|
|
|
|
|
|
|
|
|
Glide.with(coverImage.getContext())
|
|
|
|
|
.load(imageUrl)
|
|
|
|
|
.apply(new RequestOptions()
|
|
|
|
|
.placeholder(R.drawable.bg_cover_placeholder)
|
|
|
|
|
.error(R.drawable.bg_cover_placeholder)
|
|
|
|
|
.centerCrop())
|
|
|
|
|
.into(coverImage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void loadAvatarImage(int position) {
|
|
|
|
|
String avatarUrl = "https://i.pravatar.cc/100?img=" + ((position % 70) + 1);
|
|
|
|
|
|
|
|
|
|
Glide.with(streamerAvatar.getContext())
|
|
|
|
|
.load(avatarUrl)
|
|
|
|
|
.apply(new RequestOptions()
|
|
|
|
|
.placeholder(R.drawable.ic_account_circle_24)
|
|
|
|
|
.error(R.drawable.ic_account_circle_24)
|
|
|
|
|
.circleCrop())
|
|
|
|
|
.into(streamerAvatar);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String generateRandomTitle(int position) {
|
|
|
|
|
String[] titles = {
|
|
|
|
|
"#健身穿搭 #一万种健与美 #健身女孩 #完美身材",
|
|
|
|
|
"避雷秀厢附近租房",
|
|
|
|
|
"找线下收U换现 可长期合作 有实力的私面聊",
|
|
|
|
|
"我提笔不为离愁 只为你转身回眸",
|
|
|
|
|
"今日穿搭分享 #日常穿搭 #时尚",
|
|
|
|
|
"周末vlog #生活记录 #美好生活",
|
|
|
|
|
"美食探店 #吃货日常 #美食推荐",
|
|
|
|
|
"旅行日记 #风景 #旅行攻略",
|
|
|
|
|
"护肤心得分享 #护肤 #美妆",
|
|
|
|
|
"读书笔记 #好书推荐 #阅读"
|
|
|
|
|
};
|
|
|
|
|
return titles[position % titles.length];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String generateRandomName(int position) {
|
|
|
|
|
String[] names = {
|
|
|
|
|
"小桃兔兔", "别管我了", "火火", "RicLei",
|
|
|
|
|
"甜甜圈", "小确幸", "追光者", "星河漫步",
|
|
|
|
|
"清风徐来", "月光宝盒"
|
|
|
|
|
};
|
|
|
|
|
return names[position % names.length];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String formatNumber(int num) {
|
|
|
|
|
if (num >= 10000) {
|
|
|
|
|
return String.format("%.1fw", num / 10000.0);
|
|
|
|
|
} else if (num >= 1000) {
|
|
|
|
|
return String.format("%.1fk", num / 1000.0);
|
2025-12-17 16:33:59 +08:00
|
|
|
}
|
2025-12-31 19:41:22 +08:00
|
|
|
return String.valueOf(num);
|
2025-12-17 16:33:59 +08:00
|
|
|
}
|
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);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|