安卓界面的修改
|
|
@ -13,7 +13,8 @@ android {
|
|||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
buildConfigField("String", "API_BASE_URL", "\"http://10.0.2.2:3001/api/\"")
|
||||
buildConfigField("String", "API_BASE_URL_EMULATOR", "\"http://10.0.2.2:3001/api/\"")
|
||||
buildConfigField("String", "API_BASE_URL_DEVICE", "\"http://192.168.1.100:3001/api/\"")
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,44 @@
|
|||
android:hardwareAccelerated="true"
|
||||
android:largeHeap="true">
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_paths" />
|
||||
</provider>
|
||||
|
||||
<activity
|
||||
android:name="com.example.livestreaming.TabPlaceholderActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name="com.example.livestreaming.WatchHistoryActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name="com.example.livestreaming.MyFriendsActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name="com.example.livestreaming.FansListActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name="com.example.livestreaming.LikesListActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name="com.example.livestreaming.FollowingListActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name="com.example.livestreaming.EditProfileActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name="com.example.livestreaming.FishPondActivity"
|
||||
android:exported="false" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
package com.example.livestreaming;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
import androidx.recyclerview.widget.ListAdapter;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class ChatAdapter extends ListAdapter<ChatMessage, ChatAdapter.VH> {
|
||||
|
||||
public ChatAdapter() {
|
||||
super(DIFF);
|
||||
}
|
||||
|
||||
private static final DiffUtil.ItemCallback<ChatMessage> DIFF = new DiffUtil.ItemCallback<ChatMessage>() {
|
||||
@Override
|
||||
public boolean areItemsTheSame(@NonNull ChatMessage oldItem, @NonNull ChatMessage newItem) {
|
||||
// Chat messages have no stable id; treat position as identity.
|
||||
return oldItem == newItem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(@NonNull ChatMessage oldItem, @NonNull ChatMessage newItem) {
|
||||
String ou = oldItem.getUser();
|
||||
String nu = newItem.getUser();
|
||||
if (ou == null ? nu != null : !ou.equals(nu)) return false;
|
||||
String om = oldItem.getMessage();
|
||||
String nm = newItem.getMessage();
|
||||
if (om == null ? nm != null : !om.equals(nm)) return false;
|
||||
return oldItem.isSystem() == newItem.isSystem();
|
||||
}
|
||||
};
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_chat_message, parent, false);
|
||||
return new VH(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull VH holder, int position) {
|
||||
ChatMessage m = getItem(position);
|
||||
if (m == null) {
|
||||
holder.messageText.setText("");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m.isSystem()) {
|
||||
holder.messageText.setText(m.getMessage() != null ? m.getMessage() : "");
|
||||
holder.messageText.setAlpha(0.8f);
|
||||
} else {
|
||||
String user = m.getUser();
|
||||
String msg = m.getMessage();
|
||||
if (user == null || user.trim().isEmpty()) {
|
||||
holder.messageText.setText(msg != null ? msg : "");
|
||||
} else {
|
||||
holder.messageText.setText(user + ": " + (msg != null ? msg : ""));
|
||||
}
|
||||
holder.messageText.setAlpha(1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
static class VH extends RecyclerView.ViewHolder {
|
||||
final TextView messageText;
|
||||
|
||||
VH(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
messageText = itemView.findViewById(R.id.messageText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package com.example.livestreaming;
|
||||
|
||||
public class ChatMessage {
|
||||
|
||||
private final String user;
|
||||
private final String message;
|
||||
private final boolean system;
|
||||
|
||||
public ChatMessage(String systemMessage, boolean system) {
|
||||
this.user = null;
|
||||
this.message = systemMessage;
|
||||
this.system = system;
|
||||
}
|
||||
|
||||
public ChatMessage(String user, String message) {
|
||||
this.user = user;
|
||||
this.message = message;
|
||||
this.system = false;
|
||||
}
|
||||
|
||||
public String getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public boolean isSystem() {
|
||||
return system;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
package com.example.livestreaming;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class ConversationItem {
|
||||
|
||||
private final String id;
|
||||
private final String title;
|
||||
private final String lastMessage;
|
||||
private final String timeText;
|
||||
private final int unreadCount;
|
||||
private final boolean muted;
|
||||
|
||||
public ConversationItem(String id, String title, String lastMessage, String timeText, int unreadCount, boolean muted) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.lastMessage = lastMessage;
|
||||
this.timeText = timeText;
|
||||
this.unreadCount = unreadCount;
|
||||
this.muted = muted;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getLastMessage() {
|
||||
return lastMessage;
|
||||
}
|
||||
|
||||
public String getTimeText() {
|
||||
return timeText;
|
||||
}
|
||||
|
||||
public int getUnreadCount() {
|
||||
return unreadCount;
|
||||
}
|
||||
|
||||
public boolean isMuted() {
|
||||
return muted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof ConversationItem)) return false;
|
||||
ConversationItem that = (ConversationItem) o;
|
||||
return unreadCount == that.unreadCount
|
||||
&& muted == that.muted
|
||||
&& Objects.equals(id, that.id)
|
||||
&& Objects.equals(title, that.title)
|
||||
&& Objects.equals(lastMessage, that.lastMessage)
|
||||
&& Objects.equals(timeText, that.timeText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, title, lastMessage, timeText, unreadCount, muted);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
package com.example.livestreaming;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
import androidx.recyclerview.widget.ListAdapter;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.example.livestreaming.databinding.ItemConversationBinding;
|
||||
|
||||
public class ConversationsAdapter extends ListAdapter<ConversationItem, ConversationsAdapter.VH> {
|
||||
|
||||
public interface OnConversationClickListener {
|
||||
void onConversationClick(ConversationItem item);
|
||||
}
|
||||
|
||||
private final OnConversationClickListener onConversationClickListener;
|
||||
|
||||
public ConversationsAdapter(OnConversationClickListener onConversationClickListener) {
|
||||
super(DIFF);
|
||||
this.onConversationClickListener = onConversationClickListener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
ItemConversationBinding binding = ItemConversationBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new VH(binding, onConversationClickListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull VH holder, int position) {
|
||||
holder.bind(getItem(position));
|
||||
}
|
||||
|
||||
static class VH extends RecyclerView.ViewHolder {
|
||||
|
||||
private final ItemConversationBinding binding;
|
||||
private final OnConversationClickListener onConversationClickListener;
|
||||
|
||||
VH(ItemConversationBinding binding, OnConversationClickListener onConversationClickListener) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
this.onConversationClickListener = onConversationClickListener;
|
||||
}
|
||||
|
||||
void bind(ConversationItem item) {
|
||||
binding.title.setText(item != null && item.getTitle() != null ? item.getTitle() : "");
|
||||
binding.lastMessage.setText(item != null && item.getLastMessage() != null ? item.getLastMessage() : "");
|
||||
binding.timeText.setText(item != null && item.getTimeText() != null ? item.getTimeText() : "");
|
||||
|
||||
int unread = item != null ? item.getUnreadCount() : 0;
|
||||
if (unread > 0) {
|
||||
binding.unreadBadge.setVisibility(View.VISIBLE);
|
||||
binding.unreadBadge.setText(unread > 99 ? "99+" : String.valueOf(unread));
|
||||
} else {
|
||||
binding.unreadBadge.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (item != null && item.isMuted()) {
|
||||
binding.muteIcon.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
binding.muteIcon.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
binding.getRoot().setOnClickListener(v -> {
|
||||
if (item == null) return;
|
||||
if (onConversationClickListener != null) onConversationClickListener.onConversationClick(item);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static final DiffUtil.ItemCallback<ConversationItem> DIFF = new DiffUtil.ItemCallback<ConversationItem>() {
|
||||
@Override
|
||||
public boolean areItemsTheSame(@NonNull ConversationItem oldItem, @NonNull ConversationItem newItem) {
|
||||
String o = oldItem.getId();
|
||||
String n = newItem.getId();
|
||||
return o != null && o.equals(n);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(@NonNull ConversationItem oldItem, @NonNull ConversationItem newItem) {
|
||||
return oldItem.equals(newItem);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
package com.example.livestreaming;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.content.FileProvider;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.example.livestreaming.databinding.ActivityEditProfileBinding;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class EditProfileActivity extends AppCompatActivity {
|
||||
|
||||
private ActivityEditProfileBinding binding;
|
||||
|
||||
private static final String PREFS_NAME = "profile_prefs";
|
||||
private static final String KEY_NAME = "profile_name";
|
||||
private static final String KEY_BIO = "profile_bio";
|
||||
private static final String KEY_AVATAR_URI = "profile_avatar_uri";
|
||||
private static final String KEY_BIRTHDAY = "profile_birthday";
|
||||
private static final String KEY_GENDER = "profile_gender";
|
||||
private static final String KEY_LOCATION = "profile_location";
|
||||
|
||||
private Uri selectedAvatarUri = null;
|
||||
private Uri pendingCameraUri = null;
|
||||
|
||||
private ActivityResultLauncher<String> pickImageLauncher;
|
||||
private ActivityResultLauncher<Uri> takePictureLauncher;
|
||||
|
||||
public static void start(Context context) {
|
||||
Intent intent = new Intent(context, EditProfileActivity.class);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
binding = ActivityEditProfileBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
pickImageLauncher = registerForActivityResult(new ActivityResultContracts.GetContent(), uri -> {
|
||||
if (uri == null) return;
|
||||
selectedAvatarUri = uri;
|
||||
Glide.with(this).load(uri).circleCrop().into(binding.avatarPreview);
|
||||
});
|
||||
|
||||
takePictureLauncher = registerForActivityResult(new ActivityResultContracts.TakePicture(), success -> {
|
||||
if (!success) return;
|
||||
if (pendingCameraUri == null) return;
|
||||
selectedAvatarUri = pendingCameraUri;
|
||||
Glide.with(this).load(pendingCameraUri).circleCrop().into(binding.avatarPreview);
|
||||
});
|
||||
|
||||
binding.backButton.setOnClickListener(v -> finish());
|
||||
binding.cancelButton.setOnClickListener(v -> finish());
|
||||
|
||||
loadFromPrefs();
|
||||
|
||||
binding.avatarRow.setOnClickListener(v -> showAvatarBottomSheet());
|
||||
|
||||
binding.saveButton.setOnClickListener(v -> {
|
||||
String name = binding.inputName.getText() != null ? binding.inputName.getText().toString().trim() : "";
|
||||
String bio = binding.inputBio.getText() != null ? binding.inputBio.getText().toString().trim() : "";
|
||||
String birthday = binding.inputBirthday.getText() != null ? binding.inputBirthday.getText().toString().trim() : "";
|
||||
String gender = binding.inputGender.getText() != null ? binding.inputGender.getText().toString().trim() : "";
|
||||
String location = binding.inputLocation.getText() != null ? binding.inputLocation.getText().toString().trim() : "";
|
||||
|
||||
if (TextUtils.isEmpty(name)) {
|
||||
Toast.makeText(this, "昵称不能为空", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
getSharedPreferences(PREFS_NAME, MODE_PRIVATE)
|
||||
.edit()
|
||||
.putString(KEY_NAME, name)
|
||||
.apply();
|
||||
|
||||
if (TextUtils.isEmpty(bio)) {
|
||||
getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().remove(KEY_BIO).apply();
|
||||
} else {
|
||||
getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().putString(KEY_BIO, bio).apply();
|
||||
}
|
||||
|
||||
if (selectedAvatarUri != null) {
|
||||
getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().putString(KEY_AVATAR_URI, selectedAvatarUri.toString()).apply();
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(birthday)) {
|
||||
getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().remove(KEY_BIRTHDAY).apply();
|
||||
} else {
|
||||
getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().putString(KEY_BIRTHDAY, birthday).apply();
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(gender)) {
|
||||
getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().remove(KEY_GENDER).apply();
|
||||
} else {
|
||||
getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().putString(KEY_GENDER, gender).apply();
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(location)) {
|
||||
getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().remove(KEY_LOCATION).apply();
|
||||
} else {
|
||||
getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().putString(KEY_LOCATION, location).apply();
|
||||
}
|
||||
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
private void loadFromPrefs() {
|
||||
String name = getSharedPreferences(PREFS_NAME, MODE_PRIVATE).getString(KEY_NAME, "");
|
||||
String bio = getSharedPreferences(PREFS_NAME, MODE_PRIVATE).getString(KEY_BIO, "");
|
||||
String birthday = getSharedPreferences(PREFS_NAME, MODE_PRIVATE).getString(KEY_BIRTHDAY, "");
|
||||
String gender = getSharedPreferences(PREFS_NAME, MODE_PRIVATE).getString(KEY_GENDER, "");
|
||||
String location = getSharedPreferences(PREFS_NAME, MODE_PRIVATE).getString(KEY_LOCATION, "");
|
||||
String avatarUri = getSharedPreferences(PREFS_NAME, MODE_PRIVATE).getString(KEY_AVATAR_URI, null);
|
||||
|
||||
binding.inputName.setText(name);
|
||||
binding.inputBio.setText(bio);
|
||||
binding.inputBirthday.setText(birthday);
|
||||
binding.inputGender.setText(gender);
|
||||
binding.inputLocation.setText(location);
|
||||
|
||||
if (!TextUtils.isEmpty(avatarUri)) {
|
||||
selectedAvatarUri = Uri.parse(avatarUri);
|
||||
Glide.with(this).load(selectedAvatarUri).circleCrop().into(binding.avatarPreview);
|
||||
}
|
||||
}
|
||||
|
||||
private void showAvatarBottomSheet() {
|
||||
BottomSheetDialog dialog = new BottomSheetDialog(this);
|
||||
android.view.View view = getLayoutInflater().inflate(R.layout.bottom_sheet_avatar_picker, null);
|
||||
dialog.setContentView(view);
|
||||
|
||||
android.view.View pick = view.findViewById(R.id.actionPickGallery);
|
||||
android.view.View camera = view.findViewById(R.id.actionTakePhoto);
|
||||
android.view.View cancel = view.findViewById(R.id.actionCancel);
|
||||
|
||||
pick.setOnClickListener(v -> {
|
||||
dialog.dismiss();
|
||||
pickImageLauncher.launch("image/*");
|
||||
});
|
||||
|
||||
camera.setOnClickListener(v -> {
|
||||
dialog.dismiss();
|
||||
Uri uri = createTempCameraUri();
|
||||
if (uri == null) return;
|
||||
pendingCameraUri = uri;
|
||||
takePictureLauncher.launch(uri);
|
||||
});
|
||||
|
||||
cancel.setOnClickListener(v -> dialog.dismiss());
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private Uri createTempCameraUri() {
|
||||
try {
|
||||
File dir = new File(getCacheDir(), "images");
|
||||
if (!dir.exists()) dir.mkdirs();
|
||||
File file = new File(dir, "avatar_" + System.currentTimeMillis() + ".jpg");
|
||||
return FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".fileprovider", file);
|
||||
} catch (Exception e) {
|
||||
Toast.makeText(this, "无法创建相机文件", Toast.LENGTH_SHORT).show();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package com.example.livestreaming;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.example.livestreaming.databinding.ActivityFansListBinding;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class FansListActivity extends AppCompatActivity {
|
||||
|
||||
private ActivityFansListBinding binding;
|
||||
|
||||
public static void start(Context context) {
|
||||
Intent intent = new Intent(context, FansListActivity.class);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
binding = ActivityFansListBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
binding.backButton.setOnClickListener(v -> finish());
|
||||
|
||||
FriendsAdapter adapter = new FriendsAdapter(item -> {
|
||||
if (item == null) return;
|
||||
Toast.makeText(this, "打开粉丝:" + item.getName(), Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
|
||||
binding.fansRecyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||||
binding.fansRecyclerView.setAdapter(adapter);
|
||||
adapter.submitList(buildDemoFans());
|
||||
}
|
||||
|
||||
private List<FriendItem> buildDemoFans() {
|
||||
List<FriendItem> list = new ArrayList<>();
|
||||
list.add(new FriendItem("f1", "小雨", "关注了你 · 2分钟前", true));
|
||||
list.add(new FriendItem("f2", "阿宁", "关注了你 · 昨天", false));
|
||||
list.add(new FriendItem("f3", "小星", "关注了你 · 周二", true));
|
||||
list.add(new FriendItem("f4", "小林", "关注了你 · 上周", false));
|
||||
list.add(new FriendItem("f5", "阿杰", "关注了你 · 上周", false));
|
||||
list.add(new FriendItem("f6", "小七", "关注了你 · 上月", true));
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package com.example.livestreaming;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.example.livestreaming.databinding.ActivityFollowingListBinding;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class FollowingListActivity extends AppCompatActivity {
|
||||
|
||||
private ActivityFollowingListBinding binding;
|
||||
|
||||
public static void start(Context context) {
|
||||
Intent intent = new Intent(context, FollowingListActivity.class);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
binding = ActivityFollowingListBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
binding.backButton.setOnClickListener(v -> finish());
|
||||
|
||||
FriendsAdapter adapter = new FriendsAdapter(item -> {
|
||||
if (item == null) return;
|
||||
Toast.makeText(this, "打开关注:" + item.getName(), Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
|
||||
binding.followingRecyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||||
binding.followingRecyclerView.setAdapter(adapter);
|
||||
adapter.submitList(buildDemoFollowing());
|
||||
}
|
||||
|
||||
private List<FriendItem> buildDemoFollowing() {
|
||||
List<FriendItem> list = new ArrayList<>();
|
||||
list.add(new FriendItem("fo1", "王者荣耀陪练", "主播 · 正在直播", true));
|
||||
list.add(new FriendItem("fo2", "音乐电台", "主播 · 今日 20:00 开播", false));
|
||||
list.add(new FriendItem("fo3", "户外阿杰", "主播 · 1小时前开播", true));
|
||||
list.add(new FriendItem("fo4", "美食探店", "主播 · 昨天直播", false));
|
||||
list.add(new FriendItem("fo5", "聊天小七", "主播 · 正在直播", true));
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
package com.example.livestreaming;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class FriendItem {
|
||||
|
||||
private final String id;
|
||||
private final String name;
|
||||
private final String subtitle;
|
||||
private final boolean online;
|
||||
|
||||
public FriendItem(String id, String name, String subtitle, boolean online) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.subtitle = subtitle;
|
||||
this.online = online;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getSubtitle() {
|
||||
return subtitle;
|
||||
}
|
||||
|
||||
public boolean isOnline() {
|
||||
return online;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof FriendItem)) return false;
|
||||
FriendItem that = (FriendItem) o;
|
||||
return online == that.online
|
||||
&& Objects.equals(id, that.id)
|
||||
&& Objects.equals(name, that.name)
|
||||
&& Objects.equals(subtitle, that.subtitle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, name, subtitle, online);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
package com.example.livestreaming;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
import androidx.recyclerview.widget.ListAdapter;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.example.livestreaming.databinding.ItemFriendBinding;
|
||||
|
||||
public class FriendsAdapter extends ListAdapter<FriendItem, FriendsAdapter.VH> {
|
||||
|
||||
public interface OnFriendClickListener {
|
||||
void onFriendClick(FriendItem item);
|
||||
}
|
||||
|
||||
private final OnFriendClickListener onFriendClickListener;
|
||||
|
||||
public FriendsAdapter(OnFriendClickListener onFriendClickListener) {
|
||||
super(DIFF);
|
||||
this.onFriendClickListener = onFriendClickListener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
ItemFriendBinding binding = ItemFriendBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new VH(binding, onFriendClickListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull VH holder, int position) {
|
||||
holder.bind(getItem(position));
|
||||
}
|
||||
|
||||
static class VH extends RecyclerView.ViewHolder {
|
||||
|
||||
private final ItemFriendBinding binding;
|
||||
private final OnFriendClickListener onFriendClickListener;
|
||||
|
||||
VH(ItemFriendBinding binding, OnFriendClickListener onFriendClickListener) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
this.onFriendClickListener = onFriendClickListener;
|
||||
}
|
||||
|
||||
void bind(FriendItem item) {
|
||||
binding.name.setText(item != null && item.getName() != null ? item.getName() : "");
|
||||
binding.subtitle.setText(item != null && item.getSubtitle() != null ? item.getSubtitle() : "");
|
||||
|
||||
if (item != null && item.isOnline()) {
|
||||
binding.onlineDot.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
binding.onlineDot.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
binding.getRoot().setOnClickListener(v -> {
|
||||
if (item == null) return;
|
||||
if (onFriendClickListener != null) onFriendClickListener.onFriendClick(item);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static final DiffUtil.ItemCallback<FriendItem> DIFF = new DiffUtil.ItemCallback<FriendItem>() {
|
||||
@Override
|
||||
public boolean areItemsTheSame(@NonNull FriendItem oldItem, @NonNull FriendItem newItem) {
|
||||
String o = oldItem.getId();
|
||||
String n = newItem.getId();
|
||||
return o != null && o.equals(n);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(@NonNull FriendItem oldItem, @NonNull FriendItem newItem) {
|
||||
return oldItem.equals(newItem);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package com.example.livestreaming;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.example.livestreaming.databinding.ActivityLikesListBinding;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class LikesListActivity extends AppCompatActivity {
|
||||
|
||||
private ActivityLikesListBinding binding;
|
||||
|
||||
public static void start(Context context) {
|
||||
Intent intent = new Intent(context, LikesListActivity.class);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
binding = ActivityLikesListBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
binding.backButton.setOnClickListener(v -> finish());
|
||||
|
||||
ConversationsAdapter adapter = new ConversationsAdapter(item -> {
|
||||
if (item == null) return;
|
||||
Toast.makeText(this, "查看获赞:" + item.getTitle(), Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
|
||||
binding.likesRecyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||||
binding.likesRecyclerView.setAdapter(adapter);
|
||||
adapter.submitList(buildDemoLikes());
|
||||
}
|
||||
|
||||
private List<ConversationItem> buildDemoLikes() {
|
||||
List<ConversationItem> list = new ArrayList<>();
|
||||
list.add(new ConversationItem("l1", "小雨", "赞了你的直播间", "09:12", 0, false));
|
||||
list.add(new ConversationItem("l2", "阿宁", "赞了你的作品", "昨天", 0, false));
|
||||
list.add(new ConversationItem("l3", "小星", "赞了你", "周二", 0, false));
|
||||
list.add(new ConversationItem("l4", "小林", "赞了你的直播回放", "上周", 0, false));
|
||||
list.add(new ConversationItem("l5", "阿杰", "赞了你的作品", "上周", 0, false));
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
|||
import com.example.livestreaming.databinding.ActivityMainBinding;
|
||||
import com.example.livestreaming.databinding.DialogCreateRoomBinding;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
import com.example.livestreaming.net.ApiClient;
|
||||
import com.example.livestreaming.net.ApiResponse;
|
||||
import com.example.livestreaming.net.CreateRoomRequest;
|
||||
|
|
@ -42,6 +43,9 @@ public class MainActivity extends AppCompatActivity {
|
|||
private ActivityMainBinding binding;
|
||||
private RoomsAdapter adapter;
|
||||
|
||||
private final List<Room> allRooms = new ArrayList<>();
|
||||
private String currentCategory = "推荐";
|
||||
|
||||
private final Handler handler = new Handler(Looper.getMainLooper());
|
||||
private Runnable pollRunnable;
|
||||
|
||||
|
|
@ -76,12 +80,56 @@ public class MainActivity extends AppCompatActivity {
|
|||
binding.roomsRecyclerView.setAdapter(adapter);
|
||||
|
||||
// 立即显示演示数据,提升用户体验
|
||||
adapter.submitList(buildDemoRooms(6));
|
||||
allRooms.clear();
|
||||
allRooms.addAll(buildDemoRooms(12));
|
||||
applyCategoryFilter(currentCategory);
|
||||
}
|
||||
|
||||
private void setupUI() {
|
||||
binding.swipeRefresh.setOnRefreshListener(this::fetchRooms);
|
||||
|
||||
binding.topTabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
|
||||
@Override
|
||||
public void onTabSelected(TabLayout.Tab tab) {
|
||||
if (tab == null) return;
|
||||
CharSequence title = tab.getText();
|
||||
TabPlaceholderActivity.start(MainActivity.this, title != null ? title.toString() : "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabUnselected(TabLayout.Tab tab) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabReselected(TabLayout.Tab tab) {
|
||||
if (tab == null) return;
|
||||
CharSequence title = tab.getText();
|
||||
TabPlaceholderActivity.start(MainActivity.this, title != null ? title.toString() : "");
|
||||
}
|
||||
});
|
||||
|
||||
binding.categoryTabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
|
||||
@Override
|
||||
public void onTabSelected(TabLayout.Tab tab) {
|
||||
if (tab == null) return;
|
||||
CharSequence title = tab.getText();
|
||||
currentCategory = title != null ? title.toString() : "推荐";
|
||||
applyCategoryFilter(currentCategory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabUnselected(TabLayout.Tab tab) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabReselected(TabLayout.Tab tab) {
|
||||
if (tab == null) return;
|
||||
CharSequence title = tab.getText();
|
||||
currentCategory = title != null ? title.toString() : "推荐";
|
||||
applyCategoryFilter(currentCategory);
|
||||
}
|
||||
});
|
||||
|
||||
binding.roomsRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
@Override
|
||||
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
|
||||
|
|
@ -356,7 +404,9 @@ public class MainActivity extends AppCompatActivity {
|
|||
if (rooms == null || rooms.isEmpty()) {
|
||||
rooms = buildDemoRooms(12);
|
||||
}
|
||||
adapter.submitList(rooms);
|
||||
allRooms.clear();
|
||||
allRooms.addAll(rooms);
|
||||
applyCategoryFilter(currentCategory);
|
||||
adapter.bumpCoverOffset();
|
||||
}
|
||||
|
||||
|
|
@ -365,12 +415,42 @@ public class MainActivity extends AppCompatActivity {
|
|||
binding.loading.setVisibility(View.GONE);
|
||||
binding.swipeRefresh.setRefreshing(false);
|
||||
isFetching = false;
|
||||
adapter.submitList(buildDemoRooms(12));
|
||||
allRooms.clear();
|
||||
allRooms.addAll(buildDemoRooms(12));
|
||||
applyCategoryFilter(currentCategory);
|
||||
adapter.bumpCoverOffset();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void applyCategoryFilter(String category) {
|
||||
String c = category != null ? category : "推荐";
|
||||
if ("推荐".equals(c)) {
|
||||
adapter.submitList(new ArrayList<>(allRooms));
|
||||
return;
|
||||
}
|
||||
|
||||
List<Room> filtered = new ArrayList<>();
|
||||
for (Room r : allRooms) {
|
||||
if (r == null) continue;
|
||||
if (c.equals(getDemoCategoryForRoom(r))) {
|
||||
filtered.add(r);
|
||||
}
|
||||
}
|
||||
adapter.submitList(filtered);
|
||||
}
|
||||
|
||||
private String getDemoCategoryForRoom(Room room) {
|
||||
String[] categories = new String[]{"游戏", "才艺", "户外", "音乐", "美食", "聊天"};
|
||||
try {
|
||||
String seed = room != null && room.getId() != null ? room.getId() : room != null ? room.getTitle() : "";
|
||||
int h = Math.abs(seed != null ? seed.hashCode() : 0);
|
||||
return categories[h % categories.length];
|
||||
} catch (Exception ignored) {
|
||||
return "游戏";
|
||||
}
|
||||
}
|
||||
|
||||
private List<Room> buildDemoRooms(int count) {
|
||||
List<Room> list = new ArrayList<>();
|
||||
for (int i = 0; i < count; i++) {
|
||||
|
|
|
|||
|
|
@ -3,12 +3,17 @@ package com.example.livestreaming;
|
|||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.example.livestreaming.databinding.ActivityMessagesBinding;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MessagesActivity extends AppCompatActivity {
|
||||
|
||||
private ActivityMessagesBinding binding;
|
||||
|
|
@ -24,6 +29,8 @@ public class MessagesActivity extends AppCompatActivity {
|
|||
binding = ActivityMessagesBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
setupConversationList();
|
||||
|
||||
BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation;
|
||||
bottomNavigation.setSelectedItemId(R.id.nav_messages);
|
||||
bottomNavigation.setOnItemSelectedListener(item -> {
|
||||
|
|
@ -55,6 +62,28 @@ public class MessagesActivity extends AppCompatActivity {
|
|||
});
|
||||
}
|
||||
|
||||
private void setupConversationList() {
|
||||
ConversationsAdapter adapter = new ConversationsAdapter(item -> {
|
||||
if (item == null) return;
|
||||
Toast.makeText(this, "打开会话:" + item.getTitle(), Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
binding.conversationsRecyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||||
binding.conversationsRecyclerView.setAdapter(adapter);
|
||||
adapter.submitList(buildDemoConversations());
|
||||
}
|
||||
|
||||
private List<ConversationItem> buildDemoConversations() {
|
||||
List<ConversationItem> list = new ArrayList<>();
|
||||
list.add(new ConversationItem("sys", "系统通知", "欢迎来到直播间~新手指南已送达", "09:12", 2, false));
|
||||
list.add(new ConversationItem("a", "小王(主播)", "今晚8点开播,记得来捧场!", "昨天", 0, false));
|
||||
list.add(new ConversationItem("b", "附近的人", "嗨~一起连麦吗?", "昨天", 5, false));
|
||||
list.add(new ConversationItem("c", "运营小助手", "活动报名已通过,点击查看详情", "周二", 0, true));
|
||||
list.add(new ConversationItem("d", "直播间群聊", "[图片]", "周一", 19, false));
|
||||
list.add(new ConversationItem("e", "小李", "收到啦", "周一", 0, false));
|
||||
list.add(new ConversationItem("f", "客服", "您好,请描述一下遇到的问题", "上周", 1, false));
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,89 @@
|
|||
package com.example.livestreaming;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.example.livestreaming.databinding.ActivityMyFriendsBinding;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MyFriendsActivity extends AppCompatActivity {
|
||||
|
||||
private ActivityMyFriendsBinding binding;
|
||||
private FriendsAdapter adapter;
|
||||
private final List<FriendItem> all = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
binding = ActivityMyFriendsBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
binding.backButton.setOnClickListener(v -> finish());
|
||||
|
||||
adapter = new FriendsAdapter(item -> {
|
||||
if (item == null) return;
|
||||
Toast.makeText(this, "打开挚友:" + item.getName(), Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
|
||||
binding.friendsRecyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||||
binding.friendsRecyclerView.setAdapter(adapter);
|
||||
|
||||
all.clear();
|
||||
all.addAll(buildDemoFriends());
|
||||
adapter.submitList(new ArrayList<>(all));
|
||||
|
||||
binding.searchEdit.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
String q = s != null ? s.toString().trim() : "";
|
||||
applyFilter(q);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void applyFilter(String query) {
|
||||
if (query == null || query.trim().isEmpty()) {
|
||||
adapter.submitList(new ArrayList<>(all));
|
||||
return;
|
||||
}
|
||||
String q = query.toLowerCase();
|
||||
List<FriendItem> filtered = new ArrayList<>();
|
||||
for (FriendItem f : all) {
|
||||
if (f == null) continue;
|
||||
String name = f.getName() != null ? f.getName() : "";
|
||||
String sub = f.getSubtitle() != null ? f.getSubtitle() : "";
|
||||
if (name.toLowerCase().contains(q) || sub.toLowerCase().contains(q)) {
|
||||
filtered.add(f);
|
||||
}
|
||||
}
|
||||
adapter.submitList(filtered);
|
||||
}
|
||||
|
||||
private List<FriendItem> buildDemoFriends() {
|
||||
List<FriendItem> list = new ArrayList<>();
|
||||
list.add(new FriendItem("1", "小王", "最近在线:5分钟前", true));
|
||||
list.add(new FriendItem("2", "小李", "共同关注:王者荣耀", false));
|
||||
list.add(new FriendItem("3", "安安", "备注:一起连麦", true));
|
||||
list.add(new FriendItem("4", "小陈", "最近在线:昨天", false));
|
||||
list.add(new FriendItem("5", "小美", "共同关注:音乐", true));
|
||||
list.add(new FriendItem("6", "老张", "最近在线:3天前", false));
|
||||
list.add(new FriendItem("7", "小七", "备注:挚友", true));
|
||||
list.add(new FriendItem("8", "阿杰", "共同关注:美食", false));
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
package com.example.livestreaming;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class NearbyUser {
|
||||
|
||||
private final String id;
|
||||
private final String name;
|
||||
private final String distanceText;
|
||||
private final boolean isLive;
|
||||
|
||||
public NearbyUser(String id, String name, String distanceText, boolean isLive) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.distanceText = distanceText;
|
||||
this.isLive = isLive;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getDistanceText() {
|
||||
return distanceText;
|
||||
}
|
||||
|
||||
public boolean isLive() {
|
||||
return isLive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof NearbyUser)) return false;
|
||||
NearbyUser that = (NearbyUser) o;
|
||||
return isLive == that.isLive
|
||||
&& Objects.equals(id, that.id)
|
||||
&& Objects.equals(name, that.name)
|
||||
&& Objects.equals(distanceText, that.distanceText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, name, distanceText, isLive);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
package com.example.livestreaming;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
import androidx.recyclerview.widget.ListAdapter;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.example.livestreaming.databinding.ItemNearbyUserBinding;
|
||||
|
||||
public class NearbyUsersAdapter extends ListAdapter<NearbyUser, NearbyUsersAdapter.VH> {
|
||||
|
||||
public interface OnUserClickListener {
|
||||
void onUserClick(NearbyUser user);
|
||||
}
|
||||
|
||||
private final OnUserClickListener onUserClickListener;
|
||||
|
||||
public NearbyUsersAdapter(OnUserClickListener onUserClickListener) {
|
||||
super(DIFF);
|
||||
this.onUserClickListener = onUserClickListener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
ItemNearbyUserBinding binding = ItemNearbyUserBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new VH(binding, onUserClickListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull VH holder, int position) {
|
||||
holder.bind(getItem(position));
|
||||
}
|
||||
|
||||
static class VH extends RecyclerView.ViewHolder {
|
||||
|
||||
private final ItemNearbyUserBinding binding;
|
||||
private final OnUserClickListener onUserClickListener;
|
||||
|
||||
VH(ItemNearbyUserBinding binding, OnUserClickListener onUserClickListener) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
this.onUserClickListener = onUserClickListener;
|
||||
}
|
||||
|
||||
void bind(NearbyUser user) {
|
||||
binding.userName.setText(user != null && user.getName() != null ? user.getName() : "");
|
||||
binding.distanceText.setText(user != null && user.getDistanceText() != null ? user.getDistanceText() : "");
|
||||
|
||||
try {
|
||||
String seed = user != null && user.getId() != null ? user.getId() : String.valueOf(getBindingAdapterPosition());
|
||||
int h = Math.abs(seed.hashCode());
|
||||
int mode = h % 3;
|
||||
String ratio;
|
||||
if (mode == 0) {
|
||||
ratio = "1:1";
|
||||
} else if (mode == 1) {
|
||||
ratio = "3:4";
|
||||
} else {
|
||||
ratio = "4:3";
|
||||
}
|
||||
ViewGroup.LayoutParams lp = binding.avatarImage.getLayoutParams();
|
||||
if (lp instanceof ConstraintLayout.LayoutParams) {
|
||||
ConstraintLayout.LayoutParams clp = (ConstraintLayout.LayoutParams) lp;
|
||||
if (clp.dimensionRatio == null || !clp.dimensionRatio.equals(ratio)) {
|
||||
clp.dimensionRatio = ratio;
|
||||
binding.avatarImage.setLayoutParams(clp);
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
if (user != null && user.isLive()) {
|
||||
binding.liveBadge.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
binding.liveBadge.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
binding.getRoot().setOnClickListener(v -> {
|
||||
if (user == null) return;
|
||||
if (onUserClickListener != null) onUserClickListener.onUserClick(user);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static final DiffUtil.ItemCallback<NearbyUser> DIFF = new DiffUtil.ItemCallback<NearbyUser>() {
|
||||
@Override
|
||||
public boolean areItemsTheSame(@NonNull NearbyUser oldItem, @NonNull NearbyUser newItem) {
|
||||
String o = oldItem.getId();
|
||||
String n = newItem.getId();
|
||||
return o != null && o.equals(n);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(@NonNull NearbyUser oldItem, @NonNull NearbyUser newItem) {
|
||||
return oldItem.equals(newItem);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -3,9 +3,14 @@ package com.example.livestreaming;
|
|||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.example.livestreaming.databinding.ActivityProfileBinding;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
|
||||
|
|
@ -13,6 +18,18 @@ public class ProfileActivity extends AppCompatActivity {
|
|||
|
||||
private ActivityProfileBinding binding;
|
||||
|
||||
private static final String PREFS_NAME = "profile_prefs";
|
||||
private static final String KEY_NAME = "profile_name";
|
||||
private static final String KEY_BIO = "profile_bio";
|
||||
private static final String KEY_LEVEL = "profile_level";
|
||||
private static final String KEY_FANS_BADGE = "profile_fans_badge";
|
||||
private static final String KEY_BADGE = "profile_badge";
|
||||
private static final String KEY_AVATAR_RES = "profile_avatar_res";
|
||||
private static final String KEY_AVATAR_URI = "profile_avatar_uri";
|
||||
private static final String KEY_BIRTHDAY = "profile_birthday";
|
||||
private static final String KEY_GENDER = "profile_gender";
|
||||
private static final String KEY_LOCATION = "profile_location";
|
||||
|
||||
public static void start(Context context) {
|
||||
Intent intent = new Intent(context, ProfileActivity.class);
|
||||
context.startActivity(intent);
|
||||
|
|
@ -24,6 +41,10 @@ public class ProfileActivity extends AppCompatActivity {
|
|||
binding = ActivityProfileBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
loadProfileFromPrefs();
|
||||
setupEditableAreas();
|
||||
setupNavigationClicks();
|
||||
|
||||
BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation;
|
||||
bottomNavigation.setSelectedItemId(R.id.nav_profile);
|
||||
bottomNavigation.setOnItemSelectedListener(item -> {
|
||||
|
|
@ -52,10 +73,96 @@ public class ProfileActivity extends AppCompatActivity {
|
|||
});
|
||||
}
|
||||
|
||||
private void loadProfileFromPrefs() {
|
||||
String n = getSharedPreferences(PREFS_NAME, MODE_PRIVATE).getString(KEY_NAME, null);
|
||||
if (!TextUtils.isEmpty(n)) binding.name.setText(n);
|
||||
|
||||
String bio = getSharedPreferences(PREFS_NAME, MODE_PRIVATE).getString(KEY_BIO, null);
|
||||
if (!TextUtils.isEmpty(bio)) {
|
||||
binding.bioText.setText(bio);
|
||||
binding.bioText.setTextColor(0xFF111111);
|
||||
}
|
||||
|
||||
String avatarUri = getSharedPreferences(PREFS_NAME, MODE_PRIVATE).getString(KEY_AVATAR_URI, null);
|
||||
if (!TextUtils.isEmpty(avatarUri)) {
|
||||
Glide.with(this).load(avatarUri).into(binding.avatar);
|
||||
} else {
|
||||
int avatarRes = getSharedPreferences(PREFS_NAME, MODE_PRIVATE).getInt(KEY_AVATAR_RES, 0);
|
||||
if (avatarRes != 0) {
|
||||
binding.avatar.setImageResource(avatarRes);
|
||||
}
|
||||
}
|
||||
|
||||
// 等级/称号/徽章:保持固定显示(例如“月亮/星耀/至尊”),不从本地缓存覆盖。
|
||||
}
|
||||
|
||||
private void setupEditableAreas() {
|
||||
binding.name.setOnClickListener(v -> showEditDialog("编辑昵称", binding.name.getText() != null ? binding.name.getText().toString() : "", text -> {
|
||||
binding.name.setText(text);
|
||||
getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().putString(KEY_NAME, text).apply();
|
||||
}));
|
||||
|
||||
binding.bioText.setOnClickListener(v -> showEditDialog("编辑签名", binding.bioText.getText() != null ? binding.bioText.getText().toString() : "", text -> {
|
||||
if (TextUtils.isEmpty(text)) {
|
||||
binding.bioText.setText("填写个人签名更容易获得关注,点击此处添加");
|
||||
binding.bioText.setTextColor(0xFF999999);
|
||||
getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().remove(KEY_BIO).apply();
|
||||
} else {
|
||||
binding.bioText.setText(text);
|
||||
binding.bioText.setTextColor(0xFF111111);
|
||||
getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().putString(KEY_BIO, text).apply();
|
||||
}
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
private void setupNavigationClicks() {
|
||||
binding.topActionSearch.setOnClickListener(v -> TabPlaceholderActivity.start(this, "搜索"));
|
||||
binding.topActionClock.setOnClickListener(v -> TabPlaceholderActivity.start(this, "定位/发现"));
|
||||
binding.topActionMore.setOnClickListener(v -> TabPlaceholderActivity.start(this, "更多"));
|
||||
|
||||
binding.following.setOnClickListener(v -> FollowingListActivity.start(this));
|
||||
binding.followers.setOnClickListener(v -> FansListActivity.start(this));
|
||||
binding.likes.setOnClickListener(v -> LikesListActivity.start(this));
|
||||
|
||||
binding.recordBtn.setOnClickListener(v -> Toast.makeText(this, "录制声音(待实现)", Toast.LENGTH_SHORT).show());
|
||||
|
||||
binding.action1.setOnClickListener(v -> TabPlaceholderActivity.start(this, "公园勋章"));
|
||||
binding.action2.setOnClickListener(v -> WatchHistoryActivity.start(this));
|
||||
binding.action3.setOnClickListener(v -> startActivity(new Intent(this, MyFriendsActivity.class)));
|
||||
|
||||
binding.editProfile.setOnClickListener(v -> EditProfileActivity.start(this));
|
||||
binding.shareHome.setOnClickListener(v -> TabPlaceholderActivity.start(this, "分享主页"));
|
||||
binding.addFriendBtn.setOnClickListener(v -> TabPlaceholderActivity.start(this, "加好友"));
|
||||
}
|
||||
|
||||
private interface OnTextSaved {
|
||||
void onSaved(String text);
|
||||
}
|
||||
|
||||
private void showEditDialog(String title, String initialValue, OnTextSaved onSaved) {
|
||||
EditText editText = new EditText(this);
|
||||
editText.setText(initialValue != null ? initialValue : "");
|
||||
editText.setSelection(editText.getText() != null ? editText.getText().length() : 0);
|
||||
int pad = (int) (16 * getResources().getDisplayMetrics().density);
|
||||
editText.setPadding(pad, pad, pad, pad);
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(title)
|
||||
.setView(editText)
|
||||
.setNegativeButton("取消", null)
|
||||
.setPositiveButton("保存", (d, w) -> {
|
||||
String t = editText.getText() != null ? editText.getText().toString().trim() : "";
|
||||
if (onSaved != null) onSaved.onSaved(t);
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
if (binding != null) {
|
||||
loadProfileFromPrefs();
|
||||
binding.bottomNavInclude.bottomNavigation.setSelectedItemId(R.id.nav_profile);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,127 @@
|
|||
package com.example.livestreaming;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
|
||||
|
||||
import com.example.livestreaming.databinding.ActivityTabPlaceholderBinding;
|
||||
import com.example.livestreaming.net.Room;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TabPlaceholderActivity extends AppCompatActivity {
|
||||
|
||||
public static final String EXTRA_TITLE = "extra_title";
|
||||
|
||||
private ActivityTabPlaceholderBinding binding;
|
||||
|
||||
public static void start(Context context, String title) {
|
||||
Intent intent = new Intent(context, TabPlaceholderActivity.class);
|
||||
intent.putExtra(EXTRA_TITLE, title);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
binding = ActivityTabPlaceholderBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
String title = getIntent() != null ? getIntent().getStringExtra(EXTRA_TITLE) : null;
|
||||
if (title == null) title = "";
|
||||
binding.titleText.setText(title);
|
||||
|
||||
binding.backButton.setOnClickListener(v -> finish());
|
||||
|
||||
renderByTitle(title);
|
||||
}
|
||||
|
||||
private void renderByTitle(String title) {
|
||||
if ("关注".equals(title)) {
|
||||
showFollowRooms();
|
||||
return;
|
||||
}
|
||||
if ("附近".equals(title)) {
|
||||
showNearbyUsers();
|
||||
return;
|
||||
}
|
||||
|
||||
binding.genericScroll.setVisibility(View.VISIBLE);
|
||||
binding.followRecyclerView.setVisibility(View.GONE);
|
||||
binding.nearbyRecyclerView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private void showFollowRooms() {
|
||||
binding.genericScroll.setVisibility(View.GONE);
|
||||
binding.followRecyclerView.setVisibility(View.VISIBLE);
|
||||
binding.nearbyRecyclerView.setVisibility(View.GONE);
|
||||
|
||||
RoomsAdapter adapter = new RoomsAdapter(room -> {
|
||||
if (room == null) return;
|
||||
Intent intent = new Intent(TabPlaceholderActivity.this, RoomDetailActivity.class);
|
||||
intent.putExtra(RoomDetailActivity.EXTRA_ROOM_ID, room.getId());
|
||||
startActivity(intent);
|
||||
});
|
||||
|
||||
StaggeredGridLayoutManager glm = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
|
||||
glm.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS);
|
||||
binding.followRecyclerView.setLayoutManager(glm);
|
||||
binding.followRecyclerView.setAdapter(adapter);
|
||||
|
||||
adapter.submitList(buildFollowDemoRooms(16));
|
||||
}
|
||||
|
||||
private void showNearbyUsers() {
|
||||
binding.genericScroll.setVisibility(View.GONE);
|
||||
binding.followRecyclerView.setVisibility(View.GONE);
|
||||
binding.nearbyRecyclerView.setVisibility(View.VISIBLE);
|
||||
|
||||
NearbyUsersAdapter adapter = new NearbyUsersAdapter(user -> {
|
||||
if (user == null) return;
|
||||
Toast.makeText(this, "点击:" + user.getName(), Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
|
||||
StaggeredGridLayoutManager glm = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
|
||||
glm.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS);
|
||||
binding.nearbyRecyclerView.setLayoutManager(glm);
|
||||
binding.nearbyRecyclerView.setAdapter(adapter);
|
||||
|
||||
adapter.submitList(buildNearbyDemoUsers(18));
|
||||
}
|
||||
|
||||
private List<Room> buildFollowDemoRooms(int count) {
|
||||
List<Room> list = new ArrayList<>();
|
||||
for (int i = 0; i < count; i++) {
|
||||
String id = "follow-" + i;
|
||||
String title = "关注主播直播间 " + (i + 1);
|
||||
String streamer = "已关注主播" + (i + 1);
|
||||
boolean live = i % 4 != 0;
|
||||
list.add(new Room(id, title, streamer, live));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private List<NearbyUser> buildNearbyDemoUsers(int count) {
|
||||
List<NearbyUser> list = new ArrayList<>();
|
||||
for (int i = 0; i < count; i++) {
|
||||
String id = "nearby-" + i;
|
||||
String name = "附近主播" + (i + 1);
|
||||
boolean live = i % 3 == 0;
|
||||
String distanceText;
|
||||
if (i < 3) {
|
||||
distanceText = (300 + i * 120) + "m";
|
||||
} else {
|
||||
float km = 0.8f + (i - 3) * 0.35f;
|
||||
distanceText = String.format("%.1fkm", km);
|
||||
}
|
||||
list.add(new NearbyUser(id, name, distanceText, live));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
package com.example.livestreaming;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
|
||||
|
||||
import com.example.livestreaming.databinding.ActivityWatchHistoryBinding;
|
||||
import com.example.livestreaming.net.Room;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class WatchHistoryActivity extends AppCompatActivity {
|
||||
|
||||
private ActivityWatchHistoryBinding binding;
|
||||
|
||||
public static void start(Context context) {
|
||||
Intent intent = new Intent(context, WatchHistoryActivity.class);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
binding = ActivityWatchHistoryBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
binding.backButton.setOnClickListener(v -> finish());
|
||||
|
||||
RoomsAdapter adapter = new RoomsAdapter(room -> {
|
||||
if (room == null) return;
|
||||
Intent intent = new Intent(WatchHistoryActivity.this, RoomDetailActivity.class);
|
||||
intent.putExtra(RoomDetailActivity.EXTRA_ROOM_ID, room.getId());
|
||||
startActivity(intent);
|
||||
});
|
||||
|
||||
StaggeredGridLayoutManager glm = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
|
||||
glm.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS);
|
||||
binding.roomsRecyclerView.setLayoutManager(glm);
|
||||
binding.roomsRecyclerView.setAdapter(adapter);
|
||||
|
||||
adapter.submitList(buildDemoHistory(18));
|
||||
}
|
||||
|
||||
private List<Room> buildDemoHistory(int count) {
|
||||
List<Room> list = new ArrayList<>();
|
||||
for (int i = 0; i < count; i++) {
|
||||
String id = "history-" + i;
|
||||
String title = "看过的直播间 " + (i + 1);
|
||||
String streamer = "主播" + (i + 1);
|
||||
boolean live = i % 5 != 0;
|
||||
list.add(new Room(id, title, streamer, live));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
|
@ -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="@android:color/transparent" />
|
||||
|
||||
</shape>
|
||||
5
android-app/app/src/main/res/drawable/bg_purple_999.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||
<solid android:color="#7A3FF2" />
|
||||
<corners android:radius="999dp" />
|
||||
</shape>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||
<corners android:radius="14dp" />
|
||||
<solid android:color="#F3E8FF" />
|
||||
</shape>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||
<corners android:radius="999dp" />
|
||||
<solid android:color="#E64A3B" />
|
||||
</shape>
|
||||
12
android-app/app/src/main/res/drawable/ic_arrow_back_24.xml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?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="#111111"
|
||||
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8l8,8l1.41,-1.41L7.83,13H20v-2z" />
|
||||
|
||||
</vector>
|
||||
BIN
android-app/app/src/main/res/drawable/ic_globe_clean.png
Normal file
|
After Width: | Height: | Size: 137 KiB |
BIN
android-app/app/src/main/res/drawable/ic_globe_clean_filled.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
android-app/app/src/main/res/drawable/ic_globe_no_bg.png
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
android-app/app/src/main/res/drawable/wish_tree.png
Normal file
|
After Width: | Height: | Size: 258 KiB |
BIN
android-app/app/src/main/res/drawable/wish_tree_black.jpg
Normal file
|
After Width: | Height: | Size: 218 KiB |
|
After Width: | Height: | Size: 284 KiB |
BIN
android-app/app/src/main/res/drawable/wish_tree_prev_no_bg.png
Normal file
|
After Width: | Height: | Size: 382 KiB |
BIN
android-app/app/src/main/res/drawable/wish_tree_trim_backup.png
Normal file
|
After Width: | Height: | Size: 387 KiB |
273
android-app/app/src/main/res/layout/activity_edit_profile.xml
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
<?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="#F6F7FB">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="#F6F7FB">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="12dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/backButton"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:contentDescription="back"
|
||||
android:src="@drawable/ic_arrow_back_24"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/titleText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="编辑资料"
|
||||
android:textColor="#111111"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="@id/backButton"
|
||||
app:layout_constraintEnd_toStartOf="@id/saveButton"
|
||||
app:layout_constraintStart_toEndOf="@id/backButton"
|
||||
app:layout_constraintTop_toTopOf="@id/backButton" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/saveButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="32dp"
|
||||
android:background="@drawable/bg_purple_999"
|
||||
android:gravity="center"
|
||||
android:paddingStart="14dp"
|
||||
android:paddingEnd="14dp"
|
||||
android:text="保存"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/backButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/backButton" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingTop="14dp"
|
||||
android:paddingBottom="24dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/avatarRow"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="72dp"
|
||||
android:background="@drawable/bg_white_16"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/avatarLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="头像"
|
||||
android:textColor="#111111"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_marginTop="14dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/avatarHint"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="6dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="从相册选择或拍照"
|
||||
android:textColor="#666666"
|
||||
android:textSize="13sp"
|
||||
app:layout_constraintEnd_toStartOf="@id/avatarPreview"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/avatarLabel" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/avatarPreview"
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="64dp"
|
||||
android:background="@drawable/bg_avatar_circle"
|
||||
android:padding="10dp"
|
||||
android:src="@drawable/ic_person_24"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/nameLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="昵称"
|
||||
app:boxBackgroundMode="filled"
|
||||
app:boxBackgroundColor="@android:color/white"
|
||||
app:boxCornerRadiusTopStart="14dp"
|
||||
app:boxCornerRadiusTopEnd="14dp"
|
||||
app:boxCornerRadiusBottomStart="14dp"
|
||||
app:boxCornerRadiusBottomEnd="14dp"
|
||||
app:boxStrokeColor="#22000000"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/avatarRow"
|
||||
android:layout_marginTop="12dp">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/inputName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/bioLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:hint="个人签名"
|
||||
app:boxBackgroundMode="filled"
|
||||
app:boxBackgroundColor="@android:color/white"
|
||||
app:boxCornerRadiusTopStart="14dp"
|
||||
app:boxCornerRadiusTopEnd="14dp"
|
||||
app:boxCornerRadiusBottomStart="14dp"
|
||||
app:boxCornerRadiusBottomEnd="14dp"
|
||||
app:boxStrokeColor="#22000000"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/nameLayout">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/inputBio"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minLines="2"
|
||||
android:maxLines="4" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/birthdayLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:hint="生日(例如 2001-08-08)"
|
||||
app:boxBackgroundMode="filled"
|
||||
app:boxBackgroundColor="@android:color/white"
|
||||
app:boxCornerRadiusTopStart="14dp"
|
||||
app:boxCornerRadiusTopEnd="14dp"
|
||||
app:boxCornerRadiusBottomStart="14dp"
|
||||
app:boxCornerRadiusBottomEnd="14dp"
|
||||
app:boxStrokeColor="#22000000"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/bioLayout">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/inputBirthday"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/genderLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:hint="性别(男/女/保密)"
|
||||
app:boxBackgroundMode="filled"
|
||||
app:boxBackgroundColor="@android:color/white"
|
||||
app:boxCornerRadiusTopStart="14dp"
|
||||
app:boxCornerRadiusTopEnd="14dp"
|
||||
app:boxCornerRadiusBottomStart="14dp"
|
||||
app:boxCornerRadiusBottomEnd="14dp"
|
||||
app:boxStrokeColor="#22000000"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/birthdayLayout">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/inputGender"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/locationLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:hint="所在地(例如 北京·朝阳)"
|
||||
app:boxBackgroundMode="filled"
|
||||
app:boxBackgroundColor="@android:color/white"
|
||||
app:boxCornerRadiusTopStart="14dp"
|
||||
app:boxCornerRadiusTopEnd="14dp"
|
||||
app:boxCornerRadiusBottomStart="14dp"
|
||||
app:boxCornerRadiusBottomEnd="14dp"
|
||||
app:boxStrokeColor="#22000000"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/genderLayout">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/inputLocation"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/cancelButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="44dp"
|
||||
android:layout_marginTop="18dp"
|
||||
android:background="@drawable/bg_gray_12"
|
||||
android:gravity="center"
|
||||
android:text="取消"
|
||||
android:textColor="#111111"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/locationLayout" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
60
android-app/app/src/main/res/layout/activity_fans_list.xml
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
<?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="@android:color/white">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/white">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingTop="14dp"
|
||||
android:paddingBottom="14dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/backButton"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:contentDescription="back"
|
||||
android:src="@drawable/ic_arrow_back_24"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/titleText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="粉丝列表"
|
||||
android:textColor="#111111"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="@id/backButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/backButton"
|
||||
app:layout_constraintTop_toTopOf="@id/backButton" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/fansRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingBottom="16dp"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
|
@ -97,13 +97,15 @@
|
|||
|
||||
<ImageView
|
||||
android:id="@+id/centerButton"
|
||||
android:layout_width="92dp"
|
||||
android:layout_height="92dp"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="120dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/bg_fishpond_center_circle"
|
||||
android:background="@android:color/transparent"
|
||||
android:clipToOutline="true"
|
||||
android:elevation="10dp"
|
||||
android:padding="22dp"
|
||||
android:src="@drawable/ic_globe_24" />
|
||||
android:padding="0dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/ic_globe_clean_filled" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="72dp"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
<?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="@android:color/white">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/white">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingTop="14dp"
|
||||
android:paddingBottom="14dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/backButton"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:contentDescription="back"
|
||||
android:src="@drawable/ic_arrow_back_24"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/titleText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="关注列表"
|
||||
android:textColor="#111111"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="@id/backButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/backButton"
|
||||
app:layout_constraintTop_toTopOf="@id/backButton" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/followingRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingBottom="16dp"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
60
android-app/app/src/main/res/layout/activity_likes_list.xml
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
<?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="@android:color/white">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/white">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingTop="14dp"
|
||||
android:paddingBottom="14dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/backButton"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:contentDescription="back"
|
||||
android:src="@drawable/ic_arrow_back_24"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/titleText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="获赞"
|
||||
android:textColor="#111111"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="@id/backButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/backButton"
|
||||
app:layout_constraintTop_toTopOf="@id/backButton" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/likesRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingBottom="16dp"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
|
@ -5,16 +5,44 @@
|
|||
android:layout_height="match_parent"
|
||||
android:background="@android:color/white">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/white">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingTop="14dp"
|
||||
android:paddingBottom="14dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="消息"
|
||||
android:textColor="#111111"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/conversationsRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:clipToPadding="false"
|
||||
android:paddingBottom="88dp"
|
||||
android:text="消息"
|
||||
android:textColor="#111111"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
|
||||
<include
|
||||
android:id="@+id/bottomNavInclude"
|
||||
|
|
|
|||
99
android-app/app/src/main/res/layout/activity_my_friends.xml
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
<?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="@android:color/white">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/white">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingTop="14dp"
|
||||
android:paddingBottom="12dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/backButton"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:contentDescription="back"
|
||||
android:src="@drawable/ic_arrow_back_24"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/titleText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="我的挚友"
|
||||
android:textColor="#111111"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="@id/backButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/backButton"
|
||||
app:layout_constraintTop_toTopOf="@id/backButton" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/searchContainer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:background="@drawable/bg_search"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/backButton">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/searchIcon"
|
||||
android:layout_width="18dp"
|
||||
android:layout_height="18dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:contentDescription="search"
|
||||
android:src="@drawable/ic_search_24"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/searchEdit"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="8dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:hint="搜索挚友"
|
||||
android:inputType="text"
|
||||
android:singleLine="true"
|
||||
android:textColor="#111111"
|
||||
android:textColorHint="#999999"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/searchIcon"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/friendsRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingBottom="16dp"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
242
android-app/app/src/main/res/layout/activity_room_detail_new.xml
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/loading"
|
||||
android:layout_width="wrap_content"
|
||||
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.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/topBar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="12dp"
|
||||
android:paddingVertical="10dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/backButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:insetTop="0dp"
|
||||
android:insetBottom="0dp"
|
||||
android:minHeight="0dp"
|
||||
android:minWidth="0dp"
|
||||
android:text="返回"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/topTitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="直播间"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/topRightGroup"
|
||||
app:layout_constraintStart_toEndOf="@id/backButton"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/topRightGroup"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/topViewerCount"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:text="0"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/fullscreenButton"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/fullscreenButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:insetTop="0dp"
|
||||
android:insetBottom="0dp"
|
||||
android:minHeight="0dp"
|
||||
android:minWidth="0dp"
|
||||
android:text="全屏"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/playerArea"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintDimensionRatio="16:9"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/topBar">
|
||||
|
||||
<androidx.media3.ui.PlayerView
|
||||
android:id="@+id/playerView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/offlineLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="主播暂未开播"
|
||||
android:textColor="#666666" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/liveTag"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="10dp"
|
||||
android:paddingHorizontal="10dp"
|
||||
android:paddingVertical="6dp"
|
||||
android:text="LIVE"
|
||||
android:textColor="@android:color/white"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/roomInfoLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="12dp"
|
||||
android:paddingVertical="10dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/playerArea">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/roomTitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="直播间"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toStartOf="@id/followButton"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/streamerName"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="主播"
|
||||
android:textColor="#666666"
|
||||
app:layout_constraintEnd_toStartOf="@id/followButton"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/roomTitle" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/followButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="关注"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/chatLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/roomInfoLayout">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/chatRecyclerView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:overScrollMode="never"
|
||||
android:paddingHorizontal="12dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/chatInputBar"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/chatInputBar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/chatInput"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="说点什么..."
|
||||
android:imeOptions="actionSend"
|
||||
android:inputType="text"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/sendButton"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/sendButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="发送"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
246
android-app/app/src/main/res/layout/activity_tab_placeholder.xml
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
<?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="@android:color/white">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/white">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingTop="14dp"
|
||||
android:paddingBottom="14dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/backButton"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:contentDescription="back"
|
||||
android:src="@drawable/ic_arrow_back_24"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/titleText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="#111111"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="@id/backButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/backButton"
|
||||
app:layout_constraintTop_toTopOf="@id/backButton" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/contentContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/genericScroll"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="24dp">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:cardCornerRadius="16dp"
|
||||
app:cardElevation="0dp"
|
||||
app:strokeColor="#14000000"
|
||||
app:strokeWidth="1dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="频道概览"
|
||||
android:textColor="#111111"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="6dp"
|
||||
android:lineSpacingExtra="2dp"
|
||||
android:text="这里先放一个通用样式骨架:你后续提出需求时,我会把对应模块替换成真实内容(列表/网格/筛选/推荐流等)。"
|
||||
android:textColor="#666666"
|
||||
android:textSize="13sp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="14dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_weight="1"
|
||||
android:text="筛选"
|
||||
app:cornerRadius="12dp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_weight="1"
|
||||
android:text="排序"
|
||||
app:cornerRadius="12dp"
|
||||
app:strokeWidth="1dp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_weight="1"
|
||||
android:text="更多"
|
||||
app:cornerRadius="12dp"
|
||||
app:strokeWidth="1dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="18dp"
|
||||
android:text="推荐内容"
|
||||
android:textColor="#111111"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
app:cardCornerRadius="16dp"
|
||||
app:cardElevation="0dp"
|
||||
app:strokeColor="#14000000"
|
||||
app:strokeWidth="1dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="14dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="占位卡片 1"
|
||||
android:textColor="#111111"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="6dp"
|
||||
android:text="后续可替换成直播间列表、短视频流、附近的人等。"
|
||||
android:textColor="#666666"
|
||||
android:textSize="13sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
app:cardCornerRadius="16dp"
|
||||
app:cardElevation="0dp"
|
||||
app:strokeColor="#14000000"
|
||||
app:strokeWidth="1dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="14dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="占位卡片 2"
|
||||
android:textColor="#111111"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="6dp"
|
||||
android:text="这里可以放一个横滑专题、榜单、活动入口等。"
|
||||
android:textColor="#666666"
|
||||
android:textSize="13sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/followRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="24dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/nearbyRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="24dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
<?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="@android:color/white">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/white">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingTop="14dp"
|
||||
android:paddingBottom="14dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/backButton"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:contentDescription="back"
|
||||
android:src="@drawable/ic_arrow_back_24"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/titleText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="观看历史"
|
||||
android:textColor="#111111"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="@id/backButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/backButton"
|
||||
app:layout_constraintTop_toTopOf="@id/backButton" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/roomsRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="16dp"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
|
@ -5,16 +5,124 @@
|
|||
android:layout_height="match_parent"
|
||||
android:background="@android:color/white">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:paddingBottom="88dp"
|
||||
android:text="许愿树"
|
||||
android:textColor="#111111"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
android:background="#FFF7F7"
|
||||
android:paddingBottom="88dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/topBar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingTop="14dp"
|
||||
android:paddingBottom="10dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="WishTree"
|
||||
android:textColor="#B28AE6"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/searchButton"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/searchButton"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:contentDescription="Search"
|
||||
android:padding="8dp"
|
||||
android:src="@drawable/ic_search_24"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/notifyButton"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/notifyButton"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:contentDescription="Notifications"
|
||||
android:padding="8dp"
|
||||
android:src="@drawable/ic_notifications_24"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/banner"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:background="@drawable/bg_wish_tree_banner"
|
||||
android:paddingHorizontal="14dp"
|
||||
android:paddingVertical="10dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/topBar">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bannerText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="元旦许愿树 | 任意充值即翻倍"
|
||||
android:textColor="#6E2DB7"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<!--
|
||||
<TextView
|
||||
android:id="@+id/bannerTimer"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_wish_tree_timer"
|
||||
android:paddingHorizontal="10dp"
|
||||
android:paddingVertical="6dp"
|
||||
android:text="12:30 05:00"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
-->
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/wishTreeImage"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginStart="0dp"
|
||||
android:layout_marginEnd="0dp"
|
||||
android:adjustViewBounds="true"
|
||||
android:contentDescription="Wish Tree"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/wish_tree"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/banner" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<include
|
||||
android:id="@+id/bottomNavInclude"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="12dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/actionPickGallery"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="52dp"
|
||||
android:gravity="center"
|
||||
android:text="从相册选择"
|
||||
android:textColor="#111111"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="#11000000" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/actionTakePhoto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="52dp"
|
||||
android:gravity="center"
|
||||
android:text="拍照"
|
||||
android:textColor="#111111"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="8dp"
|
||||
android:background="@android:color/transparent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/actionCancel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="52dp"
|
||||
android:gravity="center"
|
||||
android:text="取消"
|
||||
android:textColor="#666666"
|
||||
android:textSize="16sp" />
|
||||
|
||||
</LinearLayout>
|
||||
67
android-app/app/src/main/res/layout/dialog_stream_info.xml
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/addressLabel"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="推流地址"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/addressText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="6dp"
|
||||
android:textColor="#666666"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/addressLabel" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/copyAddressBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="复制地址"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/addressText" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/keyLabel"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="推流密钥"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/copyAddressBtn" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/keyText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="6dp"
|
||||
android:textColor="#666666"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/keyLabel" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/copyKeyBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="复制密钥"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/keyText" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/messageText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingVertical="4dp"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="14sp" />
|
||||
97
android-app/app/src/main/res/layout/item_conversation.xml
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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="72dp"
|
||||
android:background="@android:color/white"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/avatar"
|
||||
android:layout_width="44dp"
|
||||
android:layout_height="44dp"
|
||||
android:background="@drawable/bg_avatar_circle"
|
||||
android:padding="6dp"
|
||||
android:src="@drawable/ic_account_circle_24"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="会话标题"
|
||||
android:textColor="#111111"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toStartOf="@id/timeText"
|
||||
app:layout_constraintStart_toEndOf="@id/avatar"
|
||||
app:layout_constraintTop_toTopOf="@id/avatar"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="10dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/lastMessage"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="最后一条消息内容预览"
|
||||
android:textColor="#666666"
|
||||
android:textSize="13sp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/avatar"
|
||||
app:layout_constraintEnd_toStartOf="@id/unreadBadge"
|
||||
app:layout_constraintStart_toStartOf="@id/title"
|
||||
android:layout_marginEnd="10dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/timeText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="12:30"
|
||||
android:textColor="#999999"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/title" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/muteIcon"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:contentDescription="mute"
|
||||
android:src="@android:drawable/ic_lock_silent_mode"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/lastMessage"
|
||||
app:layout_constraintEnd_toStartOf="@id/unreadBadge" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/unreadBadge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="18dp"
|
||||
android:background="@drawable/live_badge_background"
|
||||
android:gravity="center"
|
||||
android:minWidth="18dp"
|
||||
android:paddingStart="6dp"
|
||||
android:paddingEnd="6dp"
|
||||
android:text="1"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="11sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/lastMessage"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<View
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:background="#0F000000"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/avatar" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
71
android-app/app/src/main/res/layout/item_friend.xml
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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="72dp"
|
||||
android:background="@android:color/white"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/avatar"
|
||||
android:layout_width="44dp"
|
||||
android:layout_height="44dp"
|
||||
android:background="@drawable/bg_avatar_circle"
|
||||
android:padding="6dp"
|
||||
android:src="@drawable/ic_account_circle_24"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<View
|
||||
android:id="@+id/onlineDot"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
android:background="@drawable/live_badge_background"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/avatar"
|
||||
app:layout_constraintEnd_toEndOf="@id/avatar" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="挚友昵称"
|
||||
android:textColor="#111111"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/avatar"
|
||||
app:layout_constraintTop_toTopOf="@id/avatar" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="最近在线 / 共同关注 / 备注"
|
||||
android:textColor="#666666"
|
||||
android:textSize="13sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/avatar"
|
||||
app:layout_constraintTop_toBottomOf="@id/name" />
|
||||
|
||||
<View
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:background="#0F000000"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/avatar" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
78
android-app/app/src/main/res/layout/item_nearby_user.xml
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="6dp"
|
||||
app:cardCornerRadius="14dp"
|
||||
app:cardElevation="0dp"
|
||||
app:strokeColor="#14000000"
|
||||
app:strokeWidth="1dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/avatarImage"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="#0A000000"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/ic_account_circle_24"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
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:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/live_badge_background"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingTop="3dp"
|
||||
android:paddingBottom="3dp"
|
||||
android:text="LIVE"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="10sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintStart_toStartOf="@id/avatarImage"
|
||||
app:layout_constraintTop_toTopOf="@id/avatarImage" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/userName"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="附近主播"
|
||||
android:textColor="#111111"
|
||||
android:textSize="13sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/avatarImage" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/distanceText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="1.2km"
|
||||
android:textColor="#666666"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/userName" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
4
android-app/app/src/main/res/xml/file_paths.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<cache-path name="cache_images" path="images/" />
|
||||
</paths>
|
||||
BIN
android-app/img/无背景地球图标.png
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
android-app/img/有背景图的许愿树.png
Normal file
|
After Width: | Height: | Size: 258 KiB |
BIN
android-app/img/许愿树.png
Normal file
|
After Width: | Height: | Size: 284 KiB |
62
android-app/scripts/remove_checkerboard.ps1
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
param(
|
||||
[Parameter(Mandatory=$true)][string]$InputPath,
|
||||
[Parameter(Mandatory=$true)][string]$OutputPath,
|
||||
[string]$BackupPath = ""
|
||||
)
|
||||
|
||||
if (!(Test-Path -LiteralPath $InputPath)) {
|
||||
throw "Input file not found: $InputPath"
|
||||
}
|
||||
|
||||
if ($BackupPath -ne "") {
|
||||
Copy-Item -LiteralPath $InputPath -Destination $BackupPath -Force
|
||||
}
|
||||
|
||||
Add-Type -AssemblyName System.Drawing
|
||||
|
||||
# Load from a stream to avoid locking the input file (required if output overwrites input)
|
||||
$fs = [System.IO.File]::Open($InputPath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
|
||||
try {
|
||||
$bmp = New-Object System.Drawing.Bitmap($fs)
|
||||
} finally {
|
||||
$fs.Dispose()
|
||||
}
|
||||
|
||||
$out = New-Object System.Drawing.Bitmap($bmp.Width, $bmp.Height, [System.Drawing.Imaging.PixelFormat]::Format32bppArgb)
|
||||
|
||||
function IsBgPixel($c) {
|
||||
$r = [int]$c.R
|
||||
$g = [int]$c.G
|
||||
$b = [int]$c.B
|
||||
|
||||
$nearGray = ([math]::Abs($r - $g) -le 10) -and ([math]::Abs($g - $b) -le 10)
|
||||
if (-not $nearGray) { return $false }
|
||||
|
||||
# Typical checkerboard: very light gray and mid light gray
|
||||
$isLight = ($r -ge 235 -and $g -ge 235 -and $b -ge 235)
|
||||
$isMid = ($r -ge 205 -and $g -ge 205 -and $b -ge 205 -and $r -le 235)
|
||||
return ($isLight -or $isMid)
|
||||
}
|
||||
|
||||
for ($y = 0; $y -lt $bmp.Height; $y++) {
|
||||
for ($x = 0; $x -lt $bmp.Width; $x++) {
|
||||
$c = $bmp.GetPixel($x, $y)
|
||||
|
||||
if (IsBgPixel $c) {
|
||||
$out.SetPixel($x, $y, [System.Drawing.Color]::FromArgb(0, $c.R, $c.G, $c.B))
|
||||
} else {
|
||||
$out.SetPixel($x, $y, [System.Drawing.Color]::FromArgb(255, $c.R, $c.G, $c.B))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$tmp = "$OutputPath.tmp"
|
||||
|
||||
try {
|
||||
$out.Save($tmp, [System.Drawing.Imaging.ImageFormat]::Png)
|
||||
} finally {
|
||||
$bmp.Dispose()
|
||||
$out.Dispose()
|
||||
}
|
||||
|
||||
Move-Item -LiteralPath $tmp -Destination $OutputPath -Force
|
||||
70
android-app/scripts/trim_transparent.ps1
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
param(
|
||||
[Parameter(Mandatory=$true)][string]$InputPath,
|
||||
[Parameter(Mandatory=$true)][string]$OutputPath,
|
||||
[string]$BackupPath = "",
|
||||
[int]$AlphaThreshold = 1,
|
||||
[int]$Padding = 0
|
||||
)
|
||||
|
||||
if (!(Test-Path -LiteralPath $InputPath)) {
|
||||
throw "Input file not found: $InputPath"
|
||||
}
|
||||
|
||||
if ($BackupPath -ne "") {
|
||||
Copy-Item -LiteralPath $InputPath -Destination $BackupPath -Force
|
||||
}
|
||||
|
||||
Add-Type -AssemblyName System.Drawing
|
||||
|
||||
# Load from stream to avoid locking
|
||||
$fs = [System.IO.File]::Open($InputPath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
|
||||
try {
|
||||
$bmp = New-Object System.Drawing.Bitmap($fs)
|
||||
} finally {
|
||||
$fs.Dispose()
|
||||
}
|
||||
|
||||
$minX = $bmp.Width
|
||||
$minY = $bmp.Height
|
||||
$maxX = -1
|
||||
$maxY = -1
|
||||
|
||||
for ($y = 0; $y -lt $bmp.Height; $y++) {
|
||||
for ($x = 0; $x -lt $bmp.Width; $x++) {
|
||||
$c = $bmp.GetPixel($x, $y)
|
||||
if ($c.A -ge $AlphaThreshold) {
|
||||
if ($x -lt $minX) { $minX = $x }
|
||||
if ($y -lt $minY) { $minY = $y }
|
||||
if ($x -gt $maxX) { $maxX = $x }
|
||||
if ($y -gt $maxY) { $maxY = $y }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($maxX -lt 0 -or $maxY -lt 0) {
|
||||
# No non-transparent pixels, just copy input
|
||||
$bmp.Dispose()
|
||||
Copy-Item -LiteralPath $InputPath -Destination $OutputPath -Force
|
||||
exit 0
|
||||
}
|
||||
|
||||
$minX = [math]::Max(0, $minX - $Padding)
|
||||
$minY = [math]::Max(0, $minY - $Padding)
|
||||
$maxX = [math]::Min($bmp.Width - 1, $maxX + $Padding)
|
||||
$maxY = [math]::Min($bmp.Height - 1, $maxY + $Padding)
|
||||
|
||||
$newW = $maxX - $minX + 1
|
||||
$newH = $maxY - $minY + 1
|
||||
|
||||
$rect = New-Object System.Drawing.Rectangle($minX, $minY, $newW, $newH)
|
||||
$cropped = $bmp.Clone($rect, [System.Drawing.Imaging.PixelFormat]::Format32bppArgb)
|
||||
|
||||
$tmp = "$OutputPath.tmp"
|
||||
try {
|
||||
$cropped.Save($tmp, [System.Drawing.Imaging.ImageFormat]::Png)
|
||||
} finally {
|
||||
$cropped.Dispose()
|
||||
$bmp.Dispose()
|
||||
}
|
||||
|
||||
Move-Item -LiteralPath $tmp -Destination $OutputPath -Force
|
||||