主题:直播和作品按钮页面

This commit is contained in:
xiao12feng8 2026-01-09 14:45:05 +08:00
parent 669e2457a9
commit d19d0cc631
10 changed files with 951 additions and 11 deletions

View File

@ -197,6 +197,11 @@
android:name="com.example.livestreaming.UserProfileActivity" android:name="com.example.livestreaming.UserProfileActivity"
android:exported="false" /> android:exported="false" />
<activity
android:name="com.example.livestreaming.PublishCenterActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity <activity
android:name="com.example.livestreaming.WishTreeActivity" android:name="com.example.livestreaming.WishTreeActivity"
android:exported="false" /> android:exported="false" />

View File

@ -609,7 +609,8 @@ public class MainActivity extends AppCompatActivity {
binding.fabAddLive.setOnClickListener(new DebounceClickListener() { binding.fabAddLive.setOnClickListener(new DebounceClickListener() {
@Override @Override
public void onDebouncedClick(View v) { public void onDebouncedClick(View v) {
showCreateRoomDialog(); // 跳转到发布中心页面
PublishCenterActivity.start(MainActivity.this);
} }
}); });

View File

@ -539,7 +539,7 @@ public class ProfileActivity extends AppCompatActivity {
if (!AuthHelper.requireLogin(this, "发布作品需要登录")) { if (!AuthHelper.requireLogin(this, "发布作品需要登录")) {
return; return;
} }
PublishWorkActivity.start(this); PublishCenterActivity.start(this);
}); });
// 悬浮按钮固定显示 // 悬浮按钮固定显示
@ -548,7 +548,7 @@ public class ProfileActivity extends AppCompatActivity {
if (!AuthHelper.requireLogin(this, "发布作品需要登录")) { if (!AuthHelper.requireLogin(this, "发布作品需要登录")) {
return; return;
} }
PublishWorkActivity.start(this); PublishCenterActivity.start(this);
}); });
binding.likedGoBrowseBtn.setOnClickListener(v -> startActivity(new Intent(this, MainActivity.class))); binding.likedGoBrowseBtn.setOnClickListener(v -> startActivity(new Intent(this, MainActivity.class)));
binding.favGoBrowseBtn.setOnClickListener(v -> startActivity(new Intent(this, MainActivity.class))); binding.favGoBrowseBtn.setOnClickListener(v -> startActivity(new Intent(this, MainActivity.class)));
@ -578,7 +578,7 @@ public class ProfileActivity extends AppCompatActivity {
if (!AuthHelper.requireLogin(this, "发布作品需要登录")) { if (!AuthHelper.requireLogin(this, "发布作品需要登录")) {
return; return;
} }
PublishWorkActivity.start(this); PublishCenterActivity.start(this);
}); });
loadMyWorks(); loadMyWorks();

View File

@ -0,0 +1,373 @@
package com.example.livestreaming;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import com.bumptech.glide.Glide;
import com.example.livestreaming.databinding.ActivityPublishCenterBinding;
import com.example.livestreaming.net.ApiClient;
import com.example.livestreaming.net.ApiResponse;
import com.example.livestreaming.net.CategoryResponse;
import com.example.livestreaming.net.CreateRoomRequest;
import com.example.livestreaming.net.FileUploadResponse;
import com.example.livestreaming.net.Room;
import com.example.livestreaming.net.StreamUrls;
import com.example.livestreaming.net.WorksRequest;
import com.google.android.material.tabs.TabLayout;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class PublishCenterActivity extends AppCompatActivity {
private ActivityPublishCenterBinding binding;
private int currentTab = 0;
private List<CategoryResponse> categoryList = new ArrayList<>();
private int selectedCategoryId = -1;
private Uri dynamicCoverUri = null;
private ActivityResultLauncher<Intent> imagePickerLauncher;
public static void start(Context context) {
context.startActivity(new Intent(context, PublishCenterActivity.class));
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!AuthHelper.requireLogin(this, "发布内容需要登录")) {
finish();
return;
}
binding = ActivityPublishCenterBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setupImagePicker();
setupToolbar();
setupTabs();
setupClickListeners();
loadCategories();
showTab(0);
}
private void setupImagePicker() {
imagePickerLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) {
Uri uri = result.getData().getData();
if (uri != null) {
dynamicCoverUri = uri;
showDynamicCover(uri);
}
}
}
);
}
private void setupToolbar() {
binding.btnBack.setOnClickListener(v -> finish());
}
private void setupTabs() {
binding.tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) { showTab(tab.getPosition()); }
@Override
public void onTabUnselected(TabLayout.Tab tab) {}
@Override
public void onTabReselected(TabLayout.Tab tab) {}
});
}
private void setupClickListeners() {
binding.btnStartLive.setOnClickListener(v -> startMobileLive());
binding.btnPublishDynamic.setOnClickListener(v -> publishDynamic());
binding.dynamicCoverContainer.setOnClickListener(v -> pickDynamicCover());
binding.btnRemoveDynamicCover.setOnClickListener(v -> removeDynamicCover());
binding.btnPublishWork.setOnClickListener(v -> PublishWorkActivity.start(this));
binding.btnCopyStreamUrl.setOnClickListener(v -> copyToClipboard("推流地址", binding.tvStreamUrl.getText().toString()));
binding.btnCopyStreamKey.setOnClickListener(v -> copyToClipboard("推流码", binding.tvStreamKey.getText().toString()));
binding.btnGetStreamInfo.setOnClickListener(v -> createPcLiveRoom());
}
private void showTab(int position) {
currentTab = position;
binding.contentMobileLive.setVisibility(View.GONE);
binding.contentDynamic.setVisibility(View.GONE);
binding.contentWork.setVisibility(View.GONE);
binding.contentPcLive.setVisibility(View.GONE);
switch (position) {
case 0: binding.contentMobileLive.setVisibility(View.VISIBLE); break;
case 1: binding.contentDynamic.setVisibility(View.VISIBLE); break;
case 2: binding.contentWork.setVisibility(View.VISIBLE); break;
case 3: binding.contentPcLive.setVisibility(View.VISIBLE); break;
}
}
private void loadCategories() {
ApiClient.getService(this).getLiveRoomCategories().enqueue(new Callback<ApiResponse<List<CategoryResponse>>>() {
@Override
public void onResponse(Call<ApiResponse<List<CategoryResponse>>> call, Response<ApiResponse<List<CategoryResponse>>> response) {
if (response.isSuccessful() && response.body() != null && response.body().isOk()) {
categoryList = response.body().getData();
if (categoryList == null) categoryList = new ArrayList<>();
}
setupCategorySpinner();
}
@Override
public void onFailure(Call<ApiResponse<List<CategoryResponse>>> call, Throwable t) {
setupCategorySpinner();
}
});
}
private void setupCategorySpinner() {
List<String> names = new ArrayList<>();
names.add("请选择分类");
for (CategoryResponse cat : categoryList) names.add(cat.getName());
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, names);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
binding.spinnerPcCategory.setAdapter(adapter);
binding.spinnerPcCategory.setOnItemSelectedListener(new android.widget.AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(android.widget.AdapterView<?> parent, View view, int pos, long id) {
selectedCategoryId = (pos > 0 && pos <= categoryList.size()) ? categoryList.get(pos - 1).getId() : -1;
}
@Override
public void onNothingSelected(android.widget.AdapterView<?> parent) { selectedCategoryId = -1; }
});
}
private void startMobileLive() {
String title = binding.etLiveTitle.getText().toString().trim();
if (TextUtils.isEmpty(title)) title = "我的直播间";
Intent intent = new Intent(this, BroadcastActivity.class);
intent.putExtra("title", title);
startActivity(intent);
finish();
}
private void pickDynamicCover() {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("image/*");
imagePickerLauncher.launch(intent);
}
private void showDynamicCover(Uri uri) {
binding.ivDynamicCover.setVisibility(View.VISIBLE);
binding.dynamicCoverPlaceholder.setVisibility(View.GONE);
binding.btnRemoveDynamicCover.setVisibility(View.VISIBLE);
Glide.with(this).load(uri).centerCrop().into(binding.ivDynamicCover);
}
private void removeDynamicCover() {
dynamicCoverUri = null;
binding.ivDynamicCover.setVisibility(View.GONE);
binding.dynamicCoverPlaceholder.setVisibility(View.VISIBLE);
binding.btnRemoveDynamicCover.setVisibility(View.GONE);
}
private void publishDynamic() {
String content = binding.etDynamicContent.getText().toString().trim();
if (TextUtils.isEmpty(content)) {
Toast.makeText(this, "请输入动态内容", Toast.LENGTH_SHORT).show();
return;
}
if (content.length() > 1000) {
Toast.makeText(this, "内容不能超过1000字", Toast.LENGTH_SHORT).show();
return;
}
binding.btnPublishDynamic.setEnabled(false);
binding.btnPublishDynamic.setText("发布中...");
if (dynamicCoverUri != null) {
// 用户选择了封面上传后发布
uploadDynamicCoverAndPublish(content);
} else {
// 用户没选封面使用特殊标记纯文字动态
// 使用 "text_only" 作为标记在展示时识别为纯文字动态
doPublishDynamic(content, "text_only");
}
}
private void uploadDynamicCoverAndPublish(String content) {
try {
File file = uriToFile(dynamicCoverUri);
if (file == null) {
Toast.makeText(this, "无法读取图片", Toast.LENGTH_SHORT).show();
resetDynamicButton();
return;
}
RequestBody requestFile = RequestBody.create(MediaType.parse("image/*"), file);
MultipartBody.Part body = MultipartBody.Part.createFormData("file", file.getName(), requestFile);
RequestBody model = RequestBody.create(MediaType.parse("text/plain"), "work");
RequestBody pid = RequestBody.create(MediaType.parse("text/plain"), "0");
ApiClient.getService(this).uploadImage(body, model, pid).enqueue(new Callback<ApiResponse<FileUploadResponse>>() {
@Override
public void onResponse(Call<ApiResponse<FileUploadResponse>> call, Response<ApiResponse<FileUploadResponse>> response) {
if (response.isSuccessful() && response.body() != null && response.body().isOk()) {
FileUploadResponse data = response.body().getData();
doPublishDynamic(content, data != null ? data.getUrl() : null);
} else {
resetDynamicButton();
Toast.makeText(PublishCenterActivity.this, "封面上传失败", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<ApiResponse<FileUploadResponse>> call, Throwable t) {
resetDynamicButton();
Toast.makeText(PublishCenterActivity.this, "网络错误", Toast.LENGTH_SHORT).show();
}
});
} catch (Exception e) {
resetDynamicButton();
Toast.makeText(this, "图片处理失败", Toast.LENGTH_SHORT).show();
}
}
private void doPublishDynamic(String content, String coverUrl) {
WorksRequest request = new WorksRequest();
request.setTitle("");
request.setDescription(content);
request.setType("IMAGE");
request.setImageUrls(new ArrayList<>());
request.setCoverUrl(coverUrl != null ? coverUrl : "");
ApiClient.getService(this).publishWork(request).enqueue(new Callback<ApiResponse<Long>>() {
@Override
public void onResponse(Call<ApiResponse<Long>> call, Response<ApiResponse<Long>> response) {
resetDynamicButton();
if (response.isSuccessful() && response.body() != null && response.body().isOk()) {
Toast.makeText(PublishCenterActivity.this, "动态发布成功", Toast.LENGTH_SHORT).show();
binding.etDynamicContent.setText("");
removeDynamicCover();
finish();
} else {
String msg = response.body() != null ? response.body().getMessage() : "发布失败";
Toast.makeText(PublishCenterActivity.this, msg, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<ApiResponse<Long>> call, Throwable t) {
resetDynamicButton();
Toast.makeText(PublishCenterActivity.this, "网络错误", Toast.LENGTH_SHORT).show();
}
});
}
private void resetDynamicButton() {
binding.btnPublishDynamic.setEnabled(true);
binding.btnPublishDynamic.setText("发布动态");
}
private File uriToFile(Uri uri) {
try {
InputStream is = getContentResolver().openInputStream(uri);
if (is == null) return null;
File file = new File(getCacheDir(), "temp_cover_" + System.currentTimeMillis() + ".jpg");
FileOutputStream os = new FileOutputStream(file);
byte[] buf = new byte[4096];
int len;
while ((len = is.read(buf)) != -1) os.write(buf, 0, len);
is.close();
os.close();
return file;
} catch (Exception e) {
return null;
}
}
private void createPcLiveRoom() {
String title = binding.etPcLiveTitle.getText().toString().trim();
if (TextUtils.isEmpty(title)) {
Toast.makeText(this, "请输入直播标题", Toast.LENGTH_SHORT).show();
return;
}
if (selectedCategoryId <= 0) {
Toast.makeText(this, "请选择直播分类", Toast.LENGTH_SHORT).show();
return;
}
binding.btnGetStreamInfo.setEnabled(false);
binding.btnGetStreamInfo.setText("创建中...");
binding.streamInfoLoading.setVisibility(View.VISIBLE);
// 获取当前用户昵称作为主播名称
String streamerName = com.example.livestreaming.net.AuthStore.getNickname(this);
if (TextUtils.isEmpty(streamerName)) {
streamerName = title; // 如果没有昵称使用标题
}
CreateRoomRequest request = new CreateRoomRequest(title, streamerName, "pc");
request.setCategoryId(selectedCategoryId);
ApiClient.getService(this).createRoom(request).enqueue(new Callback<ApiResponse<Room>>() {
@Override
public void onResponse(Call<ApiResponse<Room>> call, Response<ApiResponse<Room>> response) {
binding.btnGetStreamInfo.setEnabled(true);
binding.btnGetStreamInfo.setText("创建直播间并获取推流信息");
binding.streamInfoLoading.setVisibility(View.GONE);
if (response.isSuccessful() && response.body() != null && response.body().isOk()) {
Room room = response.body().getData();
if (room != null) {
showStreamInfo(room);
} else {
Toast.makeText(PublishCenterActivity.this, "创建成功但未获取到推流信息", Toast.LENGTH_SHORT).show();
}
} else {
String msg = response.body() != null ? response.body().getMessage() : "创建直播间失败";
Toast.makeText(PublishCenterActivity.this, msg, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<ApiResponse<Room>> call, Throwable t) {
binding.btnGetStreamInfo.setEnabled(true);
binding.btnGetStreamInfo.setText("创建直播间并获取推流信息");
binding.streamInfoLoading.setVisibility(View.GONE);
Toast.makeText(PublishCenterActivity.this, "网络错误", Toast.LENGTH_SHORT).show();
}
});
}
private void showStreamInfo(Room room) {
binding.streamInfoContent.setVisibility(View.VISIBLE);
String streamKey = room.getStreamKey();
StreamUrls streamUrls = room.getStreamUrls();
String pushUrl = (streamUrls != null && streamUrls.getRtmp() != null) ? streamUrls.getRtmp() : "";
binding.tvStreamUrl.setText(!TextUtils.isEmpty(pushUrl) ? pushUrl : "未获取到推流地址");
binding.tvStreamKey.setText(!TextUtils.isEmpty(streamKey) ? streamKey : "未获取到推流码");
Toast.makeText(this, "直播间创建成功!", Toast.LENGTH_SHORT).show();
}
private void copyToClipboard(String label, String text) {
if (TextUtils.isEmpty(text) || text.startsWith("未获取")) {
Toast.makeText(this, "内容为空", Toast.LENGTH_SHORT).show();
return;
}
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
if (clipboard != null) {
clipboard.setPrimaryClip(ClipData.newPlainText(label, text));
Toast.makeText(this, label + "已复制", Toast.LENGTH_SHORT).show();
}
}
}

View File

@ -59,7 +59,10 @@ public class WorksAdapter extends RecyclerView.Adapter<WorksAdapter.WorksViewHol
} }
static class WorksViewHolder extends RecyclerView.ViewHolder { static class WorksViewHolder extends RecyclerView.ViewHolder {
private final View coverContainer;
private final View textContentContainer;
private final ImageView coverImage; private final ImageView coverImage;
private final TextView contentText;
private final TextView titleText; private final TextView titleText;
private final TextView authorText; private final TextView authorText;
private final TextView likeCountText; private final TextView likeCountText;
@ -67,7 +70,10 @@ public class WorksAdapter extends RecyclerView.Adapter<WorksAdapter.WorksViewHol
public WorksViewHolder(@NonNull View itemView) { public WorksViewHolder(@NonNull View itemView) {
super(itemView); super(itemView);
coverContainer = itemView.findViewById(R.id.coverContainer);
textContentContainer = itemView.findViewById(R.id.textContentContainer);
coverImage = itemView.findViewById(R.id.worksCoverImage); coverImage = itemView.findViewById(R.id.worksCoverImage);
contentText = itemView.findViewById(R.id.worksContentText);
titleText = itemView.findViewById(R.id.worksTitleText); titleText = itemView.findViewById(R.id.worksTitleText);
authorText = itemView.findViewById(R.id.worksAuthorText); authorText = itemView.findViewById(R.id.worksAuthorText);
likeCountText = itemView.findViewById(R.id.worksLikeCountText); likeCountText = itemView.findViewById(R.id.worksLikeCountText);
@ -82,18 +88,36 @@ public class WorksAdapter extends RecyclerView.Adapter<WorksAdapter.WorksViewHol
"title=" + works.getTitle() + "title=" + works.getTitle() +
", userName=" + works.getUserName() + ", userName=" + works.getUserName() +
", authorName=" + works.getAuthorName() + ", authorName=" + works.getAuthorName() +
", likeCount=" + works.getLikeCount()); ", likeCount=" + works.getLikeCount() +
", coverUrl=" + works.getCoverUrl());
// 判断是否有有效封面排除纯文字动态标记
String coverUrl = works.getCoverUrl();
boolean hasValidCover = coverUrl != null && !coverUrl.isEmpty()
&& !coverUrl.equals("text_only"); // 排除纯文字动态标记
if (hasValidCover) {
// 有封面显示封面图片
coverContainer.setVisibility(View.VISIBLE);
textContentContainer.setVisibility(View.GONE);
// 设置封面图片
if (works.getCoverUrl() != null && !works.getCoverUrl().isEmpty()) {
Glide.with(itemView.getContext()) Glide.with(itemView.getContext())
.load(works.getCoverUrl()) .load(coverUrl)
.centerCrop() .centerCrop()
.placeholder(R.drawable.bg_cover_placeholder) .placeholder(R.drawable.bg_cover_placeholder)
.error(R.drawable.bg_cover_placeholder) .error(R.drawable.bg_cover_placeholder)
.into(coverImage); .into(coverImage);
} else { } else {
coverImage.setImageResource(R.drawable.bg_cover_placeholder); // 无封面显示文字内容
coverContainer.setVisibility(View.GONE);
textContentContainer.setVisibility(View.VISIBLE);
String content = works.getDescription();
if (content != null && !content.isEmpty()) {
contentText.setText(content);
} else {
contentText.setText("暂无内容");
}
} }
// 设置标题 // 设置标题

View File

@ -1,11 +1,11 @@
<?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">
<corners android:radius="8dp" />
<stroke <stroke
android:width="1dp" android:width="1dp"
android:color="#CCCCCC" android:color="#CCCCCC"
android:dashWidth="4dp" android:dashWidth="4dp"
android:dashGap="2dp" /> android:dashGap="2dp" />
<corners android:radius="8dp" />
<solid android:color="#F5F5F5" /> <solid android:color="#F5F5F5" />
</shape> </shape>

View 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">
<stroke
android:width="1dp"
android:color="#DDDDDD" />
<corners android:radius="8dp" />
<solid android:color="#FFFFFF" />
</shape>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF4757"
android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM17,15.59L15.59,17 12,13.41 8.41,17 7,15.59 10.59,12 7,8.41 8.41,7 12,10.59 15.59,7 17,8.41 13.41,12 17,15.59z"/>
</vector>

View File

@ -0,0 +1,494 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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="match_parent"
android:background="@color/white">
<LinearLayout
android:id="@+id/topBar"
android:layout_width="match_parent"
android:layout_height="56dp"
android:orientation="horizontal"
android:gravity="center_vertical"
android:paddingHorizontal="16dp"
android:background="@color/white"
android:elevation="2dp">
<ImageView
android:id="@+id/btnBack"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_close_24"
android:contentDescription="关闭"
app:tint="#333333" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="16dp"
android:text="发布"
android:textSize="18sp"
android:textColor="#333333"
android:textStyle="bold" />
</LinearLayout>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginTop="56dp"
android:background="@color/white"
app:tabMode="fixed"
app:tabGravity="fill"
app:tabIndicatorColor="#FF4757"
app:tabIndicatorHeight="3dp"
app:tabSelectedTextColor="#FF4757"
app:tabTextColor="#666666"
app:tabRippleColor="@android:color/transparent">
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="手机开播" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="发布动态" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="发布作品" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="电脑开播" />
</com.google.android.material.tabs.TabLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="104dp">
<!-- 手机开播 -->
<ScrollView
android:id="@+id/contentMobileLive"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="手机开播"
android:textSize="20sp"
android:textColor="#333333"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="使用手机摄像头进行直播"
android:textSize="14sp"
android:textColor="#999999" />
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:hint="直播标题"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etLiveTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:maxLength="30" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/btnStartLive"
android:layout_width="match_parent"
android:layout_height="52dp"
android:layout_marginTop="32dp"
android:text="开始直播"
android:textSize="16sp"
app:backgroundTint="#FF4757"
app:cornerRadius="26dp" />
</LinearLayout>
</ScrollView>
<!-- 发布动态 -->
<ScrollView
android:id="@+id/contentDynamic"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="发布动态"
android:textSize="20sp"
android:textColor="#333333"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="快速发布文字动态,可选择添加封面图片"
android:textSize="14sp"
android:textColor="#999999" />
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:hint="说点什么..."
app:counterEnabled="true"
app:counterMaxLength="1000"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etDynamicContent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textMultiLine"
android:minLines="5"
android:gravity="top"
android:maxLength="1000" />
</com.google.android.material.textfield.TextInputLayout>
<!-- 封面图片选择(可选) -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="封面图片(可选)"
android:textSize="14sp"
android:textColor="#666666" />
<FrameLayout
android:id="@+id/dynamicCoverContainer"
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_marginTop="8dp"
android:background="@drawable/bg_dashed_border"
android:clickable="true"
android:focusable="true">
<ImageView
android:id="@+id/ivDynamicCover"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:visibility="gone" />
<LinearLayout
android:id="@+id/dynamicCoverPlaceholder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<ImageView
android:layout_width="32dp"
android:layout_height="32dp"
android:src="@drawable/ic_add_photo_24"
app:tint="#999999" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="添加封面"
android:textSize="12sp"
android:textColor="#999999" />
</LinearLayout>
<ImageView
android:id="@+id/btnRemoveDynamicCover"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="top|end"
android:layout_margin="4dp"
android:src="@drawable/ic_close_circle_24"
android:visibility="gone"
android:contentDescription="移除封面" />
</FrameLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/btnPublishDynamic"
android:layout_width="match_parent"
android:layout_height="52dp"
android:layout_marginTop="32dp"
android:text="发布动态"
android:textSize="16sp"
app:backgroundTint="#FF4757"
app:cornerRadius="26dp" />
</LinearLayout>
</ScrollView>
<!-- 发布作品 -->
<ScrollView
android:id="@+id/contentWork"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="发布作品"
android:textSize="20sp"
android:textColor="#333333"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="发布图片或视频作品,支持添加标题、描述等"
android:textSize="14sp"
android:textColor="#999999" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnPublishWork"
android:layout_width="match_parent"
android:layout_height="52dp"
android:layout_marginTop="32dp"
android:text="去发布作品"
android:textSize="16sp"
app:backgroundTint="#FF4757"
app:cornerRadius="26dp" />
</LinearLayout>
</ScrollView>
<!-- 电脑开播 -->
<ScrollView
android:id="@+id/contentPcLive"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="电脑开播"
android:textSize="20sp"
android:textColor="#333333"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="使用OBS等推流软件进行直播"
android:textSize="14sp"
android:textColor="#999999" />
<!-- 直播标题 -->
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:hint="直播标题"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etPcLiveTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:maxLength="30" />
</com.google.android.material.textfield.TextInputLayout>
<!-- 分类选择 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="选择分类"
android:textSize="14sp"
android:textColor="#666666" />
<Spinner
android:id="@+id/spinnerPcCategory"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginTop="8dp"
android:background="@drawable/bg_spinner"
android:paddingHorizontal="12dp" />
<ProgressBar
android:id="@+id/streamInfoLoading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="24dp"
android:visibility="gone" />
<LinearLayout
android:id="@+id/streamInfoContent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginTop="24dp"
android:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="推流地址"
android:textSize="14sp"
android:textColor="#666666" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="horizontal"
android:background="@drawable/bg_edit_text"
android:padding="12dp">
<TextView
android:id="@+id/tvStreamUrl"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text=""
android:textSize="13sp"
android:textColor="#333333"
android:maxLines="2"
android:ellipsize="end" />
<ImageView
android:id="@+id/btnCopyStreamUrl"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="8dp"
android:src="@drawable/ic_copy_24"
android:contentDescription="复制"
app:tint="#666666" />
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="推流码"
android:textSize="14sp"
android:textColor="#666666" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="horizontal"
android:background="@drawable/bg_edit_text"
android:padding="12dp">
<TextView
android:id="@+id/tvStreamKey"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text=""
android:textSize="13sp"
android:textColor="#333333"
android:maxLines="2"
android:ellipsize="end" />
<ImageView
android:id="@+id/btnCopyStreamKey"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="8dp"
android:src="@drawable/ic_copy_24"
android:contentDescription="复制"
app:tint="#666666" />
</LinearLayout>
</LinearLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/btnGetStreamInfo"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginTop="24dp"
android:text="创建直播间并获取推流信息"
android:textSize="14sp"
app:backgroundTint="#FF4757"
app:cornerRadius="24dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="使用说明"
android:textSize="14sp"
android:textColor="#333333"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="1. 填写直播标题并选择分类\n2. 点击按钮创建直播间获取推流信息\n3. 下载并安装OBS Studio\n4. 打开OBS点击设置 - 推流\n5. 服务选择自定义\n6. 将上方的推流地址和推流码填入\n7. 点击开始推流即可"
android:textSize="13sp"
android:textColor="#666666"
android:lineSpacingExtra="4dp" />
</LinearLayout>
</ScrollView>
</FrameLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -12,8 +12,9 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<!-- 封面图片 --> <!-- 封面图片区域 -->
<FrameLayout <FrameLayout
android:id="@+id/coverContainer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="240dp"> android:layout_height="240dp">
@ -49,6 +50,29 @@
</FrameLayout> </FrameLayout>
<!-- 纯文字动态区域(无封面时显示) -->
<LinearLayout
android:id="@+id/textContentContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="120dp"
android:orientation="vertical"
android:padding="12dp"
android:background="#F8F8F8"
android:visibility="gone">
<TextView
android:id="@+id/worksContentText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="15sp"
android:textColor="#333333"
android:lineSpacingExtra="4dp"
android:maxLines="6"
android:ellipsize="end" />
</LinearLayout>
<!-- 作品信息 --> <!-- 作品信息 -->
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"