修复全屏播放和退出登录功能:1.添加退出登录按钮 2.修复全屏播放黑屏问题 3.添加退出全屏按钮 4.修复退出全屏后布局恢复
This commit is contained in:
parent
202c8452c5
commit
b2f848c918
|
|
@ -11,7 +11,9 @@ import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.SurfaceHolder;
|
import android.view.SurfaceHolder;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
@ -69,13 +71,13 @@ public class BroadcastActivity extends AppCompatActivity implements ConnectCheck
|
||||||
private boolean streamerVerified = false;
|
private boolean streamerVerified = false;
|
||||||
private boolean cameraInitialized = false;
|
private boolean cameraInitialized = false;
|
||||||
|
|
||||||
// 推流参数 - 竖屏直播使用9:16分辨率
|
// 推流参数 - 竖屏直播
|
||||||
// 手机竖屏直播,直接使用竖屏分辨率,不需要交换宽高
|
// 使用 9:16 或接近手机屏幕比例的分辨率
|
||||||
private static final int VIDEO_WIDTH = 720; // 竖屏宽度
|
private static final int VIDEO_WIDTH = 720; // 竖屏宽度
|
||||||
private static final int VIDEO_HEIGHT = 1280; // 竖屏高度 (9:16)
|
private static final int VIDEO_HEIGHT = 1280; // 竖屏高度 (9:16)
|
||||||
private static final int VIDEO_FPS = 25; // 标准帧率
|
private static final int VIDEO_FPS = 30; // 提高帧率
|
||||||
private static final int VIDEO_BITRATE = 1500 * 1024; // 1.5Mbps,高清
|
private static final int VIDEO_BITRATE = 2000 * 1024; // 2Mbps,更高清
|
||||||
private static final int AUDIO_BITRATE = 64 * 1024;
|
private static final int AUDIO_BITRATE = 128 * 1024; // 128kbps 音频
|
||||||
private static final int AUDIO_SAMPLE_RATE = 44100;
|
private static final int AUDIO_SAMPLE_RATE = 44100;
|
||||||
|
|
||||||
// 直播计时
|
// 直播计时
|
||||||
|
|
@ -84,6 +86,10 @@ public class BroadcastActivity extends AppCompatActivity implements ConnectCheck
|
||||||
private long startTime = 0;
|
private long startTime = 0;
|
||||||
private Runnable timerRunnable;
|
private Runnable timerRunnable;
|
||||||
|
|
||||||
|
// 预览尺寸(用于计算 centerCrop)
|
||||||
|
private int previewWidth = 0;
|
||||||
|
private int previewHeight = 0;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
@ -316,8 +322,12 @@ public class BroadcastActivity extends AppCompatActivity implements ConnectCheck
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initCameraInternal() {
|
private void initCameraInternal() {
|
||||||
|
// 获取屏幕尺寸
|
||||||
|
int screenWidth = getResources().getDisplayMetrics().widthPixels;
|
||||||
|
int screenHeight = getResources().getDisplayMetrics().heightPixels;
|
||||||
|
Log.d(TAG, "屏幕尺寸: " + screenWidth + "x" + screenHeight);
|
||||||
|
|
||||||
// 手机直播使用竖屏分辨率
|
// 手机直播使用竖屏分辨率
|
||||||
// RootEncoder 会自动处理摄像头旋转
|
|
||||||
int videoWidth = VIDEO_WIDTH; // 720
|
int videoWidth = VIDEO_WIDTH; // 720
|
||||||
int videoHeight = VIDEO_HEIGHT; // 1280
|
int videoHeight = VIDEO_HEIGHT; // 1280
|
||||||
Log.d(TAG, "目标视频分辨率: " + videoWidth + "x" + videoHeight + " (竖屏9:16)");
|
Log.d(TAG, "目标视频分辨率: " + videoWidth + "x" + videoHeight + " (竖屏9:16)");
|
||||||
|
|
@ -355,11 +365,46 @@ public class BroadcastActivity extends AppCompatActivity implements ConnectCheck
|
||||||
rtmpCamera2 = null;
|
rtmpCamera2 = null;
|
||||||
} else {
|
} else {
|
||||||
// 编码器准备好后,开始预览
|
// 编码器准备好后,开始预览
|
||||||
// 使用 CameraHelper.Facing 来指定摄像头
|
|
||||||
CameraHelper.Facing facing = isFrontCamera ? CameraHelper.Facing.FRONT : CameraHelper.Facing.BACK;
|
CameraHelper.Facing facing = isFrontCamera ? CameraHelper.Facing.FRONT : CameraHelper.Facing.BACK;
|
||||||
rtmpCamera2.startPreview(facing, videoWidth, videoHeight);
|
|
||||||
|
// 使用摄像头原生支持的分辨率
|
||||||
|
// 先尝试自动分辨率,让库自己选择最佳分辨率
|
||||||
|
boolean previewStarted = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 直接使用自动分辨率,避免分辨率不支持的问题
|
||||||
|
rtmpCamera2.startPreview(facing);
|
||||||
|
Log.d(TAG, "Camera2 预览启动成功(自动分辨率)");
|
||||||
|
previewStarted = true;
|
||||||
|
// 使用默认的 640x480 计算 centerCrop
|
||||||
|
adjustPreviewSize(640, 480);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w(TAG, "自动分辨率预览失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!previewStarted) {
|
||||||
|
// 自动分辨率失败,尝试常见分辨率
|
||||||
|
int[][] resolutions = {
|
||||||
|
{640, 480}, // VGA 4:3
|
||||||
|
{1280, 720}, // HD 16:9
|
||||||
|
{1920, 1080}, // Full HD 16:9
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int[] res : resolutions) {
|
||||||
|
try {
|
||||||
|
rtmpCamera2.startPreview(facing, res[0], res[1]);
|
||||||
|
Log.d(TAG, "Camera2 预览启动成功: " + res[0] + "x" + res[1]);
|
||||||
|
previewStarted = true;
|
||||||
|
adjustPreviewSize(res[0], res[1]);
|
||||||
|
break;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w(TAG, "预览分辨率 " + res[0] + "x" + res[1] + " 不支持: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useCamera2 = true;
|
useCamera2 = true;
|
||||||
Log.d(TAG, "Camera2 + OpenGlView 初始化完成,预览已开始");
|
Log.d(TAG, "Camera2 + OpenGlView 初始化完成");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
@ -401,10 +446,42 @@ public class BroadcastActivity extends AppCompatActivity implements ConnectCheck
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 使用摄像头原生支持的分辨率
|
||||||
CameraHelper.Facing facing = isFrontCamera ? CameraHelper.Facing.FRONT : CameraHelper.Facing.BACK;
|
CameraHelper.Facing facing = isFrontCamera ? CameraHelper.Facing.FRONT : CameraHelper.Facing.BACK;
|
||||||
rtmpCamera1.startPreview(facing);
|
|
||||||
|
// 先尝试自动分辨率
|
||||||
|
boolean previewStarted = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
rtmpCamera1.startPreview(facing);
|
||||||
|
Log.d(TAG, "Camera1 预览启动成功(自动分辨率)");
|
||||||
|
previewStarted = true;
|
||||||
|
adjustPreviewSize(640, 480);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w(TAG, "自动分辨率预览失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!previewStarted) {
|
||||||
|
int[][] resolutions = {
|
||||||
|
{640, 480},
|
||||||
|
{1280, 720},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int[] res : resolutions) {
|
||||||
|
try {
|
||||||
|
rtmpCamera1.startPreview(facing, res[0], res[1]);
|
||||||
|
Log.d(TAG, "Camera1 预览启动成功: " + res[0] + "x" + res[1]);
|
||||||
|
previewStarted = true;
|
||||||
|
adjustPreviewSize(res[0], res[1]);
|
||||||
|
break;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w(TAG, "预览分辨率 " + res[0] + "x" + res[1] + " 不支持: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useCamera2 = false;
|
useCamera2 = false;
|
||||||
Log.d(TAG, "Camera1 + OpenGlView 初始化完成,预览已开始");
|
Log.d(TAG, "Camera1 + OpenGlView 初始化完成");
|
||||||
return;
|
return;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "Camera1 初始化也失败: " + e.getMessage(), e);
|
Log.e(TAG, "Camera1 初始化也失败: " + e.getMessage(), e);
|
||||||
|
|
@ -425,6 +502,52 @@ public class BroadcastActivity extends AppCompatActivity implements ConnectCheck
|
||||||
return getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY);
|
return getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调整 OpenGlView 尺寸实现 centerCrop 效果
|
||||||
|
* 让预览画面填满屏幕,裁剪多余部分
|
||||||
|
*/
|
||||||
|
private void adjustPreviewSize(int cameraWidth, int cameraHeight) {
|
||||||
|
// 获取容器尺寸
|
||||||
|
int containerWidth = binding.previewContainer.getWidth();
|
||||||
|
int containerHeight = binding.previewContainer.getHeight();
|
||||||
|
|
||||||
|
if (containerWidth == 0 || containerHeight == 0) {
|
||||||
|
Log.w(TAG, "容器尺寸为0,延迟调整");
|
||||||
|
mainHandler.postDelayed(() -> adjustPreviewSize(cameraWidth, cameraHeight), 100);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 摄像头输出是横向的,竖屏显示时需要交换宽高
|
||||||
|
int previewW = cameraHeight; // 旋转后的宽度
|
||||||
|
int previewH = cameraWidth; // 旋转后的高度
|
||||||
|
|
||||||
|
Log.d(TAG, "容器尺寸: " + containerWidth + "x" + containerHeight);
|
||||||
|
Log.d(TAG, "预览尺寸(旋转后): " + previewW + "x" + previewH);
|
||||||
|
|
||||||
|
// 计算 centerCrop 需要的缩放比例
|
||||||
|
float containerRatio = (float) containerWidth / containerHeight;
|
||||||
|
float previewRatio = (float) previewW / previewH;
|
||||||
|
|
||||||
|
int newWidth, newHeight;
|
||||||
|
|
||||||
|
if (previewRatio > containerRatio) {
|
||||||
|
// 预览更宽,以高度为基准,宽度会超出
|
||||||
|
newHeight = containerHeight;
|
||||||
|
newWidth = (int) (containerHeight * previewRatio);
|
||||||
|
} else {
|
||||||
|
// 预览更高,以宽度为基准,高度会超出
|
||||||
|
newWidth = containerWidth;
|
||||||
|
newHeight = (int) (containerWidth / previewRatio);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "调整后的 OpenGlView 尺寸: " + newWidth + "x" + newHeight);
|
||||||
|
|
||||||
|
// 更新 OpenGlView 尺寸
|
||||||
|
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(newWidth, newHeight);
|
||||||
|
params.gravity = android.view.Gravity.CENTER;
|
||||||
|
binding.openGlView.setLayoutParams(params);
|
||||||
|
}
|
||||||
|
|
||||||
private void switchCamera() {
|
private void switchCamera() {
|
||||||
isFrontCamera = !isFrontCamera;
|
isFrontCamera = !isFrontCamera;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -278,6 +278,7 @@ public class RoomDetailActivity extends AppCompatActivity implements SurfaceHold
|
||||||
// 返回按钮
|
// 返回按钮
|
||||||
binding.backButton.setOnClickListener(v -> finish());
|
binding.backButton.setOnClickListener(v -> finish());
|
||||||
|
|
||||||
|
// 退出全屏按钮点击事件
|
||||||
binding.exitFullscreenButton.setOnClickListener(v -> {
|
binding.exitFullscreenButton.setOnClickListener(v -> {
|
||||||
if (isFullscreen) {
|
if (isFullscreen) {
|
||||||
toggleFullscreen();
|
toggleFullscreen();
|
||||||
|
|
@ -286,16 +287,6 @@ public class RoomDetailActivity extends AppCompatActivity implements SurfaceHold
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
View.OnClickListener fullscreenOverlayToggle = v -> {
|
|
||||||
if (!isFullscreen) return;
|
|
||||||
int vis = binding.exitFullscreenButton.getVisibility();
|
|
||||||
binding.exitFullscreenButton.setVisibility(vis == View.VISIBLE ? View.GONE : View.VISIBLE);
|
|
||||||
};
|
|
||||||
|
|
||||||
// PlayerView 往往会自己消费触摸事件,导致 container 的点击回调不触发,所以这里两处都绑定
|
|
||||||
binding.playerContainer.setOnClickListener(fullscreenOverlayToggle);
|
|
||||||
binding.playerView.setOnClickListener(fullscreenOverlayToggle);
|
|
||||||
|
|
||||||
// 全屏按钮
|
// 全屏按钮
|
||||||
binding.fullscreenButton.setOnClickListener(v -> toggleFullscreen());
|
binding.fullscreenButton.setOnClickListener(v -> toggleFullscreen());
|
||||||
|
|
||||||
|
|
@ -325,14 +316,26 @@ public class RoomDetailActivity extends AppCompatActivity implements SurfaceHold
|
||||||
* 手机:视频全屏显示(100%高度),UI元素覆盖在视频上
|
* 手机:视频全屏显示(100%高度),UI元素覆盖在视频上
|
||||||
*/
|
*/
|
||||||
private void adjustVideoPlayerHeight() {
|
private void adjustVideoPlayerHeight() {
|
||||||
// 初始化时不做任何调整,等待视频流加载后根据视频宽高比调整
|
// 初始化视频区域高度为屏幕的45%
|
||||||
android.util.Log.d("RoomDetail", "等待视频流加载后根据视频宽高比调整显示方式...");
|
android.util.DisplayMetrics displayMetrics = new android.util.DisplayMetrics();
|
||||||
|
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
|
||||||
|
int screenHeight = displayMetrics.heightPixels;
|
||||||
|
int videoHeight = (int) (screenHeight * 0.45);
|
||||||
|
|
||||||
|
android.widget.FrameLayout.LayoutParams playerParams =
|
||||||
|
(android.widget.FrameLayout.LayoutParams) binding.playerContainer.getLayoutParams();
|
||||||
|
playerParams.width = android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||||
|
playerParams.height = videoHeight;
|
||||||
|
playerParams.gravity = android.view.Gravity.CENTER;
|
||||||
|
binding.playerContainer.setLayoutParams(playerParams);
|
||||||
|
|
||||||
|
android.util.Log.d("RoomDetail", "初始化视频区域高度: " + videoHeight + " (屏幕高度的45%)");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据视频流的宽高比调整显示方式
|
* 根据视频流的宽高比调整显示方式
|
||||||
* 手机推流(竖屏视频,高度>宽度):视频全屏显示
|
* 手机推流(竖屏视频,高度>宽度):视频全屏显示
|
||||||
* 电脑推流(横屏视频,宽度>高度):视频显示50%高度
|
* 电脑推流(横屏视频,宽度>高度):视频在中间显示,保持比例
|
||||||
*/
|
*/
|
||||||
private void adjustVideoDisplayByStreamType(int videoWidth, int videoHeight) {
|
private void adjustVideoDisplayByStreamType(int videoWidth, int videoHeight) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -343,48 +346,54 @@ public class RoomDetailActivity extends AppCompatActivity implements SurfaceHold
|
||||||
int screenWidth = displayMetrics.widthPixels;
|
int screenWidth = displayMetrics.widthPixels;
|
||||||
|
|
||||||
// 判断视频是竖屏还是横屏
|
// 判断视频是竖屏还是横屏
|
||||||
// 手机推流:视频是竖屏的(高度 > 宽度)
|
|
||||||
// 电脑推流:视频是横屏的(宽度 > 高度)
|
|
||||||
boolean isMobileStream = videoHeight > videoWidth;
|
boolean isMobileStream = videoHeight > videoWidth;
|
||||||
|
|
||||||
android.util.Log.d("RoomDetail", "视频尺寸: " + videoWidth + "x" + videoHeight +
|
android.util.Log.d("RoomDetail", "视频尺寸: " + videoWidth + "x" + videoHeight +
|
||||||
", 屏幕尺寸: " + screenWidth + "x" + screenHeight +
|
", 屏幕尺寸: " + screenWidth + "x" + screenHeight +
|
||||||
", 推流类型: " + (isMobileStream ? "手机(竖屏)" : "电脑(横屏)"));
|
", 推流类型: " + (isMobileStream ? "手机(竖屏)" : "电脑(横屏)"));
|
||||||
|
|
||||||
// 无论什么类型的推流,都让视频全屏显示
|
android.widget.FrameLayout.LayoutParams playerParams =
|
||||||
// 手机推流的竖屏视频会自动填满屏幕
|
(android.widget.FrameLayout.LayoutParams) binding.playerContainer.getLayoutParams();
|
||||||
// 电脑推流的横屏视频会在上下留黑边(保持比例)
|
|
||||||
android.util.Log.d("RoomDetail", "视频全屏显示模式");
|
if (isMobileStream) {
|
||||||
|
// 手机推流(竖屏视频):全屏显示
|
||||||
|
android.util.Log.d("RoomDetail", "手机推流 - 全屏显示模式");
|
||||||
|
playerParams.width = android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||||
|
playerParams.height = android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||||
|
playerParams.gravity = android.view.Gravity.CENTER;
|
||||||
|
} else {
|
||||||
|
// 电脑推流(横屏视频):在中间显示,保持16:9比例
|
||||||
|
android.util.Log.d("RoomDetail", "电脑推流 - 居中显示模式");
|
||||||
|
|
||||||
|
// 计算16:9比例的高度
|
||||||
|
float aspectRatio = (float) videoWidth / videoHeight;
|
||||||
|
int targetHeight = (int) (screenWidth / aspectRatio);
|
||||||
|
|
||||||
|
// 限制最大高度为屏幕的60%
|
||||||
|
int maxHeight = (int) (screenHeight * 0.6);
|
||||||
|
if (targetHeight > maxHeight) {
|
||||||
|
targetHeight = maxHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
playerParams.width = android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||||
|
playerParams.height = targetHeight;
|
||||||
|
playerParams.gravity = android.view.Gravity.CENTER;
|
||||||
|
|
||||||
|
android.util.Log.d("RoomDetail", "视频区域高度: " + targetHeight + " (屏幕高度的" +
|
||||||
|
(targetHeight * 100 / screenHeight) + "%)");
|
||||||
|
}
|
||||||
|
|
||||||
// 设置 playerContainer 全屏
|
|
||||||
android.view.ViewGroup.LayoutParams playerParams = binding.playerContainer.getLayoutParams();
|
|
||||||
playerParams.width = android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
|
||||||
playerParams.height = android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
|
||||||
binding.playerContainer.setLayoutParams(playerParams);
|
binding.playerContainer.setLayoutParams(playerParams);
|
||||||
|
|
||||||
// 设置 SurfaceView 全屏填充
|
// 设置 SurfaceView
|
||||||
if (binding.flvSurfaceView != null && binding.flvSurfaceView.getVisibility() == View.VISIBLE) {
|
if (binding.flvSurfaceView != null && binding.flvSurfaceView.getVisibility() == View.VISIBLE) {
|
||||||
android.view.ViewGroup.LayoutParams surfaceParams = binding.flvSurfaceView.getLayoutParams();
|
android.view.ViewGroup.LayoutParams surfaceParams = binding.flvSurfaceView.getLayoutParams();
|
||||||
surfaceParams.width = android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
surfaceParams.width = android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||||
surfaceParams.height = android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
surfaceParams.height = android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||||
binding.flvSurfaceView.setLayoutParams(surfaceParams);
|
binding.flvSurfaceView.setLayoutParams(surfaceParams);
|
||||||
|
|
||||||
// 关键:设置 SurfaceView 的固定尺寸以保持视频比例
|
|
||||||
// 对于竖屏视频,让它填满屏幕宽度,高度按比例计算
|
|
||||||
if (isMobileStream) {
|
|
||||||
// 竖屏视频:宽度填满屏幕,高度按比例
|
|
||||||
float aspectRatio = (float) videoHeight / videoWidth;
|
|
||||||
int targetHeight = (int) (screenWidth * aspectRatio);
|
|
||||||
if (targetHeight > screenHeight) {
|
|
||||||
// 如果计算出的高度超过屏幕,则以屏幕高度为准
|
|
||||||
targetHeight = screenHeight;
|
|
||||||
}
|
|
||||||
binding.flvSurfaceView.getHolder().setFixedSize(screenWidth, targetHeight);
|
|
||||||
android.util.Log.d("RoomDetail", "SurfaceView 固定尺寸: " + screenWidth + "x" + targetHeight);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置 PlayerView 全屏填充
|
// 设置 PlayerView
|
||||||
if (binding.playerView != null) {
|
if (binding.playerView != null) {
|
||||||
android.view.ViewGroup.LayoutParams pvParams = binding.playerView.getLayoutParams();
|
android.view.ViewGroup.LayoutParams pvParams = binding.playerView.getLayoutParams();
|
||||||
pvParams.width = android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
pvParams.width = android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||||
|
|
@ -624,28 +633,42 @@ public class RoomDetailActivity extends AppCompatActivity implements SurfaceHold
|
||||||
// 停止之前的重连任务
|
// 停止之前的重连任务
|
||||||
stopChatReconnect();
|
stopChatReconnect();
|
||||||
|
|
||||||
|
// 获取用户token
|
||||||
|
String token = AuthStore.getToken(this);
|
||||||
|
|
||||||
|
// 构建WebSocket URL,添加token参数
|
||||||
String wsUrl = getWsChatBaseUrl() + roomId;
|
String wsUrl = getWsChatBaseUrl() + roomId;
|
||||||
|
if (!TextUtils.isEmpty(token)) {
|
||||||
|
wsUrl += "?token=" + token;
|
||||||
|
}
|
||||||
android.util.Log.d("ChatWebSocket", "准备连接WebSocket: " + wsUrl);
|
android.util.Log.d("ChatWebSocket", "准备连接WebSocket: " + wsUrl);
|
||||||
|
|
||||||
chatWsClient = new OkHttpClient.Builder()
|
try {
|
||||||
.pingInterval(30, java.util.concurrent.TimeUnit.SECONDS) // OkHttp 内置 ping
|
chatWsClient = new OkHttpClient.Builder()
|
||||||
.build();
|
.pingInterval(30, java.util.concurrent.TimeUnit.SECONDS)
|
||||||
Request request = new Request.Builder()
|
.connectTimeout(10, java.util.concurrent.TimeUnit.SECONDS)
|
||||||
.url(wsUrl)
|
.readTimeout(30, java.util.concurrent.TimeUnit.SECONDS)
|
||||||
.build();
|
.build();
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(wsUrl)
|
||||||
|
.build();
|
||||||
|
|
||||||
chatWebSocket = chatWsClient.newWebSocket(request, new WebSocketListener() {
|
chatWebSocket = chatWsClient.newWebSocket(request, new WebSocketListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onOpen(WebSocket webSocket, okhttp3.Response response) {
|
public void onOpen(WebSocket webSocket, okhttp3.Response response) {
|
||||||
android.util.Log.d("ChatWebSocket", "弹幕连接成功: roomId=" + roomId);
|
android.util.Log.d("ChatWebSocket", "弹幕连接成功: roomId=" + roomId);
|
||||||
isChatWebSocketConnected = true;
|
isChatWebSocketConnected = true;
|
||||||
chatReconnectAttempts = 0;
|
chatReconnectAttempts = 0;
|
||||||
// 启动心跳检测
|
// 启动心跳检测
|
||||||
startChatHeartbeat();
|
startChatHeartbeat();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
handler.post(() -> {
|
||||||
public void onMessage(WebSocket webSocket, String text) {
|
addChatMessage(new ChatMessage("已连接到弹幕服务", true));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMessage(WebSocket webSocket, String text) {
|
||||||
// 收到消息,解析并显示
|
// 收到消息,解析并显示
|
||||||
android.util.Log.d("ChatWebSocket", "收到消息: " + text);
|
android.util.Log.d("ChatWebSocket", "收到消息: " + text);
|
||||||
try {
|
try {
|
||||||
|
|
@ -709,9 +732,19 @@ public class RoomDetailActivity extends AppCompatActivity implements SurfaceHold
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(WebSocket webSocket, Throwable t, okhttp3.Response response) {
|
public void onFailure(WebSocket webSocket, Throwable t, okhttp3.Response response) {
|
||||||
android.util.Log.e("ChatWebSocket", "连接失败: " + t.getMessage());
|
String errorMsg = t != null ? t.getMessage() : "未知错误";
|
||||||
|
int responseCode = response != null ? response.code() : -1;
|
||||||
|
android.util.Log.e("ChatWebSocket", "连接失败: " + errorMsg + ", responseCode=" + responseCode);
|
||||||
isChatWebSocketConnected = false;
|
isChatWebSocketConnected = false;
|
||||||
stopChatHeartbeat();
|
stopChatHeartbeat();
|
||||||
|
|
||||||
|
// 在UI上显示连接状态(仅首次失败时显示)
|
||||||
|
if (chatReconnectAttempts == 0) {
|
||||||
|
handler.post(() -> {
|
||||||
|
addChatMessage(new ChatMessage("弹幕服务连接中...", true));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 尝试重连
|
// 尝试重连
|
||||||
scheduleChatReconnect();
|
scheduleChatReconnect();
|
||||||
}
|
}
|
||||||
|
|
@ -727,6 +760,11 @@ public class RoomDetailActivity extends AppCompatActivity implements SurfaceHold
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
android.util.Log.e("ChatWebSocket", "创建WebSocket失败: " + e.getMessage());
|
||||||
|
isChatWebSocketConnected = false;
|
||||||
|
scheduleChatReconnect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -949,10 +987,17 @@ public class RoomDetailActivity extends AppCompatActivity implements SurfaceHold
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendChatViaWebSocket(String content) {
|
private void sendChatViaWebSocket(String content) {
|
||||||
|
// 先本地显示消息(乐观更新)
|
||||||
|
String nickname = AuthStore.getNickname(this);
|
||||||
|
if (TextUtils.isEmpty(nickname)) {
|
||||||
|
nickname = "我";
|
||||||
|
}
|
||||||
|
addChatMessage(new ChatMessage(nickname, content));
|
||||||
|
|
||||||
if (chatWebSocket == null || !isChatWebSocketConnected) {
|
if (chatWebSocket == null || !isChatWebSocketConnected) {
|
||||||
// 如果 WebSocket 未连接,先本地显示
|
// WebSocket 未连接,尝试重连
|
||||||
addChatMessage(new ChatMessage("我", content));
|
android.util.Log.w("ChatWebSocket", "WebSocket未连接,尝试重连...");
|
||||||
Toast.makeText(this, "弹幕连接断开,消息仅本地显示", Toast.LENGTH_SHORT).show();
|
connectChatWebSocket();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -960,14 +1005,15 @@ public class RoomDetailActivity extends AppCompatActivity implements SurfaceHold
|
||||||
JSONObject json = new JSONObject();
|
JSONObject json = new JSONObject();
|
||||||
json.put("type", "chat");
|
json.put("type", "chat");
|
||||||
json.put("content", content);
|
json.put("content", content);
|
||||||
json.put("nickname", AuthStore.getNickname(this));
|
json.put("nickname", nickname);
|
||||||
json.put("userId", AuthStore.getUserId(this));
|
json.put("userId", AuthStore.getUserId(this));
|
||||||
|
|
||||||
chatWebSocket.send(json.toString());
|
boolean sent = chatWebSocket.send(json.toString());
|
||||||
|
if (!sent) {
|
||||||
|
android.util.Log.w("ChatWebSocket", "消息发送失败,WebSocket可能已断开");
|
||||||
|
}
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
android.util.Log.e("ChatWebSocket", "发送消息失败: " + e.getMessage());
|
android.util.Log.e("ChatWebSocket", "发送消息失败: " + e.getMessage());
|
||||||
// 失败时本地显示
|
|
||||||
addChatMessage(new ChatMessage("我", content));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1020,25 +1066,63 @@ public class RoomDetailActivity extends AppCompatActivity implements SurfaceHold
|
||||||
// 退出全屏
|
// 退出全屏
|
||||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||||
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||||
// ActionBar可能为null,需要检查
|
// 恢复系统UI
|
||||||
androidx.appcompat.app.ActionBar actionBar = getSupportActionBar();
|
getWindow().getDecorView().setSystemUiVisibility(
|
||||||
if (actionBar != null) {
|
View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
||||||
actionBar.show();
|
|
||||||
}
|
|
||||||
isFullscreen = false;
|
isFullscreen = false;
|
||||||
if (binding != null) binding.exitFullscreenButton.setVisibility(View.GONE);
|
if (binding != null) {
|
||||||
|
binding.exitFullscreenButton.setVisibility(View.GONE);
|
||||||
|
binding.fullscreenButton.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
// 恢复视频区域大小 - 使用屏幕高度的50%
|
||||||
|
android.util.DisplayMetrics displayMetrics = new android.util.DisplayMetrics();
|
||||||
|
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
|
||||||
|
int screenHeight = displayMetrics.heightPixels;
|
||||||
|
int videoHeight = (int) (screenHeight * 0.45); // 45%屏幕高度
|
||||||
|
|
||||||
|
android.widget.FrameLayout.LayoutParams playerParams =
|
||||||
|
(android.widget.FrameLayout.LayoutParams) binding.playerContainer.getLayoutParams();
|
||||||
|
playerParams.width = android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||||
|
playerParams.height = videoHeight;
|
||||||
|
playerParams.gravity = android.view.Gravity.CENTER;
|
||||||
|
playerParams.setMargins(0, 0, 0, 0);
|
||||||
|
binding.playerContainer.setLayoutParams(playerParams);
|
||||||
|
|
||||||
|
// 显示所有UI元素
|
||||||
|
binding.uiOverlayContainer.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// 进入全屏
|
// 进入全屏(横屏)
|
||||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
||||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||||
WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||||
// ActionBar可能为null,需要检查
|
// 隐藏系统UI(状态栏和导航栏)
|
||||||
androidx.appcompat.app.ActionBar actionBar = getSupportActionBar();
|
getWindow().getDecorView().setSystemUiVisibility(
|
||||||
if (actionBar != null) {
|
View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||||
actionBar.hide();
|
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||||
}
|
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||||
|
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||||
|
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||||
|
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
||||||
|
|
||||||
isFullscreen = true;
|
isFullscreen = true;
|
||||||
if (binding != null) binding.exitFullscreenButton.setVisibility(View.GONE);
|
if (binding != null) {
|
||||||
|
binding.exitFullscreenButton.setVisibility(View.VISIBLE);
|
||||||
|
binding.fullscreenButton.setVisibility(View.GONE);
|
||||||
|
|
||||||
|
// 全屏时视频区域填满屏幕
|
||||||
|
android.widget.FrameLayout.LayoutParams playerParams =
|
||||||
|
(android.widget.FrameLayout.LayoutParams) binding.playerContainer.getLayoutParams();
|
||||||
|
playerParams.width = android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||||
|
playerParams.height = android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||||
|
playerParams.gravity = android.view.Gravity.CENTER;
|
||||||
|
playerParams.setMargins(0, 0, 0, 0);
|
||||||
|
binding.playerContainer.setLayoutParams(playerParams);
|
||||||
|
|
||||||
|
// 隐藏UI覆盖层(除了退出按钮)
|
||||||
|
binding.uiOverlayContainer.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1065,18 +1149,59 @@ public class RoomDetailActivity extends AppCompatActivity implements SurfaceHold
|
||||||
super.onConfigurationChanged(newConfig);
|
super.onConfigurationChanged(newConfig);
|
||||||
|
|
||||||
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||||
// 横屏时隐藏其他UI元素,只显示播放器
|
// 横屏时隐藏其他UI元素,只显示播放器和退出按钮
|
||||||
binding.topBar.setVisibility(View.GONE);
|
isFullscreen = true;
|
||||||
binding.roomInfoLayout.setVisibility(View.GONE);
|
|
||||||
binding.chatInputLayout.setVisibility(View.GONE);
|
// 隐藏系统UI
|
||||||
binding.chatRecyclerView.setVisibility(View.GONE);
|
getWindow().getDecorView().setSystemUiVisibility(
|
||||||
|
View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||||
|
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||||
|
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||||
|
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||||
|
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||||
|
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
||||||
|
|
||||||
|
// 隐藏UI覆盖层
|
||||||
|
binding.uiOverlayContainer.setVisibility(View.GONE);
|
||||||
|
|
||||||
|
// 全屏时视频区域填满屏幕
|
||||||
|
android.widget.FrameLayout.LayoutParams playerParams =
|
||||||
|
(android.widget.FrameLayout.LayoutParams) binding.playerContainer.getLayoutParams();
|
||||||
|
playerParams.width = android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||||
|
playerParams.height = android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||||
|
playerParams.gravity = android.view.Gravity.CENTER;
|
||||||
|
playerParams.setMargins(0, 0, 0, 0);
|
||||||
|
binding.playerContainer.setLayoutParams(playerParams);
|
||||||
|
|
||||||
|
// 显示退出全屏按钮(在playerContainer内)
|
||||||
binding.exitFullscreenButton.setVisibility(View.VISIBLE);
|
binding.exitFullscreenButton.setVisibility(View.VISIBLE);
|
||||||
|
binding.fullscreenButton.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
// 竖屏时显示所有UI元素
|
// 竖屏时显示所有UI元素
|
||||||
binding.topBar.setVisibility(View.VISIBLE);
|
isFullscreen = false;
|
||||||
binding.roomInfoLayout.setVisibility(View.VISIBLE);
|
|
||||||
binding.chatInputLayout.setVisibility(View.VISIBLE);
|
// 恢复系统UI
|
||||||
binding.chatRecyclerView.setVisibility(View.VISIBLE);
|
getWindow().getDecorView().setSystemUiVisibility(
|
||||||
|
View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
||||||
|
|
||||||
|
// 显示UI覆盖层
|
||||||
|
binding.uiOverlayContainer.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
// 竖屏时恢复视频区域大小 - 使用屏幕高度的45%
|
||||||
|
android.util.DisplayMetrics displayMetrics = new android.util.DisplayMetrics();
|
||||||
|
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
|
||||||
|
int screenHeight = displayMetrics.heightPixels;
|
||||||
|
int videoHeight = (int) (screenHeight * 0.45);
|
||||||
|
|
||||||
|
android.widget.FrameLayout.LayoutParams playerParams =
|
||||||
|
(android.widget.FrameLayout.LayoutParams) binding.playerContainer.getLayoutParams();
|
||||||
|
playerParams.width = android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||||
|
playerParams.height = videoHeight;
|
||||||
|
playerParams.gravity = android.view.Gravity.CENTER;
|
||||||
|
playerParams.setMargins(0, 0, 0, 0);
|
||||||
|
binding.playerContainer.setLayoutParams(playerParams);
|
||||||
|
|
||||||
|
binding.fullscreenButton.setVisibility(View.VISIBLE);
|
||||||
binding.exitFullscreenButton.setVisibility(View.GONE);
|
binding.exitFullscreenButton.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -134,6 +134,11 @@ public class SettingsPageActivity extends AppCompatActivity {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ("退出登录".equals(t)) {
|
||||||
|
showLogoutDialog();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 处理其他页面的点击事件
|
// 处理其他页面的点击事件
|
||||||
if (PAGE_SERVER.equals(page)) {
|
if (PAGE_SERVER.equals(page)) {
|
||||||
// 服务器设置页面的其他项目已在上面处理
|
// 服务器设置页面的其他项目已在上面处理
|
||||||
|
|
@ -385,6 +390,12 @@ public class SettingsPageActivity extends AppCompatActivity {
|
||||||
list.add(MoreItem.row("申请成为主播", "认证后可开播", R.drawable.ic_live_24));
|
list.add(MoreItem.row("申请成为主播", "认证后可开播", R.drawable.ic_live_24));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 退出登录(仅在已登录时显示)
|
||||||
|
if (com.example.livestreaming.net.AuthStore.getToken(this) != null) {
|
||||||
|
list.add(MoreItem.section("账号"));
|
||||||
|
list.add(MoreItem.row("退出登录", "退出当前账号", R.drawable.ic_person_24));
|
||||||
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -851,6 +862,25 @@ public class SettingsPageActivity extends AppCompatActivity {
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showLogoutDialog() {
|
||||||
|
new AlertDialog.Builder(this)
|
||||||
|
.setTitle("退出登录")
|
||||||
|
.setMessage("确定要退出当前账号吗?")
|
||||||
|
.setPositiveButton("确定", (d, w) -> {
|
||||||
|
// 清除所有登录数据
|
||||||
|
com.example.livestreaming.net.AuthStore.clearAll(this);
|
||||||
|
Toast.makeText(this, "已退出登录", Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
|
// 跳转到登录页面并清除任务栈
|
||||||
|
Intent intent = new Intent(this, LoginActivity.class);
|
||||||
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||||
|
startActivity(intent);
|
||||||
|
finish();
|
||||||
|
})
|
||||||
|
.setNegativeButton("取消", null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
private int dp(int value) {
|
private int dp(int value) {
|
||||||
return (int) (value * getResources().getDisplayMetrics().density);
|
return (int) (value * getResources().getDisplayMetrics().density);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
6
android-app/app/src/main/res/drawable/bg_chat_input.xml
Normal file
6
android-app/app/src/main/res/drawable/bg_chat_input.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="#33FFFFFF" />
|
||||||
|
<corners android:radius="20dp" />
|
||||||
|
</shape>
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- 直播间操作按钮圆形背景 -->
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="oval">
|
||||||
|
<solid android:color="#40000000" />
|
||||||
|
<size android:width="44dp" android:height="44dp" />
|
||||||
|
</shape>
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
<solid android:color="#E53935" />
|
android:shape="rectangle">
|
||||||
<corners android:radius="4dp" />
|
<solid android:color="#FF4444" />
|
||||||
|
<corners android:radius="12dp" />
|
||||||
</shape>
|
</shape>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- 直播中红色标签 -->
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:shape="rectangle">
|
android:shape="rectangle">
|
||||||
<solid android:color="#FF4757" />
|
<solid android:color="#FF4757" />
|
||||||
<corners android:radius="10dp" />
|
<corners android:radius="4dp" />
|
||||||
</shape>
|
</shape>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- 直播间底部渐变遮罩 -->
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<gradient
|
||||||
|
android:angle="90"
|
||||||
|
android:startColor="#90000000"
|
||||||
|
android:centerColor="#50000000"
|
||||||
|
android:endColor="#00000000"
|
||||||
|
android:type="linear" />
|
||||||
|
</shape>
|
||||||
10
android-app/app/src/main/res/drawable/bg_live_chat_input.xml
Normal file
10
android-app/app/src/main/res/drawable/bg_live_chat_input.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- 直播间聊天输入框背景 -->
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<solid android:color="#40FFFFFF" />
|
||||||
|
<corners android:radius="20dp" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="#30FFFFFF" />
|
||||||
|
</shape>
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- 关注按钮背景 -->
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<solid android:color="#FF4757" />
|
||||||
|
<corners android:radius="14dp" />
|
||||||
|
</shape>
|
||||||
11
android-app/app/src/main/res/drawable/bg_live_gift_btn.xml
Normal file
11
android-app/app/src/main/res/drawable/bg_live_gift_btn.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">
|
||||||
|
<gradient
|
||||||
|
android:angle="90"
|
||||||
|
android:startColor="#FF6B9D"
|
||||||
|
android:endColor="#A855F7"
|
||||||
|
android:type="linear" />
|
||||||
|
<corners android:radius="22dp" />
|
||||||
|
</shape>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="oval">
|
||||||
|
<solid android:color="#FF4444" />
|
||||||
|
</shape>
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- 主播信息胶囊背景 -->
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<solid android:color="#60000000" />
|
||||||
|
<corners android:radius="20dp" />
|
||||||
|
</shape>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- 直播间顶部渐变遮罩 -->
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<gradient
|
||||||
|
android:angle="270"
|
||||||
|
android:startColor="#80000000"
|
||||||
|
android:centerColor="#40000000"
|
||||||
|
android:endColor="#00000000"
|
||||||
|
android:type="linear" />
|
||||||
|
</shape>
|
||||||
|
|
@ -5,16 +5,23 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="#000000">
|
android:background="#000000">
|
||||||
|
|
||||||
<!-- 使用 OpenGlView 作为摄像头预览,全屏填充显示 -->
|
<!-- 使用 FrameLayout 包裹 OpenGlView,实现 centerCrop 效果 -->
|
||||||
<com.pedro.rtplibrary.view.OpenGlView
|
<FrameLayout
|
||||||
android:id="@+id/openGlView"
|
android:id="@+id/previewContainer"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
|
android:clipChildren="true"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
app:keepAspectRatio="false" />
|
|
||||||
|
<com.pedro.rtplibrary.view.OpenGlView
|
||||||
|
android:id="@+id/openGlView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center" />
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
<!-- 顶部工具栏 -->
|
<!-- 顶部工具栏 -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|
|
||||||
|
|
@ -2,290 +2,374 @@
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout 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="match_parent">
|
android:layout_height="match_parent"
|
||||||
|
android:background="#000000">
|
||||||
|
|
||||||
<ProgressBar
|
<!-- 顶部主播信息区域 -->
|
||||||
android:id="@+id/loading"
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:id="@+id/topBar"
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView
|
|
||||||
android:id="@+id/scroll"
|
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="wrap_content"
|
||||||
android:fillViewport="true"
|
android:background="@drawable/bg_gradient_top"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="12dp"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<!-- 返回按钮 -->
|
||||||
android:layout_width="match_parent"
|
<ImageButton
|
||||||
|
android:id="@+id/backButton"
|
||||||
|
android:layout_width="36dp"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
android:background="@drawable/bg_circle_semi_transparent"
|
||||||
|
android:contentDescription="返回"
|
||||||
|
android:src="@drawable/ic_arrow_back_24"
|
||||||
|
app:tint="@android:color/white" />
|
||||||
|
|
||||||
|
<!-- 主播头像 -->
|
||||||
|
<de.hdodenhof.circleimageview.CircleImageView
|
||||||
|
android:id="@+id/streamerAvatar"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:src="@drawable/ic_person_24" />
|
||||||
|
|
||||||
|
<!-- 主播信息 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="16dp">
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
<com.google.android.material.button.MaterialButton
|
android:orientation="vertical">
|
||||||
android:id="@+id/backButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="返回"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/liveBadge"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingHorizontal="10dp"
|
|
||||||
android:paddingVertical="6dp"
|
|
||||||
android:text="未开播"
|
|
||||||
android:textColor="@android:color/white"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/titleText"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:text="Room"
|
|
||||||
android:textSize="20sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/backButton" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/streamerText"
|
android:id="@+id/streamerText"
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:text="主播: "
|
|
||||||
android:textColor="#666666"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/titleText" />
|
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:id="@+id/playerContainer"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="200dp"
|
|
||||||
android:layout_marginTop="14dp"
|
|
||||||
android:layout_marginStart="32dp"
|
|
||||||
android:layout_marginEnd="32dp"
|
|
||||||
android:background="#000000"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/streamerText">
|
|
||||||
|
|
||||||
<androidx.media3.ui.PlayerView
|
|
||||||
android:id="@+id/playerView"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
app:resize_mode="fit"
|
|
||||||
app:show_buffering="when_playing"
|
|
||||||
app:use_controller="true" />
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/offlineHint"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="14dp"
|
|
||||||
android:gravity="center"
|
|
||||||
android:padding="18dp"
|
|
||||||
android:text="主播暂未开播"
|
|
||||||
android:textColor="#666666"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/streamerText" />
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Barrier
|
|
||||||
android:id="@+id/playerOrOfflineBarrier"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:barrierDirection="bottom"
|
android:ellipsize="end"
|
||||||
app:constraint_referenced_ids="playerContainer,offlineHint" />
|
android:maxLines="1"
|
||||||
|
android:text="主播名称"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/streamInfoTitle"
|
android:id="@+id/viewerCount"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="0 观看"
|
||||||
|
android:textColor="#CCCCCC"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 直播状态标签 -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/liveBadge"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/bg_live_badge"
|
||||||
|
android:paddingHorizontal="10dp"
|
||||||
|
android:paddingVertical="4dp"
|
||||||
|
android:text="直播中"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
|
<!-- 关闭按钮 -->
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/closeButton"
|
||||||
|
android:layout_width="36dp"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:background="@drawable/bg_circle_semi_transparent"
|
||||||
|
android:contentDescription="关闭"
|
||||||
|
android:src="@drawable/ic_close_24"
|
||||||
|
app:tint="@android:color/white" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 直播标题 -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/titleText"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:text="直播标题"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/topBar" />
|
||||||
|
|
||||||
|
<!-- 视频播放区域 - 16:9 比例,点击可全屏 -->
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/playerContainer"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:background="#000000"
|
||||||
|
app:layout_constraintDimensionRatio="16:9"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/titleText">
|
||||||
|
|
||||||
|
<androidx.media3.ui.PlayerView
|
||||||
|
android:id="@+id/playerView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:resize_mode="fit"
|
||||||
|
app:show_buffering="when_playing"
|
||||||
|
app:use_controller="false" />
|
||||||
|
|
||||||
|
<!-- 全屏按钮 -->
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/fullscreenButton"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_gravity="end|bottom"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:background="@drawable/bg_circle_semi_transparent"
|
||||||
|
android:contentDescription="全屏"
|
||||||
|
android:src="@drawable/ic_fullscreen_24"
|
||||||
|
app:tint="@android:color/white" />
|
||||||
|
|
||||||
|
<!-- 未开播提示 -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/offlineHint"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:text="主播暂未开播"
|
||||||
|
android:textColor="#999999"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<!-- 加载指示器 -->
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/loading"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:visibility="gone" />
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- 弹幕/聊天区域 -->
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/chatRecyclerView"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:paddingHorizontal="12dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/bottomBar"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/playerContainer" />
|
||||||
|
|
||||||
|
<!-- 底部互动栏 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/bottomBar"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/bg_gradient_bottom"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="12dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent">
|
||||||
|
|
||||||
|
<!-- 弹幕输入框 -->
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/chatInput"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:background="@drawable/bg_chat_input"
|
||||||
|
android:hint="说点什么..."
|
||||||
|
android:inputType="text"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:paddingHorizontal="16dp"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:textColorHint="#999999"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<!-- 点赞按钮 -->
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/likeButton"
|
||||||
|
android:layout_width="44dp"
|
||||||
|
android:layout_height="44dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:background="@drawable/bg_circle_semi_transparent"
|
||||||
|
android:contentDescription="点赞"
|
||||||
|
android:src="@drawable/ic_like_24"
|
||||||
|
app:tint="#FF6B6B" />
|
||||||
|
|
||||||
|
<!-- 点赞数 -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/likeCountText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:text="0"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
|
<!-- 礼物按钮 -->
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/giftButton"
|
||||||
|
android:layout_width="44dp"
|
||||||
|
android:layout_height="44dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:background="@drawable/bg_circle_semi_transparent"
|
||||||
|
android:contentDescription="礼物"
|
||||||
|
android:src="@drawable/ic_gift_24"
|
||||||
|
app:tint="#FFD700" />
|
||||||
|
|
||||||
|
<!-- 发送按钮 -->
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/sendButton"
|
||||||
|
android:layout_width="44dp"
|
||||||
|
android:layout_height="44dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:background="@drawable/bg_live_send_btn"
|
||||||
|
android:contentDescription="发送"
|
||||||
|
android:src="@drawable/ic_send_24"
|
||||||
|
app:tint="@android:color/white" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 推流信息区域(仅房主可见) -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/streamInfoLayout"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:background="@drawable/bg_rounded_semi_transparent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/bottomBar"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="📡 推流信息"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/rtmpValue"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="18dp"
|
android:layout_weight="1"
|
||||||
android:text="推流信息"
|
android:ellipsize="middle"
|
||||||
android:textSize="16sp"
|
android:singleLine="true"
|
||||||
android:textStyle="bold"
|
android:text="rtmp://..."
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:textColor="#CCCCCC"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
android:textSize="12sp" />
|
||||||
app:layout_constraintTop_toBottomOf="@id/playerOrOfflineBarrier" />
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<TextView
|
||||||
android:id="@+id/rtmpLayout"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/streamInfoTitle">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
|
||||||
android:id="@+id/rtmpValue"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:focusable="false"
|
|
||||||
android:hint="推流地址"
|
|
||||||
android:inputType="textUri" />
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/copyRtmpButton"
|
android:id="@+id/copyRtmpButton"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="复制地址"
|
android:background="?attr/selectableItemBackground"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:padding="4dp"
|
||||||
app:layout_constraintTop_toBottomOf="@id/rtmpLayout" />
|
android:text="复制"
|
||||||
|
android:textColor="#4FC3F7"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Group
|
<LinearLayout
|
||||||
android:id="@+id/rtmpAltGroup"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_marginTop="4dp"
|
||||||
android:visibility="gone"
|
android:orientation="horizontal">
|
||||||
app:constraint_referenced_ids="rtmpAltLayout" />
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<TextView
|
||||||
android:id="@+id/rtmpAltLayout"
|
android:id="@+id/keyValue"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="10dp"
|
android:layout_weight="1"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:ellipsize="middle"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
android:singleLine="true"
|
||||||
app:layout_constraintTop_toBottomOf="@id/copyRtmpButton">
|
android:text="密钥: ..."
|
||||||
|
android:textColor="#CCCCCC"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
<TextView
|
||||||
android:id="@+id/rtmpAltValue"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:focusable="false"
|
|
||||||
android:hint="电脑本机 OBS 可用(等价地址)"
|
|
||||||
android:inputType="textUri" />
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
|
||||||
android:id="@+id/keyLayout"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/rtmpAltLayout">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
|
||||||
android:id="@+id/keyValue"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:focusable="false"
|
|
||||||
android:hint="推流密钥"
|
|
||||||
android:inputType="text" />
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/copyKeyButton"
|
android:id="@+id/copyKeyButton"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="复制密钥"
|
android:background="?attr/selectableItemBackground"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:padding="4dp"
|
||||||
app:layout_constraintTop_toBottomOf="@id/keyLayout" />
|
android:text="复制"
|
||||||
|
android:textColor="#4FC3F7"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<TextView
|
||||||
android:id="@+id/deleteButton"
|
android:id="@+id/deleteButton"
|
||||||
android:layout_width="0dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="18dp"
|
android:layout_gravity="end"
|
||||||
android:text="删除房间"
|
android:layout_marginTop="8dp"
|
||||||
android:visibility="gone"
|
android:background="?attr/selectableItemBackground"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:padding="4dp"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
android:text="删除房间"
|
||||||
app:layout_constraintTop_toBottomOf="@id/copyKeyButton" />
|
android:textColor="#FF6B6B"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- 聊天区域 -->
|
<!-- 隐藏的旧控件(保持兼容性) -->
|
||||||
<TextView
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/chatTitle"
|
android:id="@+id/rtmpLayout"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="0dp"
|
||||||
android:layout_marginTop="18dp"
|
android:visibility="gone"
|
||||||
android:text="💬 直播间聊天"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
android:textSize="16sp"
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
android:textStyle="bold"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/copyKeyButton" />
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/chatRecyclerView"
|
android:id="@+id/rtmpAltLayout"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="200dp"
|
android:layout_height="0dp"
|
||||||
android:layout_marginTop="8dp"
|
android:visibility="gone"
|
||||||
android:background="#F5F5F5"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
android:padding="8dp"
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/chatTitle" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
android:id="@+id/chatInputLayout"
|
android:id="@+id/rtmpAltValue"
|
||||||
android:layout_width="0dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content" />
|
||||||
android:layout_marginTop="8dp"
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
android:orientation="horizontal"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/chatRecyclerView">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/chatInput"
|
android:id="@+id/keyLayout"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="0dp"
|
||||||
android:layout_weight="1"
|
android:visibility="gone"
|
||||||
android:hint="发送弹幕..."
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
android:inputType="text"
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
android:maxLines="1" />
|
|
||||||
|
|
||||||
<!-- 点赞按钮 -->
|
<androidx.constraintlayout.widget.Group
|
||||||
<ImageButton
|
android:id="@+id/rtmpAltGroup"
|
||||||
android:id="@+id/likeButton"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="48dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="48dp"
|
android:visibility="gone" />
|
||||||
android:layout_marginStart="4dp"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:src="@drawable/ic_like_24"
|
|
||||||
android:contentDescription="点赞" />
|
|
||||||
|
|
||||||
<!-- 点赞数显示 -->
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/likeCountText"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:layout_marginStart="4dp"
|
|
||||||
android:text="0"
|
|
||||||
android:textSize="14sp"
|
|
||||||
android:textColor="#666666" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/sendButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:text="发送" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
</androidx.core.widget.NestedScrollView>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,12 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="#000000">
|
android:background="#000000">
|
||||||
|
|
||||||
<!-- 全屏播放器容器 - 底层,视频填充整个屏幕 -->
|
<!-- 播放器容器 - 居中显示 -->
|
||||||
<!-- 注意:高度会根据设备类型动态调整(电脑50%,手机100%) -->
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/playerContainer"
|
android:id="@+id/playerContainer"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="0dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
android:background="#000000"
|
android:background="#000000"
|
||||||
android:tag="playerContainer"
|
android:tag="playerContainer"
|
||||||
android:elevation="1dp">
|
android:elevation="1dp">
|
||||||
|
|
@ -29,18 +29,18 @@
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
|
|
||||||
<!-- ExoPlayer 播放器 - 填充模式 -->
|
<!-- ExoPlayer 播放器 - 保持比例 -->
|
||||||
<androidx.media3.ui.PlayerView
|
<androidx.media3.ui.PlayerView
|
||||||
android:id="@+id/playerView"
|
android:id="@+id/playerView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
app:resize_mode="fill"
|
app:resize_mode="fit"
|
||||||
app:use_controller="false"
|
app:use_controller="false"
|
||||||
app:show_buffering="when_playing"
|
app:show_buffering="when_playing"
|
||||||
app:surface_type="texture_view" />
|
app:surface_type="texture_view" />
|
||||||
|
|
||||||
<!-- GSYVideoPlayer 播放器视图 - 填充模式 -->
|
<!-- GSYVideoPlayer 播放器视图 -->
|
||||||
<com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer
|
<com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer
|
||||||
android:id="@+id/gsyPlayer"
|
android:id="@+id/gsyPlayer"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
@ -82,6 +82,32 @@
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 全屏按钮 - 视频区域右下角 -->
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/fullscreenButton"
|
||||||
|
android:layout_width="36dp"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
android:layout_gravity="end|bottom"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:background="@drawable/bg_circle_semi_transparent"
|
||||||
|
android:contentDescription="全屏"
|
||||||
|
android:src="@drawable/ic_fullscreen_24"
|
||||||
|
android:tint="@android:color/white"
|
||||||
|
android:visibility="visible" />
|
||||||
|
|
||||||
|
<!-- 退出全屏按钮 - 视频区域左上角(全屏时显示) -->
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/exitFullscreenButton"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_gravity="start|top"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:background="@drawable/bg_circle_semi_transparent"
|
||||||
|
android:contentDescription="退出全屏"
|
||||||
|
android:src="@drawable/ic_arrow_back_24"
|
||||||
|
android:tint="@android:color/white"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
<!-- UI覆盖层 - 悬浮在视频上方,透明背景 -->
|
<!-- UI覆盖层 - 悬浮在视频上方,透明背景 -->
|
||||||
|
|
@ -124,17 +150,79 @@
|
||||||
android:src="@drawable/ic_arrow_back_24"
|
android:src="@drawable/ic_arrow_back_24"
|
||||||
app:tint="@android:color/white" />
|
app:tint="@android:color/white" />
|
||||||
|
|
||||||
<View
|
<!-- 主播信息卡片 - 放在顶部栏内 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/roomInfoLayout"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1" />
|
android:layout_weight="1"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:background="@drawable/bg_rounded_semi_transparent"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingHorizontal="8dp"
|
||||||
|
android:paddingVertical="6dp">
|
||||||
|
|
||||||
|
<de.hdodenhof.circleimageview.CircleImageView
|
||||||
|
android:id="@+id/streamerAvatar"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:src="@drawable/ic_person_24"
|
||||||
|
app:civ_border_color="#FFFFFF"
|
||||||
|
app:civ_border_width="1dp" />
|
||||||
|
|
||||||
|
<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/streamerName"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="主播"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:textSize="13sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/roomTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:maxWidth="120dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="直播间"
|
||||||
|
android:textColor="#CCFFFFFF"
|
||||||
|
android:textSize="11sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/followButton"
|
||||||
|
style="@style/Widget.Material3.Button.TonalButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="26dp"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:minWidth="48dp"
|
||||||
|
android:paddingHorizontal="10dp"
|
||||||
|
android:text="关注"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:textSize="11sp"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
app:backgroundTint="#FF4081"
|
||||||
|
app:cornerRadius="13dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- 直播状态标签 -->
|
<!-- 直播状态标签 -->
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/liveTag"
|
android:id="@+id/liveTag"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:background="@drawable/live_badge_background"
|
android:background="@drawable/live_badge_background"
|
||||||
android:paddingHorizontal="8dp"
|
android:paddingHorizontal="8dp"
|
||||||
android:paddingVertical="3dp"
|
android:paddingVertical="3dp"
|
||||||
|
|
@ -148,6 +236,7 @@
|
||||||
android:id="@+id/topViewerLayout"
|
android:id="@+id/topViewerLayout"
|
||||||
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:background="@drawable/bg_rounded_semi_transparent"
|
android:background="@drawable/bg_rounded_semi_transparent"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
|
|
@ -182,74 +271,6 @@
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<!-- 主播信息卡片 - 悬浮在左上角 -->
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/roomInfoLayout"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="12dp"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:background="@drawable/bg_rounded_semi_transparent"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:paddingHorizontal="8dp"
|
|
||||||
android:paddingVertical="6dp"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/topBar">
|
|
||||||
|
|
||||||
<de.hdodenhof.circleimageview.CircleImageView
|
|
||||||
android:id="@+id/streamerAvatar"
|
|
||||||
android:layout_width="32dp"
|
|
||||||
android:layout_height="32dp"
|
|
||||||
android:src="@drawable/ic_person_24"
|
|
||||||
app:civ_border_color="#FFFFFF"
|
|
||||||
app:civ_border_width="1dp" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/streamerName"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="主播"
|
|
||||||
android:textColor="@android:color/white"
|
|
||||||
android:textSize="13sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/roomTitle"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:maxWidth="100dp"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:text="直播间"
|
|
||||||
android:textColor="#CCFFFFFF"
|
|
||||||
android:textSize="11sp" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/followButton"
|
|
||||||
style="@style/Widget.Material3.Button.TonalButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="26dp"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:minWidth="48dp"
|
|
||||||
android:paddingHorizontal="10dp"
|
|
||||||
android:text="关注"
|
|
||||||
android:textColor="@android:color/white"
|
|
||||||
android:textSize="11sp"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
app:backgroundTint="#FF4081"
|
|
||||||
app:cornerRadius="13dp" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<!-- 底部渐变遮罩 -->
|
<!-- 底部渐变遮罩 -->
|
||||||
<View
|
<View
|
||||||
android:id="@+id/bottomGradient"
|
android:id="@+id/bottomGradient"
|
||||||
|
|
@ -340,35 +361,6 @@
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- 全屏按钮 - 右下角 -->
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/fullscreenButton"
|
|
||||||
android:layout_width="36dp"
|
|
||||||
android:layout_height="36dp"
|
|
||||||
android:layout_marginEnd="12dp"
|
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
android:background="@drawable/bg_circle_semi_transparent"
|
|
||||||
android:contentDescription="全屏"
|
|
||||||
android:src="@drawable/ic_fullscreen_24"
|
|
||||||
android:tint="@android:color/white"
|
|
||||||
android:visibility="visible"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/rightButtons"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
|
||||||
<!-- 退出全屏按钮 -->
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/exitFullscreenButton"
|
|
||||||
android:layout_width="36dp"
|
|
||||||
android:layout_height="36dp"
|
|
||||||
android:layout_margin="12dp"
|
|
||||||
android:visibility="gone"
|
|
||||||
android:background="@drawable/bg_circle_semi_transparent"
|
|
||||||
android:contentDescription="退出全屏"
|
|
||||||
android:src="@drawable/ic_arrow_back_24"
|
|
||||||
android:tint="@android:color/white"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<!-- 底部输入栏 - 悬浮 -->
|
<!-- 底部输入栏 - 悬浮 -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/chatInputLayout"
|
android:id="@+id/chatInputLayout"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user