question:未修复好直播功能+未部署
This commit is contained in:
parent
37ee807c8a
commit
b3726557e5
|
|
@ -44,7 +44,9 @@ import com.google.android.material.textfield.MaterialAutoCompleteTextView;
|
||||||
import com.google.android.material.textfield.TextInputLayout;
|
import com.google.android.material.textfield.TextInputLayout;
|
||||||
import com.example.livestreaming.net.ApiClient;
|
import com.example.livestreaming.net.ApiClient;
|
||||||
import com.example.livestreaming.net.ApiResponse;
|
import com.example.livestreaming.net.ApiResponse;
|
||||||
|
import com.example.livestreaming.net.ConversationResponse;
|
||||||
import com.example.livestreaming.net.CreateRoomRequest;
|
import com.example.livestreaming.net.CreateRoomRequest;
|
||||||
|
import com.example.livestreaming.net.PageResponse;
|
||||||
import com.example.livestreaming.net.Room;
|
import com.example.livestreaming.net.Room;
|
||||||
import com.example.livestreaming.net.StreamConfig;
|
import com.example.livestreaming.net.StreamConfig;
|
||||||
|
|
||||||
|
|
@ -52,6 +54,7 @@ import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.Callback;
|
import retrofit2.Callback;
|
||||||
|
|
@ -1826,7 +1829,19 @@ public class MainActivity extends AppCompatActivity {
|
||||||
/**
|
/**
|
||||||
* 获取所有直播间并筛选出关注用户的直播间
|
* 获取所有直播间并筛选出关注用户的直播间
|
||||||
*/
|
*/
|
||||||
private void fetchAndFilterFollowRooms(List<Map<String, Object>>
|
private void fetchAndFilterFollowRooms(List<Map<String, Object>> followList) {
|
||||||
|
// 从关注列表中提取用户ID
|
||||||
|
if (followList == null || followList.isEmpty()) {
|
||||||
|
if (adapter != null) {
|
||||||
|
adapter.submitList(new ArrayList<>());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// TODO: 实现根据关注列表筛选直播间
|
||||||
|
if (adapter != null) {
|
||||||
|
adapter.submitList(new ArrayList<>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建发现页面的房间列表(推荐算法前端实现)
|
* 构建发现页面的房间列表(推荐算法前端实现)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
package com.example.livestreaming;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class Post implements Serializable {
|
||||||
|
private String id;
|
||||||
|
private String userId;
|
||||||
|
private String userName;
|
||||||
|
private String userAvatar;
|
||||||
|
private String content;
|
||||||
|
private String imageUrl;
|
||||||
|
private String category;
|
||||||
|
private long timestamp;
|
||||||
|
private int likeCount;
|
||||||
|
private int commentCount;
|
||||||
|
private boolean isLiked;
|
||||||
|
|
||||||
|
public Post() {}
|
||||||
|
|
||||||
|
public Post(String id, String userId, String userName, String content, String category) {
|
||||||
|
this.id = id;
|
||||||
|
this.userId = userId;
|
||||||
|
this.userName = userName;
|
||||||
|
this.content = content;
|
||||||
|
this.category = category;
|
||||||
|
this.timestamp = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() { return id; }
|
||||||
|
public void setId(String id) { this.id = id; }
|
||||||
|
|
||||||
|
public String getUserId() { return userId; }
|
||||||
|
public void setUserId(String userId) { this.userId = userId; }
|
||||||
|
|
||||||
|
public String getUserName() { return userName; }
|
||||||
|
public void setUserName(String userName) { this.userName = userName; }
|
||||||
|
|
||||||
|
public String getUserAvatar() { return userAvatar; }
|
||||||
|
public void setUserAvatar(String userAvatar) { this.userAvatar = userAvatar; }
|
||||||
|
|
||||||
|
public String getContent() { return content; }
|
||||||
|
public void setContent(String content) { this.content = content; }
|
||||||
|
|
||||||
|
public String getImageUrl() { return imageUrl; }
|
||||||
|
public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; }
|
||||||
|
|
||||||
|
public String getCategory() { return category; }
|
||||||
|
public void setCategory(String category) { this.category = category; }
|
||||||
|
|
||||||
|
public long getTimestamp() { return timestamp; }
|
||||||
|
public void setTimestamp(long timestamp) { this.timestamp = timestamp; }
|
||||||
|
|
||||||
|
public int getLikeCount() { return likeCount; }
|
||||||
|
public void setLikeCount(int likeCount) { this.likeCount = likeCount; }
|
||||||
|
|
||||||
|
public int getCommentCount() { return commentCount; }
|
||||||
|
public void setCommentCount(int commentCount) { this.commentCount = commentCount; }
|
||||||
|
|
||||||
|
public boolean isLiked() { return isLiked; }
|
||||||
|
public void setLiked(boolean liked) { isLiked = liked; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
package com.example.livestreaming;
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class PostAdapter extends RecyclerView.Adapter<PostAdapter.PostViewHolder> {
|
||||||
|
|
||||||
|
private List<Post> posts = new ArrayList<>();
|
||||||
|
|
||||||
|
public void setPosts(List<Post> posts) {
|
||||||
|
this.posts = posts != null ? posts : new ArrayList<>();
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPost(Post post) {
|
||||||
|
if (post != null) {
|
||||||
|
posts.add(0, post);
|
||||||
|
notifyItemInserted(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public PostViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View view = LayoutInflater.from(parent.getContext())
|
||||||
|
.inflate(R.layout.item_post, parent, false);
|
||||||
|
return new PostViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull PostViewHolder holder, int position) {
|
||||||
|
Post post = posts.get(position);
|
||||||
|
holder.bind(post);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return posts.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
static class PostViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
private final TextView tvUserName;
|
||||||
|
private final TextView tvContent;
|
||||||
|
private final TextView tvTime;
|
||||||
|
private final TextView tvLikeCount;
|
||||||
|
private final TextView tvCommentCount;
|
||||||
|
|
||||||
|
public PostViewHolder(@NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
tvUserName = itemView.findViewById(R.id.tv_user_name);
|
||||||
|
tvContent = itemView.findViewById(R.id.tv_content);
|
||||||
|
tvTime = itemView.findViewById(R.id.tv_time);
|
||||||
|
tvLikeCount = itemView.findViewById(R.id.tv_like_count);
|
||||||
|
tvCommentCount = itemView.findViewById(R.id.tv_comment_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bind(Post post) {
|
||||||
|
if (tvUserName != null) tvUserName.setText(post.getUserName());
|
||||||
|
if (tvContent != null) tvContent.setText(post.getContent());
|
||||||
|
if (tvTime != null) {
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("MM-dd HH:mm", Locale.getDefault());
|
||||||
|
tvTime.setText(sdf.format(new Date(post.getTimestamp())));
|
||||||
|
}
|
||||||
|
if (tvLikeCount != null) tvLikeCount.setText(String.valueOf(post.getLikeCount()));
|
||||||
|
if (tvCommentCount != null) tvCommentCount.setText(String.valueOf(post.getCommentCount()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
package com.example.livestreaming;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class PostManager {
|
||||||
|
private static final String PREFS_NAME = "posts_prefs";
|
||||||
|
private static final String KEY_POSTS = "posts";
|
||||||
|
private static final Gson gson = new Gson();
|
||||||
|
|
||||||
|
public static void savePost(Context context, Post post) {
|
||||||
|
List<Post> posts = getAllPosts(context);
|
||||||
|
posts.add(0, post);
|
||||||
|
savePosts(context, posts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Post> getAllPosts(Context context) {
|
||||||
|
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
||||||
|
String json = prefs.getString(KEY_POSTS, "[]");
|
||||||
|
Type type = new TypeToken<List<Post>>(){}.getType();
|
||||||
|
List<Post> posts = gson.fromJson(json, type);
|
||||||
|
return posts != null ? posts : new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Post> getPostsByCategory(Context context, String category) {
|
||||||
|
return getAllPosts(context).stream()
|
||||||
|
.filter(p -> category.equals(p.getCategory()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void savePosts(Context context, List<Post> posts) {
|
||||||
|
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
||||||
|
prefs.edit().putString(KEY_POSTS, gson.toJson(posts)).apply();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
package com.example.livestreaming;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
public class PublishPostHelper {
|
||||||
|
|
||||||
|
public interface PublishCallback {
|
||||||
|
void onSuccess(Post post);
|
||||||
|
void onError(String error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void showPublishDialog(Activity activity, String category, PublishCallback callback) {
|
||||||
|
EditText input = new EditText(activity);
|
||||||
|
input.setHint("输入内容...");
|
||||||
|
|
||||||
|
new AlertDialog.Builder(activity)
|
||||||
|
.setTitle("发布动态")
|
||||||
|
.setView(input)
|
||||||
|
.setPositiveButton("发布", (dialog, which) -> {
|
||||||
|
String content = input.getText().toString().trim();
|
||||||
|
if (content.isEmpty()) {
|
||||||
|
Toast.makeText(activity, "内容不能为空", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Post post = new Post();
|
||||||
|
post.setId(String.valueOf(System.currentTimeMillis()));
|
||||||
|
post.setContent(content);
|
||||||
|
post.setCategory(category);
|
||||||
|
post.setUserName("用户");
|
||||||
|
post.setTimestamp(System.currentTimeMillis());
|
||||||
|
|
||||||
|
PostManager.savePost(activity, post);
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onSuccess(post);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setNegativeButton("取消", null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
package com.example.livestreaming;
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.DiffUtil;
|
||||||
|
import androidx.recyclerview.widget.ListAdapter;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.example.livestreaming.net.Room;
|
||||||
|
|
||||||
|
public class RoomAdapter extends ListAdapter<Room, RoomAdapter.RoomViewHolder> {
|
||||||
|
|
||||||
|
private OnRoomClickListener listener;
|
||||||
|
|
||||||
|
public interface OnRoomClickListener {
|
||||||
|
void onRoomClick(Room room);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoomAdapter() {
|
||||||
|
super(new DiffUtil.ItemCallback<Room>() {
|
||||||
|
@Override
|
||||||
|
public boolean areItemsTheSame(@NonNull Room oldItem, @NonNull Room newItem) {
|
||||||
|
return oldItem.getId() != null && oldItem.getId().equals(newItem.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean areContentsTheSame(@NonNull Room oldItem, @NonNull Room newItem) {
|
||||||
|
return oldItem.equals(newItem);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnRoomClickListener(OnRoomClickListener listener) {
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public RoomViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View view = LayoutInflater.from(parent.getContext())
|
||||||
|
.inflate(R.layout.item_room, parent, false);
|
||||||
|
return new RoomViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull RoomViewHolder holder, int position) {
|
||||||
|
Room room = getItem(position);
|
||||||
|
holder.bind(room, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class RoomViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
private final TextView tvTitle;
|
||||||
|
private final TextView tvStreamer;
|
||||||
|
private final TextView tvLikeCount;
|
||||||
|
private final ImageView ivCover;
|
||||||
|
private final View liveIndicator;
|
||||||
|
|
||||||
|
public RoomViewHolder(@NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
tvTitle = itemView.findViewById(R.id.roomTitle);
|
||||||
|
tvStreamer = itemView.findViewById(R.id.streamerName);
|
||||||
|
tvLikeCount = itemView.findViewById(R.id.likeCount);
|
||||||
|
ivCover = itemView.findViewById(R.id.coverImage);
|
||||||
|
liveIndicator = itemView.findViewById(R.id.liveBadge);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bind(Room room, OnRoomClickListener listener) {
|
||||||
|
if (tvTitle != null) tvTitle.setText(room.getTitle());
|
||||||
|
if (tvStreamer != null) tvStreamer.setText(room.getStreamerName());
|
||||||
|
if (tvLikeCount != null) tvLikeCount.setText(String.valueOf(room.getViewerCount()));
|
||||||
|
|
||||||
|
if (liveIndicator != null) {
|
||||||
|
liveIndicator.setVisibility(room.isLive() ? View.VISIBLE : View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ivCover != null && room.getCoverImage() != null) {
|
||||||
|
Glide.with(itemView.getContext())
|
||||||
|
.load(room.getCoverImage())
|
||||||
|
.placeholder(android.R.drawable.ic_menu_gallery)
|
||||||
|
.into(ivCover);
|
||||||
|
}
|
||||||
|
|
||||||
|
itemView.setOnClickListener(v -> {
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onRoomClick(room);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -100,12 +100,28 @@ public class RoomDetailActivity extends AppCompatActivity {
|
||||||
// WebSocket - 弹幕
|
// WebSocket - 弹幕
|
||||||
private WebSocket chatWebSocket;
|
private WebSocket chatWebSocket;
|
||||||
private OkHttpClient chatWsClient;
|
private OkHttpClient chatWsClient;
|
||||||
private static final String WS_CHAT_BASE_URL = "ws://192.168.1.164:8081/ws/live/chat/";
|
|
||||||
|
|
||||||
// WebSocket - 在线人数
|
// WebSocket - 在线人数
|
||||||
private WebSocket onlineCountWebSocket;
|
private WebSocket onlineCountWebSocket;
|
||||||
private OkHttpClient onlineCountWsClient;
|
private OkHttpClient onlineCountWsClient;
|
||||||
private static final String WS_ONLINE_BASE_URL = "ws://192.168.1.164:8081/ws/live/";
|
|
||||||
|
// 动态获取WebSocket URL
|
||||||
|
private String getWsChatBaseUrl() {
|
||||||
|
String baseUrl = ApiClient.getCurrentBaseUrl(this);
|
||||||
|
if (baseUrl == null || baseUrl.isEmpty()) {
|
||||||
|
baseUrl = "http://192.168.1.164:8081/";
|
||||||
|
}
|
||||||
|
// 将 http:// 转换为 ws://
|
||||||
|
return baseUrl.replace("http://", "ws://").replace("https://", "wss://") + "ws/live/chat/";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getWsOnlineBaseUrl() {
|
||||||
|
String baseUrl = ApiClient.getCurrentBaseUrl(this);
|
||||||
|
if (baseUrl == null || baseUrl.isEmpty()) {
|
||||||
|
baseUrl = "http://192.168.1.164:8081/";
|
||||||
|
}
|
||||||
|
return baseUrl.replace("http://", "ws://").replace("https://", "wss://") + "ws/live/";
|
||||||
|
}
|
||||||
|
|
||||||
// WebSocket 心跳检测 - 弹幕
|
// WebSocket 心跳检测 - 弹幕
|
||||||
private Runnable chatHeartbeatRunnable;
|
private Runnable chatHeartbeatRunnable;
|
||||||
|
|
@ -265,7 +281,7 @@ public class RoomDetailActivity extends AppCompatActivity {
|
||||||
.pingInterval(30, java.util.concurrent.TimeUnit.SECONDS) // OkHttp 内置 ping
|
.pingInterval(30, java.util.concurrent.TimeUnit.SECONDS) // OkHttp 内置 ping
|
||||||
.build();
|
.build();
|
||||||
Request request = new Request.Builder()
|
Request request = new Request.Builder()
|
||||||
.url(WS_CHAT_BASE_URL + roomId)
|
.url(getWsChatBaseUrl() + roomId)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
chatWebSocket = chatWsClient.newWebSocket(request, new WebSocketListener() {
|
chatWebSocket = chatWsClient.newWebSocket(request, new WebSocketListener() {
|
||||||
|
|
@ -372,7 +388,7 @@ public class RoomDetailActivity extends AppCompatActivity {
|
||||||
String clientId = (userIdStr != null && !userIdStr.isEmpty()) ?
|
String clientId = (userIdStr != null && !userIdStr.isEmpty()) ?
|
||||||
userIdStr :
|
userIdStr :
|
||||||
"guest_" + System.currentTimeMillis();
|
"guest_" + System.currentTimeMillis();
|
||||||
String wsUrl = WS_ONLINE_BASE_URL + roomId + "?clientId=" + clientId;
|
String wsUrl = getWsOnlineBaseUrl() + roomId + "?clientId=" + clientId;
|
||||||
|
|
||||||
onlineCountWsClient = new OkHttpClient.Builder()
|
onlineCountWsClient = new OkHttpClient.Builder()
|
||||||
.pingInterval(30, java.util.concurrent.TimeUnit.SECONDS)
|
.pingInterval(30, java.util.concurrent.TimeUnit.SECONDS)
|
||||||
|
|
@ -1050,14 +1066,19 @@ public class RoomDetailActivity extends AppCompatActivity {
|
||||||
if (ijkSurface == null) return;
|
if (ijkSurface == null) return;
|
||||||
|
|
||||||
IjkMediaPlayer p = new IjkMediaPlayer();
|
IjkMediaPlayer p = new IjkMediaPlayer();
|
||||||
p.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0);
|
// 优化缓冲设置,减少卡顿
|
||||||
|
p.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 1); // 开启缓冲
|
||||||
p.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 1);
|
p.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 1);
|
||||||
p.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fflags", "nobuffer");
|
p.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fflags", "nobuffer");
|
||||||
p.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", 1);
|
p.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", 100000); // 100ms分析时长
|
||||||
p.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 1024);
|
p.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 10240); // 10KB探测大小
|
||||||
p.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 1);
|
p.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 5); // 允许丢帧
|
||||||
p.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max_cached_duration", 300);
|
p.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max_cached_duration", 3000); // 3秒缓存
|
||||||
p.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", 1);
|
p.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "min_frames", 50); // 最小缓冲帧数
|
||||||
|
p.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", 0); // 关闭无限缓冲
|
||||||
|
p.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "reconnect", 1); // 断线重连
|
||||||
|
p.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "reconnect_streamed", 1);
|
||||||
|
p.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "reconnect_delay_max", 5); // 最大重连延迟5秒
|
||||||
|
|
||||||
p.setOnPreparedListener(mp -> {
|
p.setOnPreparedListener(mp -> {
|
||||||
binding.offlineLayout.setVisibility(View.GONE);
|
binding.offlineLayout.setVisibility(View.GONE);
|
||||||
|
|
@ -1066,14 +1087,36 @@ public class RoomDetailActivity extends AppCompatActivity {
|
||||||
});
|
});
|
||||||
|
|
||||||
p.setOnErrorListener((IMediaPlayer mp, int what, int extra) -> {
|
p.setOnErrorListener((IMediaPlayer mp, int what, int extra) -> {
|
||||||
|
android.util.Log.e("IjkPlayer", "播放错误: what=" + what + ", extra=" + extra);
|
||||||
if (ijkFallbackTried || TextUtils.isEmpty(ijkFallbackHlsUrl)) {
|
if (ijkFallbackTried || TextUtils.isEmpty(ijkFallbackHlsUrl)) {
|
||||||
binding.offlineLayout.setVisibility(View.VISIBLE);
|
binding.offlineLayout.setVisibility(View.VISIBLE);
|
||||||
|
// 5秒后尝试重新连接
|
||||||
|
handler.postDelayed(() -> {
|
||||||
|
if (!isFinishing() && !isDestroyed() && room != null && room.isLive()) {
|
||||||
|
fetchRoom(); // 重新获取房间信息并播放
|
||||||
|
}
|
||||||
|
}, 5000);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
ijkFallbackTried = true;
|
ijkFallbackTried = true;
|
||||||
startHls(ijkFallbackHlsUrl, null);
|
startHls(ijkFallbackHlsUrl, null);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 添加缓冲监听
|
||||||
|
p.setOnInfoListener((mp, what, extra) -> {
|
||||||
|
if (what == IMediaPlayer.MEDIA_INFO_BUFFERING_START) {
|
||||||
|
android.util.Log.d("IjkPlayer", "开始缓冲...");
|
||||||
|
// 可以显示加载指示器
|
||||||
|
} else if (what == IMediaPlayer.MEDIA_INFO_BUFFERING_END) {
|
||||||
|
android.util.Log.d("IjkPlayer", "缓冲结束");
|
||||||
|
// 隐藏加载指示器
|
||||||
|
} else if (what == IMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) {
|
||||||
|
android.util.Log.d("IjkPlayer", "视频开始渲染");
|
||||||
|
binding.offlineLayout.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
ijkPlayer = p;
|
ijkPlayer = p;
|
||||||
try {
|
try {
|
||||||
|
|
@ -1081,6 +1124,7 @@ public class RoomDetailActivity extends AppCompatActivity {
|
||||||
p.setDataSource(url);
|
p.setDataSource(url);
|
||||||
p.prepareAsync();
|
p.prepareAsync();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
android.util.Log.e("IjkPlayer", "播放器初始化失败: " + e.getMessage());
|
||||||
if (!TextUtils.isEmpty(ijkFallbackHlsUrl)) {
|
if (!TextUtils.isEmpty(ijkFallbackHlsUrl)) {
|
||||||
startHls(ijkFallbackHlsUrl, null);
|
startHls(ijkFallbackHlsUrl, null);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,32 @@
|
||||||
package com.example.livestreaming.net;
|
package com.example.livestreaming.net;
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName;
|
|
||||||
|
|
||||||
public class ConversationResponse {
|
public class ConversationResponse {
|
||||||
|
private Integer id;
|
||||||
@SerializedName("id")
|
private Integer targetUserId;
|
||||||
private String id;
|
private String targetUserName;
|
||||||
|
private String targetUserAvatar;
|
||||||
@SerializedName("title")
|
|
||||||
private String title;
|
|
||||||
|
|
||||||
@SerializedName("lastMessage")
|
|
||||||
private String lastMessage;
|
private String lastMessage;
|
||||||
|
private String lastMessageTime;
|
||||||
@SerializedName("timeText")
|
|
||||||
private String timeText;
|
|
||||||
|
|
||||||
@SerializedName("unreadCount")
|
|
||||||
private Integer unreadCount;
|
private Integer unreadCount;
|
||||||
|
|
||||||
@SerializedName("muted")
|
public Integer getId() { return id; }
|
||||||
private Boolean muted;
|
public void setId(Integer id) { this.id = id; }
|
||||||
|
|
||||||
@SerializedName("avatarUrl")
|
public Integer getTargetUserId() { return targetUserId; }
|
||||||
private String avatarUrl;
|
public void setTargetUserId(Integer targetUserId) { this.targetUserId = targetUserId; }
|
||||||
|
|
||||||
@SerializedName("otherUserId")
|
public String getTargetUserName() { return targetUserName; }
|
||||||
private Integer otherUserId;
|
public void setTargetUserName(String targetUserName) { this.targetUserName = targetUserName; }
|
||||||
|
|
||||||
|
public String getTargetUserAvatar() { return targetUserAvatar; }
|
||||||
|
public void setTargetUserAvatar(String targetUserAvatar) { this.targetUserAvatar = targetUserAvatar; }
|
||||||
|
|
||||||
public String getId() { return id; }
|
|
||||||
public String getTitle() { return title; }
|
|
||||||
public String getLastMessage() { return lastMessage; }
|
public String getLastMessage() { return lastMessage; }
|
||||||
public String getTimeText() { return timeText; }
|
public void setLastMessage(String lastMessage) { this.lastMessage = lastMessage; }
|
||||||
|
|
||||||
|
public String getLastMessageTime() { return lastMessageTime; }
|
||||||
|
public void setLastMessageTime(String lastMessageTime) { this.lastMessageTime = lastMessageTime; }
|
||||||
|
|
||||||
public Integer getUnreadCount() { return unreadCount; }
|
public Integer getUnreadCount() { return unreadCount; }
|
||||||
public Boolean getMuted() { return muted; }
|
public void setUnreadCount(Integer unreadCount) { this.unreadCount = unreadCount; }
|
||||||
public String getAvatarUrl() { return avatarUrl; }
|
|
||||||
public Integer getOtherUserId() { return otherUserId; }
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,26 @@
|
||||||
package com.example.livestreaming.net;
|
package com.example.livestreaming.net;
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class PageResponse<T> {
|
public class PageResponse<T> {
|
||||||
|
|
||||||
@SerializedName("list")
|
|
||||||
private List<T> list;
|
private List<T> list;
|
||||||
|
|
||||||
@SerializedName("total")
|
|
||||||
private Long total;
|
|
||||||
|
|
||||||
@SerializedName("page")
|
|
||||||
private Integer page;
|
private Integer page;
|
||||||
|
|
||||||
@SerializedName("limit")
|
|
||||||
private Integer limit;
|
private Integer limit;
|
||||||
|
private Integer total;
|
||||||
@SerializedName("totalPage")
|
|
||||||
private Integer totalPage;
|
private Integer totalPage;
|
||||||
|
|
||||||
public List<T> getList() { return list; }
|
public List<T> getList() { return list; }
|
||||||
public Long getTotal() { return total; }
|
public void setList(List<T> list) { this.list = list; }
|
||||||
public Integer getPage() { return page; }
|
|
||||||
public Integer getLimit() { return limit; }
|
|
||||||
public Integer getTotalPage() { return totalPage; }
|
|
||||||
|
|
||||||
public boolean hasMore() {
|
public Integer getPage() { return page; }
|
||||||
return page != null && totalPage != null && page < totalPage;
|
public void setPage(Integer page) { this.page = page; }
|
||||||
}
|
|
||||||
|
public Integer getLimit() { return limit; }
|
||||||
|
public void setLimit(Integer limit) { this.limit = limit; }
|
||||||
|
|
||||||
|
public Integer getTotal() { return total; }
|
||||||
|
public void setTotal(Integer total) { this.total = total; }
|
||||||
|
|
||||||
|
public Integer getTotalPage() { return totalPage; }
|
||||||
|
public void setTotalPage(Integer totalPage) { this.totalPage = totalPage; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
6
android-app/app/src/main/res/drawable/bg_trunk_text.xml
Normal file
6
android-app/app/src/main/res/drawable/bg_trunk_text.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<solid android:color="#8B4513" />
|
||||||
|
<corners android:radius="4dp" />
|
||||||
|
</shape>
|
||||||
9
android-app/app/src/main/res/drawable/bg_wish_card.xml
Normal file
9
android-app/app/src/main/res/drawable/bg_wish_card.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<solid android:color="#FFFEF3" />
|
||||||
|
<corners android:radius="8dp" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="#FFD700" />
|
||||||
|
</shape>
|
||||||
11
android-app/app/src/main/res/drawable/bg_wish_card_add.xml
Normal file
11
android-app/app/src/main/res/drawable/bg_wish_card_add.xml
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<solid android:color="#FFF8E1" />
|
||||||
|
<corners android:radius="8dp" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="#FFCC00"
|
||||||
|
android:dashWidth="4dp"
|
||||||
|
android:dashGap="2dp" />
|
||||||
|
</shape>
|
||||||
24
android-app/app/src/main/res/layout/dialog_make_wish.xml
Normal file
24
android-app/app/src/main/res/layout/dialog_make_wish.xml
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="许下你的愿望"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_marginBottom="16dp" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/editWish"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="写下你的愿望..."
|
||||||
|
android:minLines="3"
|
||||||
|
android:gravity="top" />
|
||||||
|
</LinearLayout>
|
||||||
28
android-app/app/src/main/res/layout/dialog_wish_success.xml
Normal file
28
android-app/app/src/main/res/layout/dialog_wish_success.xml
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="64dp"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:src="@android:drawable/btn_star_big_on" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="愿望已挂上许愿树"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginTop="16dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="愿你心想事成"
|
||||||
|
android:textColor="#666"
|
||||||
|
android:layout_marginTop="8dp" />
|
||||||
|
</LinearLayout>
|
||||||
81
android-app/app/src/main/res/layout/item_post.xml
Normal file
81
android-app/app/src/main/res/layout/item_post.xml
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
app:cardCornerRadius="8dp"
|
||||||
|
app:cardElevation="2dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="12dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/iv_avatar"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:src="@mipmap/ic_launcher" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_user_name"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_time"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="#999"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_like_count"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:drawableStart="@android:drawable/btn_star"
|
||||||
|
android:drawablePadding="4dp"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_comment_count"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:drawableStart="@android:drawable/ic_menu_edit"
|
||||||
|
android:drawablePadding="4dp"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
@ -1,119 +1,82 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="6dp"
|
android:layout_margin="4dp"
|
||||||
app:cardCornerRadius="12dp"
|
app:cardCornerRadius="8dp"
|
||||||
app:cardElevation="0dp">
|
app:cardElevation="2dp">
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<FrameLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/coverImage"
|
android:id="@+id/coverImage"
|
||||||
android:layout_width="0dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="120dp"
|
||||||
android:background="@drawable/bg_cover_placeholder"
|
android:scaleType="centerCrop" />
|
||||||
android:scaleType="centerCrop"
|
|
||||||
app:layout_constraintDimensionRatio="H,4:3"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/liveBadge"
|
android:id="@+id/liveBadge"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_gravity="top|end"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_margin="8dp"
|
||||||
android:paddingStart="10dp"
|
android:background="@android:color/holo_red_light"
|
||||||
android:paddingEnd="10dp"
|
android:paddingHorizontal="6dp"
|
||||||
android:paddingTop="4dp"
|
android:paddingVertical="2dp"
|
||||||
android:paddingBottom="4dp"
|
android:text="直播中"
|
||||||
android:text="LIVE"
|
|
||||||
android:textColor="@android:color/white"
|
android:textColor="@android:color/white"
|
||||||
android:textSize="11sp"
|
android:textSize="10sp"
|
||||||
android:visibility="gone"
|
android:visibility="gone" />
|
||||||
app:layout_constraintStart_toStartOf="@id/coverImage"
|
|
||||||
app:layout_constraintTop_toTopOf="@id/coverImage" />
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<LinearLayout
|
||||||
android:id="@+id/infoContainer"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingStart="10dp"
|
android:layout_gravity="bottom"
|
||||||
android:paddingEnd="10dp"
|
android:background="#80000000"
|
||||||
android:paddingTop="8dp"
|
android:orientation="vertical"
|
||||||
android:paddingBottom="10dp"
|
android:padding="8dp">
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/coverImage">
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/roomTitle"
|
android:id="@+id/roomTitle"
|
||||||
android:layout_width="0dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="2"
|
|
||||||
android:text="王者荣耀陪练"
|
|
||||||
android:textColor="#111111"
|
|
||||||
android:textSize="13sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/streamerAvatar"
|
|
||||||
android:layout_width="18dp"
|
|
||||||
android:layout_height="18dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:background="@drawable/bg_avatar_circle"
|
|
||||||
android:padding="3dp"
|
|
||||||
android:src="@drawable/ic_account_circle_24"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/roomTitle" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/streamerName"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="6dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:text="虚拟主播"
|
android:textColor="@android:color/white"
|
||||||
android:textColor="#666666"
|
android:textSize="14sp" />
|
||||||
android:textSize="12sp"
|
|
||||||
app:layout_constraintEnd_toStartOf="@id/likeIcon"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/streamerAvatar"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/roomTitle" />
|
|
||||||
|
|
||||||
<ImageView
|
<LinearLayout
|
||||||
android:id="@+id/likeIcon"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="16dp"
|
|
||||||
android:layout_height="16dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:src="@drawable/ic_heart_24"
|
|
||||||
app:layout_constraintEnd_toStartOf="@id/likeCount"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/roomTitle" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/likeCount"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="4dp"
|
android:orientation="horizontal">
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="184"
|
|
||||||
android:textColor="#888888"
|
|
||||||
android:textSize="12sp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/roomTitle" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
<TextView
|
||||||
|
android:id="@+id/streamerName"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:textColor="#CCC"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
<ImageView
|
||||||
|
android:id="@+id/likeIcon"
|
||||||
|
android:layout_width="14dp"
|
||||||
|
android:layout_height="14dp"
|
||||||
|
android:src="@android:drawable/btn_star_big_on"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
<TextView
|
||||||
|
android:id="@+id/likeCount"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:textColor="#CCC"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</FrameLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user