修改了界面中各个细节中的问题

This commit is contained in:
ShiQi 2025-12-22 16:31:46 +08:00
parent ffb175ff78
commit a6b56bceb1
49 changed files with 4017 additions and 264 deletions

View File

@ -2,6 +2,7 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<application <application
android:allowBackup="true" android:allowBackup="true"
@ -64,6 +65,38 @@
android:name="com.example.livestreaming.FishPondActivity" android:name="com.example.livestreaming.FishPondActivity"
android:exported="false" /> android:exported="false" />
<activity
android:name="com.example.livestreaming.VoiceMatchActivity"
android:exported="false" />
<activity
android:name="com.example.livestreaming.HeartbeatSignalActivity"
android:exported="false" />
<activity
android:name="com.example.livestreaming.OnlineDatingActivity"
android:exported="false" />
<activity
android:name="com.example.livestreaming.FindGameActivity"
android:exported="false" />
<activity
android:name="com.example.livestreaming.KTVTogetherActivity"
android:exported="false" />
<activity
android:name="com.example.livestreaming.DrawGuessActivity"
android:exported="false" />
<activity
android:name="com.example.livestreaming.PeaceEliteActivity"
android:exported="false" />
<activity
android:name="com.example.livestreaming.TableGamesActivity"
android:exported="false" />
<activity <activity
android:name="com.example.livestreaming.ProfileActivity" android:name="com.example.livestreaming.ProfileActivity"
android:exported="false" /> android:exported="false" />

View File

@ -0,0 +1,657 @@
{
"provinces": [
{
"name": "北京",
"cities": [
{"name": "东城区"},
{"name": "西城区"},
{"name": "朝阳区"},
{"name": "丰台区"},
{"name": "石景山区"},
{"name": "海淀区"},
{"name": "门头沟区"},
{"name": "房山区"},
{"name": "通州区"},
{"name": "顺义区"},
{"name": "昌平区"},
{"name": "大兴区"},
{"name": "怀柔区"},
{"name": "平谷区"},
{"name": "密云区"},
{"name": "延庆区"}
]
},
{
"name": "上海",
"cities": [
{"name": "黄浦区"},
{"name": "徐汇区"},
{"name": "长宁区"},
{"name": "静安区"},
{"name": "普陀区"},
{"name": "虹口区"},
{"name": "杨浦区"},
{"name": "闵行区"},
{"name": "宝山区"},
{"name": "嘉定区"},
{"name": "浦东新区"},
{"name": "金山区"},
{"name": "松江区"},
{"name": "青浦区"},
{"name": "奉贤区"},
{"name": "崇明区"}
]
},
{
"name": "天津",
"cities": [
{"name": "和平区"},
{"name": "河东区"},
{"name": "河西区"},
{"name": "南开区"},
{"name": "河北区"},
{"name": "红桥区"},
{"name": "东丽区"},
{"name": "西青区"},
{"name": "津南区"},
{"name": "北辰区"},
{"name": "武清区"},
{"name": "宝坻区"},
{"name": "滨海新区"},
{"name": "宁河区"},
{"name": "静海区"},
{"name": "蓟州区"}
]
},
{
"name": "重庆",
"cities": [
{"name": "万州区"},
{"name": "涪陵区"},
{"name": "渝中区"},
{"name": "大渡口区"},
{"name": "江北区"},
{"name": "沙坪坝区"},
{"name": "九龙坡区"},
{"name": "南岸区"},
{"name": "北碚区"},
{"name": "綦江区"},
{"name": "大足区"},
{"name": "渝北区"},
{"name": "巴南区"},
{"name": "黔江区"},
{"name": "长寿区"},
{"name": "江津区"},
{"name": "合川区"},
{"name": "永川区"},
{"name": "南川区"},
{"name": "璧山区"},
{"name": "铜梁区"},
{"name": "潼南区"},
{"name": "荣昌区"},
{"name": "开州区"},
{"name": "梁平区"},
{"name": "武隆区"}
]
},
{
"name": "河北",
"cities": [
{"name": "石家庄"},
{"name": "唐山"},
{"name": "秦皇岛"},
{"name": "邯郸"},
{"name": "邢台"},
{"name": "保定"},
{"name": "张家口"},
{"name": "承德"},
{"name": "沧州"},
{"name": "廊坊"},
{"name": "衡水"}
]
},
{
"name": "山西",
"cities": [
{"name": "太原"},
{"name": "大同"},
{"name": "阳泉"},
{"name": "长治"},
{"name": "晋城"},
{"name": "朔州"},
{"name": "晋中"},
{"name": "运城"},
{"name": "忻州"},
{"name": "临汾"},
{"name": "吕梁"}
]
},
{
"name": "内蒙古",
"cities": [
{"name": "呼和浩特"},
{"name": "包头"},
{"name": "乌海"},
{"name": "赤峰"},
{"name": "通辽"},
{"name": "鄂尔多斯"},
{"name": "呼伦贝尔"},
{"name": "巴彦淖尔"},
{"name": "乌兰察布"},
{"name": "兴安盟"},
{"name": "锡林郭勒盟"},
{"name": "阿拉善盟"}
]
},
{
"name": "辽宁",
"cities": [
{"name": "沈阳"},
{"name": "大连"},
{"name": "鞍山"},
{"name": "抚顺"},
{"name": "本溪"},
{"name": "丹东"},
{"name": "锦州"},
{"name": "营口"},
{"name": "阜新"},
{"name": "辽阳"},
{"name": "盘锦"},
{"name": "铁岭"},
{"name": "朝阳"},
{"name": "葫芦岛"}
]
},
{
"name": "吉林",
"cities": [
{"name": "长春"},
{"name": "吉林"},
{"name": "四平"},
{"name": "辽源"},
{"name": "通化"},
{"name": "白山"},
{"name": "松原"},
{"name": "白城"},
{"name": "延边朝鲜族自治州"}
]
},
{
"name": "黑龙江",
"cities": [
{"name": "哈尔滨"},
{"name": "齐齐哈尔"},
{"name": "鸡西"},
{"name": "鹤岗"},
{"name": "双鸭山"},
{"name": "大庆"},
{"name": "伊春"},
{"name": "佳木斯"},
{"name": "七台河"},
{"name": "牡丹江"},
{"name": "黑河"},
{"name": "绥化"},
{"name": "大兴安岭"}
]
},
{
"name": "江苏",
"cities": [
{"name": "南京"},
{"name": "无锡"},
{"name": "徐州"},
{"name": "常州"},
{"name": "苏州"},
{"name": "南通"},
{"name": "连云港"},
{"name": "淮安"},
{"name": "盐城"},
{"name": "扬州"},
{"name": "镇江"},
{"name": "泰州"},
{"name": "宿迁"}
]
},
{
"name": "浙江",
"cities": [
{"name": "杭州"},
{"name": "宁波"},
{"name": "温州"},
{"name": "嘉兴"},
{"name": "湖州"},
{"name": "绍兴"},
{"name": "金华"},
{"name": "衢州"},
{"name": "舟山"},
{"name": "台州"},
{"name": "丽水"}
]
},
{
"name": "安徽",
"cities": [
{"name": "合肥"},
{"name": "芜湖"},
{"name": "蚌埠"},
{"name": "淮南"},
{"name": "马鞍山"},
{"name": "淮北"},
{"name": "铜陵"},
{"name": "安庆"},
{"name": "黄山"},
{"name": "滁州"},
{"name": "阜阳"},
{"name": "宿州"},
{"name": "六安"},
{"name": "亳州"},
{"name": "池州"},
{"name": "宣城"}
]
},
{
"name": "福建",
"cities": [
{"name": "福州"},
{"name": "厦门"},
{"name": "莆田"},
{"name": "三明"},
{"name": "泉州"},
{"name": "漳州"},
{"name": "南平"},
{"name": "龙岩"},
{"name": "宁德"}
]
},
{
"name": "江西",
"cities": [
{"name": "南昌"},
{"name": "景德镇"},
{"name": "萍乡"},
{"name": "九江"},
{"name": "新余"},
{"name": "鹰潭"},
{"name": "赣州"},
{"name": "吉安"},
{"name": "宜春"},
{"name": "抚州"},
{"name": "上饶"}
]
},
{
"name": "山东",
"cities": [
{"name": "济南"},
{"name": "青岛"},
{"name": "淄博"},
{"name": "枣庄"},
{"name": "东营"},
{"name": "烟台"},
{"name": "潍坊"},
{"name": "济宁"},
{"name": "泰安"},
{"name": "威海"},
{"name": "日照"},
{"name": "临沂"},
{"name": "德州"},
{"name": "聊城"},
{"name": "滨州"},
{"name": "菏泽"}
]
},
{
"name": "河南",
"cities": [
{"name": "郑州"},
{"name": "开封"},
{"name": "洛阳"},
{"name": "平顶山"},
{"name": "安阳"},
{"name": "鹤壁"},
{"name": "新乡"},
{"name": "焦作"},
{"name": "濮阳"},
{"name": "许昌"},
{"name": "漯河"},
{"name": "三门峡"},
{"name": "南阳"},
{"name": "商丘"},
{"name": "信阳"},
{"name": "周口"},
{"name": "驻马店"},
{"name": "济源"}
]
},
{
"name": "湖北",
"cities": [
{"name": "武汉"},
{"name": "黄石"},
{"name": "十堰"},
{"name": "宜昌"},
{"name": "襄阳"},
{"name": "鄂州"},
{"name": "荆门"},
{"name": "孝感"},
{"name": "荆州"},
{"name": "黄冈"},
{"name": "咸宁"},
{"name": "随州"},
{"name": "恩施"},
{"name": "仙桃"},
{"name": "潜江"},
{"name": "天门"},
{"name": "神农架"}
]
},
{
"name": "湖南",
"cities": [
{"name": "长沙"},
{"name": "株洲"},
{"name": "湘潭"},
{"name": "衡阳"},
{"name": "邵阳"},
{"name": "岳阳"},
{"name": "常德"},
{"name": "张家界"},
{"name": "益阳"},
{"name": "郴州"},
{"name": "永州"},
{"name": "怀化"},
{"name": "娄底"},
{"name": "湘西"}
]
},
{
"name": "广东",
"cities": [
{"name": "广州"},
{"name": "深圳"},
{"name": "珠海"},
{"name": "汕头"},
{"name": "佛山"},
{"name": "韶关"},
{"name": "湛江"},
{"name": "肇庆"},
{"name": "江门"},
{"name": "茂名"},
{"name": "惠州"},
{"name": "梅州"},
{"name": "汕尾"},
{"name": "河源"},
{"name": "阳江"},
{"name": "清远"},
{"name": "东莞"},
{"name": "中山"},
{"name": "潮州"},
{"name": "揭阳"},
{"name": "云浮"}
]
},
{
"name": "广西",
"cities": [
{"name": "南宁"},
{"name": "柳州"},
{"name": "桂林"},
{"name": "梧州"},
{"name": "北海"},
{"name": "防城港"},
{"name": "钦州"},
{"name": "贵港"},
{"name": "玉林"},
{"name": "百色"},
{"name": "贺州"},
{"name": "河池"},
{"name": "来宾"},
{"name": "崇左"}
]
},
{
"name": "海南",
"cities": [
{"name": "海口"},
{"name": "三亚"},
{"name": "三沙"},
{"name": "儋州"},
{"name": "五指山"},
{"name": "琼海"},
{"name": "文昌"},
{"name": "万宁"},
{"name": "东方"},
{"name": "定安"},
{"name": "屯昌"},
{"name": "澄迈"},
{"name": "临高"},
{"name": "白沙"},
{"name": "昌江"},
{"name": "乐东"},
{"name": "陵水"},
{"name": "保亭"},
{"name": "琼中"}
]
},
{
"name": "四川",
"cities": [
{"name": "成都"},
{"name": "自贡"},
{"name": "攀枝花"},
{"name": "泸州"},
{"name": "德阳"},
{"name": "绵阳"},
{"name": "广元"},
{"name": "遂宁"},
{"name": "内江"},
{"name": "乐山"},
{"name": "南充"},
{"name": "眉山"},
{"name": "宜宾"},
{"name": "广安"},
{"name": "达州"},
{"name": "雅安"},
{"name": "巴中"},
{"name": "资阳"},
{"name": "阿坝"},
{"name": "甘孜"},
{"name": "凉山"}
]
},
{
"name": "贵州",
"cities": [
{"name": "贵阳"},
{"name": "六盘水"},
{"name": "遵义"},
{"name": "安顺"},
{"name": "毕节"},
{"name": "铜仁"},
{"name": "黔西南"},
{"name": "黔东南"},
{"name": "黔南"}
]
},
{
"name": "云南",
"cities": [
{"name": "昆明"},
{"name": "曲靖"},
{"name": "玉溪"},
{"name": "保山"},
{"name": "昭通"},
{"name": "丽江"},
{"name": "普洱"},
{"name": "临沧"},
{"name": "楚雄"},
{"name": "红河"},
{"name": "文山"},
{"name": "西双版纳"},
{"name": "大理"},
{"name": "德宏"},
{"name": "怒江"},
{"name": "迪庆"}
]
},
{
"name": "西藏",
"cities": [
{"name": "拉萨"},
{"name": "日喀则"},
{"name": "昌都"},
{"name": "林芝"},
{"name": "山南"},
{"name": "那曲"},
{"name": "阿里"}
]
},
{
"name": "陕西",
"cities": [
{"name": "西安"},
{"name": "铜川"},
{"name": "宝鸡"},
{"name": "咸阳"},
{"name": "渭南"},
{"name": "延安"},
{"name": "汉中"},
{"name": "榆林"},
{"name": "安康"},
{"name": "商洛"}
]
},
{
"name": "甘肃",
"cities": [
{"name": "兰州"},
{"name": "嘉峪关"},
{"name": "金昌"},
{"name": "白银"},
{"name": "天水"},
{"name": "武威"},
{"name": "张掖"},
{"name": "平凉"},
{"name": "酒泉"},
{"name": "庆阳"},
{"name": "定西"},
{"name": "陇南"},
{"name": "临夏"},
{"name": "甘南"}
]
},
{
"name": "青海",
"cities": [
{"name": "西宁"},
{"name": "海东"},
{"name": "海北"},
{"name": "黄南"},
{"name": "海南"},
{"name": "果洛"},
{"name": "玉树"},
{"name": "海西"}
]
},
{
"name": "宁夏",
"cities": [
{"name": "银川"},
{"name": "石嘴山"},
{"name": "吴忠"},
{"name": "固原"},
{"name": "中卫"}
]
},
{
"name": "新疆",
"cities": [
{"name": "乌鲁木齐"},
{"name": "克拉玛依"},
{"name": "吐鲁番"},
{"name": "哈密"},
{"name": "昌吉"},
{"name": "博尔塔拉"},
{"name": "巴音郭楞"},
{"name": "阿克苏"},
{"name": "克孜勒苏"},
{"name": "喀什"},
{"name": "和田"},
{"name": "伊犁"},
{"name": "塔城"},
{"name": "阿勒泰"},
{"name": "石河子"},
{"name": "阿拉尔"},
{"name": "图木舒克"},
{"name": "五家渠"},
{"name": "北屯"},
{"name": "铁门关"},
{"name": "双河"},
{"name": "可克达拉"},
{"name": "昆玉"},
{"name": "胡杨河"}
]
},
{
"name": "台湾",
"cities": [
{"name": "台北"},
{"name": "新北"},
{"name": "桃园"},
{"name": "台中"},
{"name": "台南"},
{"name": "高雄"},
{"name": "基隆"},
{"name": "新竹"},
{"name": "嘉义"},
{"name": "宜兰"},
{"name": "苗栗"},
{"name": "彰化"},
{"name": "南投"},
{"name": "云林"},
{"name": "屏东"},
{"name": "台东"},
{"name": "花莲"},
{"name": "澎湖"},
{"name": "金门"},
{"name": "连江"}
]
},
{
"name": "香港",
"cities": [
{"name": "中西区"},
{"name": "湾仔区"},
{"name": "东区"},
{"name": "南区"},
{"name": "深水埗区"},
{"name": "油尖旺区"},
{"name": "九龙城区"},
{"name": "黄大仙区"},
{"name": "观塘区"},
{"name": "荃湾区"},
{"name": "屯门区"},
{"name": "元朗区"},
{"name": "北区"},
{"name": "大埔区"},
{"name": "沙田区"},
{"name": "西贡区"},
{"name": "葵青区"},
{"name": "离岛区"}
]
},
{
"name": "澳门",
"cities": [
{"name": "花地玛堂区"},
{"name": "圣安多尼堂区"},
{"name": "望德堂区"},
{"name": "大堂区"},
{"name": "风顺堂区"},
{"name": "嘉模堂区"},
{"name": "路凼填海区"},
{"name": "圣方济各堂区"}
]
}
]
}

View File

@ -0,0 +1,162 @@
package com.example.livestreaming;
import android.app.Dialog;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
public class AvatarViewerDialog extends Dialog {
private ImageView avatarImageView;
private Uri avatarUri;
private Integer avatarResId;
private Drawable avatarDrawable;
private Context activityContext;
public AvatarViewerDialog(@NonNull Context context) {
super(context, android.R.style.Theme_Black_NoTitleBar_Fullscreen);
this.activityContext = context;
}
public static AvatarViewerDialog create(Context context) {
return new AvatarViewerDialog(context);
}
public AvatarViewerDialog setAvatarUri(Uri uri) {
this.avatarUri = uri;
this.avatarResId = null;
this.avatarDrawable = null;
return this;
}
public AvatarViewerDialog setAvatarResId(int resId) {
this.avatarResId = resId;
this.avatarUri = null;
this.avatarDrawable = null;
return this;
}
public AvatarViewerDialog setAvatarDrawable(Drawable drawable) {
this.avatarDrawable = drawable;
this.avatarUri = null;
this.avatarResId = null;
return this;
}
public AvatarViewerDialog setAvatarFromImageView(ImageView imageView) {
if (imageView == null) return this;
// 优先从ImageView的Drawable获取
Drawable drawable = imageView.getDrawable();
if (drawable != null) {
return setAvatarDrawable(drawable);
}
// 如果Drawable为空尝试从Tag获取URI或资源ID
Object tag = imageView.getTag();
if (tag instanceof Uri) {
return setAvatarUri((Uri) tag);
} else if (tag instanceof Integer) {
return setAvatarResId((Integer) tag);
}
return this;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.dialog_avatar_viewer);
Window window = getWindow();
if (window != null) {
WindowManager.LayoutParams params = window.getAttributes();
params.width = WindowManager.LayoutParams.MATCH_PARENT;
params.height = WindowManager.LayoutParams.MATCH_PARENT;
window.setAttributes(params);
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
window.setBackgroundDrawableResource(android.R.color.black);
}
avatarImageView = findViewById(R.id.avatarImageView);
if (avatarImageView == null) {
dismiss();
return;
}
// 点击任意位置关闭
View rootLayout = findViewById(R.id.rootLayout);
if (rootLayout != null) {
rootLayout.setOnClickListener(v -> dismiss());
}
// 也允许点击ImageView关闭
avatarImageView.setOnClickListener(v -> dismiss());
loadAvatar();
}
private void loadAvatar() {
if (avatarImageView == null) return;
// 使用Activity的Context而不是Dialog的Context确保Glide能正常工作
Context context = activityContext;
if (context == null) {
context = getContext();
}
if (context == null) {
avatarImageView.setImageResource(R.drawable.ic_account_circle_24);
return;
}
try {
if (avatarUri != null) {
Glide.with(context)
.load(avatarUri)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.placeholder(R.drawable.ic_account_circle_24)
.error(R.drawable.ic_account_circle_24)
.into(avatarImageView);
} else if (avatarResId != null && avatarResId != 0) {
Glide.with(context)
.load(avatarResId)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.placeholder(R.drawable.ic_account_circle_24)
.error(R.drawable.ic_account_circle_24)
.into(avatarImageView);
} else if (avatarDrawable != null) {
// 直接设置Drawable但如果是Glide的Drawable可能需要特殊处理
try {
avatarImageView.setImageDrawable(avatarDrawable);
} catch (Exception e) {
// 如果设置Drawable失败尝试使用Glide加载
// 对于Glide加载的Drawable我们需要重新加载原始资源
avatarImageView.setImageResource(R.drawable.ic_account_circle_24);
}
} else {
avatarImageView.setImageResource(R.drawable.ic_account_circle_24);
}
} catch (Exception e) {
// 如果Glide加载失败直接使用setImageResource
try {
avatarImageView.setImageResource(R.drawable.ic_account_circle_24);
} catch (Exception ex) {
// 最后的fallback
}
}
}
}

View File

@ -33,6 +33,10 @@ public class ConversationActivity extends AppCompatActivity {
context.startActivity(intent); context.startActivity(intent);
} }
private static final String EXTRA_UNREAD_COUNT = "extra_unread_count";
private int initialUnreadCount = 0;
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -42,10 +46,24 @@ public class ConversationActivity extends AppCompatActivity {
String title = getIntent() != null ? getIntent().getStringExtra(EXTRA_CONVERSATION_TITLE) : null; String title = getIntent() != null ? getIntent().getStringExtra(EXTRA_CONVERSATION_TITLE) : null;
binding.titleText.setText(title != null ? title : "会话"); binding.titleText.setText(title != null ? title : "会话");
// 获取该会话的初始未读数量
initialUnreadCount = getIntent() != null ? getIntent().getIntExtra(EXTRA_UNREAD_COUNT, 0) : 0;
binding.backButton.setOnClickListener(v -> finish()); binding.backButton.setOnClickListener(v -> finish());
setupMessages(); setupMessages();
setupInput(); setupInput();
// 用户进入会话时标记该会话的消息为已读减少未读数量
if (initialUnreadCount > 0) {
UnreadMessageManager.decrementUnreadCount(this, initialUnreadCount);
}
}
@Override
protected void onPause() {
super.onPause();
// 当用户离开会话时更新总未读数量如果会话未读数量已减少
} }
private void setupMessages() { private void setupMessages() {

View File

@ -1,8 +1,13 @@
package com.example.livestreaming; package com.example.livestreaming;
import android.graphics.Outline;
import android.net.Uri;
import android.text.TextUtils;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -10,6 +15,8 @@ import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter; import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.Locale; import java.util.Locale;
@ -56,15 +63,35 @@ public class ConversationMessagesAdapter extends ListAdapter<ChatMessage, Recycl
} }
static class IncomingVH extends RecyclerView.ViewHolder { static class IncomingVH extends RecyclerView.ViewHolder {
private final ImageView avatarView;
private final TextView nameText; private final TextView nameText;
private final TextView msgText; private final TextView msgText;
private final TextView timeText; private final TextView timeText;
IncomingVH(@NonNull View itemView) { IncomingVH(@NonNull View itemView) {
super(itemView); super(itemView);
avatarView = itemView.findViewById(R.id.avatarView);
nameText = itemView.findViewById(R.id.nameText); nameText = itemView.findViewById(R.id.nameText);
msgText = itemView.findViewById(R.id.messageText); msgText = itemView.findViewById(R.id.messageText);
timeText = itemView.findViewById(R.id.timeText); timeText = itemView.findViewById(R.id.timeText);
setupAvatarOutline();
}
private void setupAvatarOutline() {
if (avatarView == null) return;
// 设置圆形裁剪确保在模拟器上也能正常工作
// 使用post确保在View布局完成后再设置outline
avatarView.post(() -> {
if (avatarView.getWidth() > 0 && avatarView.getHeight() > 0) {
avatarView.setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setOval(0, 0, view.getWidth(), view.getHeight());
}
});
avatarView.setClipToOutline(true);
}
});
} }
void bind(ChatMessage message) { void bind(ChatMessage message) {
@ -72,23 +99,114 @@ public class ConversationMessagesAdapter extends ListAdapter<ChatMessage, Recycl
nameText.setText(message.getUsername() != null ? message.getUsername() : ""); nameText.setText(message.getUsername() != null ? message.getUsername() : "");
msgText.setText(message.getMessage() != null ? message.getMessage() : ""); msgText.setText(message.getMessage() != null ? message.getMessage() : "");
timeText.setText(TIME_FORMAT.format(new Date(message.getTimestamp()))); timeText.setText(TIME_FORMAT.format(new Date(message.getTimestamp())));
// 确保头像圆形裁剪设置正确
setupAvatarOutline();
// 加载对方头像可以根据用户名或其他信息加载
loadAvatar(message.getUsername());
}
private void loadAvatar(String username) {
if (avatarView == null) return;
// 这里可以根据用户名加载对应的头像
// 暂时使用默认头像后续可以根据实际需求从服务器或本地加载
try {
// 可以根据username从SharedPreferences或其他地方加载头像
// 这里先使用默认头像使用Glide确保圆形裁剪
Glide.with(avatarView)
.load(R.drawable.ic_account_circle_24)
.circleCrop()
.into(avatarView);
} catch (Exception e) {
Glide.with(avatarView)
.load(R.drawable.ic_account_circle_24)
.circleCrop()
.into(avatarView);
}
} }
} }
static class OutgoingVH extends RecyclerView.ViewHolder { static class OutgoingVH extends RecyclerView.ViewHolder {
private final ImageView avatarView;
private final TextView msgText; private final TextView msgText;
private final TextView timeText; private final TextView timeText;
OutgoingVH(@NonNull View itemView) { OutgoingVH(@NonNull View itemView) {
super(itemView); super(itemView);
avatarView = itemView.findViewById(R.id.avatarView);
msgText = itemView.findViewById(R.id.messageText); msgText = itemView.findViewById(R.id.messageText);
timeText = itemView.findViewById(R.id.timeText); timeText = itemView.findViewById(R.id.timeText);
setupAvatarOutline();
}
private void setupAvatarOutline() {
if (avatarView == null) return;
// 设置圆形裁剪确保在模拟器上也能正常工作
// 使用post确保在View布局完成后再设置outline
avatarView.post(() -> {
if (avatarView.getWidth() > 0 && avatarView.getHeight() > 0) {
avatarView.setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setOval(0, 0, view.getWidth(), view.getHeight());
}
});
avatarView.setClipToOutline(true);
}
});
} }
void bind(ChatMessage message) { void bind(ChatMessage message) {
if (message == null) return; if (message == null) return;
msgText.setText(message.getMessage() != null ? message.getMessage() : ""); msgText.setText(message.getMessage() != null ? message.getMessage() : "");
timeText.setText(TIME_FORMAT.format(new Date(message.getTimestamp()))); timeText.setText(TIME_FORMAT.format(new Date(message.getTimestamp())));
// 确保头像圆形裁剪设置正确
setupAvatarOutline();
// 加载用户头像
loadAvatar();
}
private void loadAvatar() {
if (avatarView == null) return;
try {
String avatarUri = avatarView.getContext()
.getSharedPreferences("profile_prefs", android.content.Context.MODE_PRIVATE)
.getString("profile_avatar_uri", null);
if (!TextUtils.isEmpty(avatarUri)) {
Glide.with(avatarView)
.load(Uri.parse(avatarUri))
.circleCrop()
.error(R.drawable.ic_account_circle_24)
.into(avatarView);
return;
}
int avatarRes = avatarView.getContext()
.getSharedPreferences("profile_prefs", android.content.Context.MODE_PRIVATE)
.getInt("profile_avatar_res", 0);
if (avatarRes != 0) {
Glide.with(avatarView)
.load(avatarRes)
.circleCrop()
.error(R.drawable.ic_account_circle_24)
.into(avatarView);
} else {
Glide.with(avatarView)
.load(R.drawable.ic_account_circle_24)
.circleCrop()
.into(avatarView);
}
} catch (Exception e) {
Glide.with(avatarView)
.load(R.drawable.ic_account_circle_24)
.circleCrop()
.into(avatarView);
}
} }
} }

View File

@ -1,14 +1,19 @@
package com.example.livestreaming; package com.example.livestreaming;
import android.graphics.Outline;
import android.net.Uri;
import android.text.TextUtils;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil; import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter; import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.example.livestreaming.databinding.ItemConversationBinding; import com.example.livestreaming.databinding.ItemConversationBinding;
public class ConversationsAdapter extends ListAdapter<ConversationItem, ConversationsAdapter.VH> { public class ConversationsAdapter extends ListAdapter<ConversationItem, ConversationsAdapter.VH> {
@ -66,11 +71,56 @@ public class ConversationsAdapter extends ListAdapter<ConversationItem, Conversa
binding.muteIcon.setVisibility(View.GONE); binding.muteIcon.setVisibility(View.GONE);
} }
// 加载头像
loadAvatar(item);
binding.getRoot().setOnClickListener(v -> { binding.getRoot().setOnClickListener(v -> {
if (item == null) return; if (item == null) return;
if (onConversationClickListener != null) onConversationClickListener.onConversationClick(item); if (onConversationClickListener != null) onConversationClickListener.onConversationClick(item);
}); });
} }
private void loadAvatar(ConversationItem item) {
if (binding.avatar == null) return;
// 设置圆形裁剪
setupAvatarOutline();
// 根据会话ID或标题加载对应的头像
// 这里可以根据实际需求从服务器或本地加载
// 暂时使用默认头像或者根据会话类型加载不同头像
try {
String conversationId = item != null ? item.getId() : null;
// 可以根据conversationId从SharedPreferences或其他地方加载头像
// 这里先使用默认头像使用Glide确保圆形裁剪
Glide.with(binding.avatar)
.load(R.drawable.ic_account_circle_24)
.circleCrop()
.into(binding.avatar);
} catch (Exception e) {
Glide.with(binding.avatar)
.load(R.drawable.ic_account_circle_24)
.circleCrop()
.into(binding.avatar);
}
}
private void setupAvatarOutline() {
if (binding.avatar == null) return;
// 设置圆形裁剪确保在模拟器上也能正常工作
binding.avatar.post(() -> {
if (binding.avatar.getWidth() > 0 && binding.avatar.getHeight() > 0) {
binding.avatar.setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setOval(0, 0, view.getWidth(), view.getHeight());
}
});
binding.avatar.setClipToOutline(true);
}
});
}
} }
private static final DiffUtil.ItemCallback<ConversationItem> DIFF = new DiffUtil.ItemCallback<ConversationItem>() { private static final DiffUtil.ItemCallback<ConversationItem> DIFF = new DiffUtil.ItemCallback<ConversationItem>() {

View File

@ -0,0 +1,61 @@
package com.example.livestreaming;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import com.example.livestreaming.databinding.ActivityDrawGuessBinding;
import com.google.android.material.bottomnavigation.BottomNavigationView;
public class DrawGuessActivity extends AppCompatActivity {
private ActivityDrawGuessBinding binding;
public static void start(Context context) {
Intent intent = new Intent(context, DrawGuessActivity.class);
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityDrawGuessBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setupUI();
}
private void setupUI() {
binding.backButton.setOnClickListener(v -> finish());
BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation;
bottomNavigation.setSelectedItemId(R.id.nav_friends);
bottomNavigation.setOnItemSelectedListener(item -> {
int id = item.getItemId();
if (id == R.id.nav_home) {
startActivity(new Intent(this, MainActivity.class));
finish();
return true;
}
if (id == R.id.nav_wish_tree) {
WishTreeActivity.start(this);
finish();
return true;
}
if (id == R.id.nav_messages) {
MessagesActivity.start(this);
finish();
return true;
}
if (id == R.id.nav_profile) {
ProfileActivity.start(this);
finish();
return true;
}
return true;
});
}
}

View File

@ -4,25 +4,41 @@ import android.Manifest;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import android.widget.AdapterView;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.FileProvider; import androidx.core.content.FileProvider;
import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts; import androidx.activity.result.contract.ActivityResultContracts;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.example.livestreaming.databinding.ActivityEditProfileBinding; import com.example.livestreaming.databinding.ActivityEditProfileBinding;
import com.google.android.material.bottomsheet.BottomSheetDialog; import com.google.android.material.bottomsheet.BottomSheetDialog;
import android.widget.NumberPicker;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
public class EditProfileActivity extends AppCompatActivity { public class EditProfileActivity extends AppCompatActivity {
@ -84,9 +100,41 @@ public class EditProfileActivity extends AppCompatActivity {
binding.cancelButton.setOnClickListener(v -> finish()); binding.cancelButton.setOnClickListener(v -> finish());
loadFromPrefs(); loadFromPrefs();
setupGenderDropdown();
setupLocationDropdown();
setupBirthdayPicker();
binding.avatarRow.setOnClickListener(v -> showAvatarBottomSheet()); binding.avatarRow.setOnClickListener(v -> showAvatarBottomSheet());
// 头像预览点击放大
binding.avatarPreview.setOnClickListener(v -> {
AvatarViewerDialog dialog = AvatarViewerDialog.create(this);
// 优先使用selectedAvatarUri当前选择但未保存的头像
if (selectedAvatarUri != null) {
dialog.setAvatarUri(selectedAvatarUri);
} else {
// 如果没有选择新头像从SharedPreferences读取
String avatarUri = getSharedPreferences(PREFS_NAME, MODE_PRIVATE).getString(KEY_AVATAR_URI, null);
int avatarRes = getSharedPreferences(PREFS_NAME, MODE_PRIVATE).getInt(KEY_AVATAR_RES, 0);
if (!TextUtils.isEmpty(avatarUri)) {
dialog.setAvatarUri(Uri.parse(avatarUri));
} else if (avatarRes != 0) {
dialog.setAvatarResId(avatarRes);
} else {
// 如果都没有尝试从ImageView获取Drawable
Drawable drawable = binding.avatarPreview.getDrawable();
if (drawable != null) {
dialog.setAvatarDrawable(drawable);
} else {
dialog.setAvatarResId(R.drawable.ic_account_circle_24);
}
}
}
dialog.show();
});
binding.saveButton.setOnClickListener(v -> { binding.saveButton.setOnClickListener(v -> {
String name = binding.inputName.getText() != null ? binding.inputName.getText().toString().trim() : ""; String name = binding.inputName.getText() != null ? binding.inputName.getText().toString().trim() : "";
String bio = binding.inputBio.getText() != null ? binding.inputBio.getText().toString().trim() : ""; String bio = binding.inputBio.getText() != null ? binding.inputBio.getText().toString().trim() : "";
@ -121,9 +169,12 @@ public class EditProfileActivity extends AppCompatActivity {
} }
} }
// 生日已经在日期选择器中实时保存这里只需要确保同步
// 如果输入框为空则清除
if (TextUtils.isEmpty(birthday)) { if (TextUtils.isEmpty(birthday)) {
getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().remove(KEY_BIRTHDAY).apply(); getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().remove(KEY_BIRTHDAY).apply();
} else { } else {
// 确保使用输入框中的最新值
getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().putString(KEY_BIRTHDAY, birthday).apply(); getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().putString(KEY_BIRTHDAY, birthday).apply();
} }
@ -139,6 +190,8 @@ public class EditProfileActivity extends AppCompatActivity {
getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().putString(KEY_LOCATION, location).apply(); getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().putString(KEY_LOCATION, location).apply();
} }
// 设置结果通知ProfileActivity需要刷新
setResult(RESULT_OK);
finish(); finish();
}); });
} }
@ -200,6 +253,11 @@ public class EditProfileActivity extends AppCompatActivity {
if (!TextUtils.isEmpty(avatarUri)) { if (!TextUtils.isEmpty(avatarUri)) {
selectedAvatarUri = Uri.parse(avatarUri); selectedAvatarUri = Uri.parse(avatarUri);
Glide.with(this).load(selectedAvatarUri).circleCrop().into(binding.avatarPreview); Glide.with(this).load(selectedAvatarUri).circleCrop().into(binding.avatarPreview);
} else {
int avatarRes = getSharedPreferences(PREFS_NAME, MODE_PRIVATE).getInt(KEY_AVATAR_RES, 0);
if (avatarRes != 0) {
Glide.with(this).load(avatarRes).circleCrop().into(binding.avatarPreview);
}
} }
} }
@ -250,4 +308,254 @@ public class EditProfileActivity extends AppCompatActivity {
return null; return null;
} }
} }
private void setupGenderDropdown() {
String[] genders = {"", "", "保密"};
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_dropdown_item_1line, genders);
binding.inputGender.setOnClickListener(v -> {
android.widget.PopupMenu popupMenu = new android.widget.PopupMenu(this, binding.inputGender);
for (int i = 0; i < genders.length; i++) {
popupMenu.getMenu().add(android.view.Menu.NONE, i, i, genders[i]);
} }
popupMenu.setOnMenuItemClickListener(item -> {
String selectedGender = genders[item.getItemId()];
binding.inputGender.setText(selectedGender);
return true;
});
popupMenu.show();
});
}
private void setupLocationDropdown() {
binding.inputLocation.setOnClickListener(v -> showLocationPicker());
}
private void showLocationPicker() {
BottomSheetDialog dialog = new BottomSheetDialog(this);
View view = LayoutInflater.from(this).inflate(R.layout.bottom_sheet_location_picker_spinner, null);
dialog.setContentView(view);
Spinner provinceSpinner = view.findViewById(R.id.provinceSpinner);
Spinner citySpinner = view.findViewById(R.id.citySpinner);
TextView confirmButton = view.findViewById(R.id.confirmButton);
TextView cancelButton = view.findViewById(R.id.cancelButton);
// 加载地址数据
LocationDataManager locationManager = LocationDataManager.getInstance();
locationManager.loadData(this);
// 解析当前地址
String currentLocation = binding.inputLocation.getText() != null ? binding.inputLocation.getText().toString() : "";
String[] parsed = locationManager.parseLocation(currentLocation);
String currentProvince = parsed[0];
String currentCity = parsed[1];
// 初始化省份Spinner
List<String> provinceNames = locationManager.getProvinceNames();
ArrayAdapter<String> provinceAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, provinceNames);
provinceAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
provinceSpinner.setAdapter(provinceAdapter);
// 设置当前选中的省份
if (!TextUtils.isEmpty(currentProvince)) {
int provinceIndex = locationManager.findProvinceIndex(currentProvince);
if (provinceIndex >= 0 && provinceIndex < provinceNames.size()) {
provinceSpinner.setSelection(provinceIndex);
}
}
// 初始化城市Spinner
final String[] selectedProvince = {currentProvince};
final String[] selectedCity = {currentCity};
updateCitySpinner(citySpinner, locationManager, selectedProvince[0], selectedCity[0]);
// 省份选择监听
provinceSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
selectedProvince[0] = provinceNames.get(position);
selectedCity[0] = "";
updateCitySpinner(citySpinner, locationManager, selectedProvince[0], "");
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
// 城市选择监听
citySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
List<String> cityNames = locationManager.getCityNames(selectedProvince[0]);
if (position >= 0 && position < cityNames.size()) {
selectedCity[0] = cityNames.get(position);
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
confirmButton.setOnClickListener(v -> {
String location = locationManager.formatLocation(selectedProvince[0], selectedCity[0]);
binding.inputLocation.setText(location);
dialog.dismiss();
});
cancelButton.setOnClickListener(v -> dialog.dismiss());
dialog.show();
}
private void updateCitySpinner(Spinner citySpinner, LocationDataManager locationManager, String province, String selectedCity) {
List<String> cityNames = locationManager.getCityNames(province);
ArrayAdapter<String> cityAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, cityNames);
cityAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
citySpinner.setAdapter(cityAdapter);
if (!TextUtils.isEmpty(selectedCity) && cityNames.contains(selectedCity)) {
int cityIndex = locationManager.findCityIndex(province, selectedCity);
if (cityIndex >= 0 && cityIndex < cityNames.size()) {
citySpinner.setSelection(cityIndex);
}
} else if (!cityNames.isEmpty()) {
citySpinner.setSelection(0);
}
}
private void setupBirthdayPicker() {
binding.inputBirthday.setOnClickListener(v -> showDatePicker());
}
private void showDatePicker() {
BottomSheetDialog dialog = new BottomSheetDialog(this);
View view = LayoutInflater.from(this).inflate(R.layout.bottom_sheet_date_picker, null);
dialog.setContentView(view);
NumberPicker yearPicker = view.findViewById(R.id.yearPicker);
NumberPicker monthPicker = view.findViewById(R.id.monthPicker);
NumberPicker dayPicker = view.findViewById(R.id.dayPicker);
TextView confirmButton = view.findViewById(R.id.confirmButton);
TextView cancelButton = view.findViewById(R.id.cancelButton);
// 获取当前日期
Calendar today = Calendar.getInstance();
int currentYear = today.get(Calendar.YEAR);
int currentMonth = today.get(Calendar.MONTH) + 1; // Calendar.MONTH 从0开始
int currentDay = today.get(Calendar.DAY_OF_MONTH);
// 解析当前生日
String currentBirthday = binding.inputBirthday.getText() != null ? binding.inputBirthday.getText().toString() : "";
final int[] selectedYear = {currentYear - 25}; // 默认25岁
final int[] selectedMonth = {currentMonth};
final int[] selectedDay = {currentDay};
if (!TextUtils.isEmpty(currentBirthday)) {
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
java.util.Date date = sdf.parse(currentBirthday);
if (date != null) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
selectedYear[0] = cal.get(Calendar.YEAR);
selectedMonth[0] = cal.get(Calendar.MONTH) + 1;
selectedDay[0] = cal.get(Calendar.DAY_OF_MONTH);
}
} catch (Exception e) {
// 解析失败使用默认值
}
}
// 设置年份范围100年前到今天
int minYear = currentYear - 100;
int maxYear = currentYear;
yearPicker.setMinValue(minYear);
yearPicker.setMaxValue(maxYear);
yearPicker.setValue(selectedYear[0]);
yearPicker.setWrapSelectorWheel(false);
// 设置月份范围1-12
monthPicker.setMinValue(1);
monthPicker.setMaxValue(12);
monthPicker.setValue(selectedMonth[0]);
monthPicker.setWrapSelectorWheel(false);
monthPicker.setDisplayedValues(new String[]{"1月", "2月", "3月", "4月", "5月", "6月",
"7月", "8月", "9月", "10月", "11月", "12月"});
// 更新日期选择器的最大值
updateDayPicker(dayPicker, selectedYear[0], selectedMonth[0], selectedDay[0]);
// 年份和月份变化时更新日期选择器
yearPicker.setOnValueChangedListener((picker, oldVal, newVal) -> {
selectedYear[0] = newVal;
int maxDay = getDaysInMonth(selectedYear[0], selectedMonth[0]);
if (selectedDay[0] > maxDay) {
selectedDay[0] = maxDay;
}
updateDayPicker(dayPicker, selectedYear[0], selectedMonth[0], selectedDay[0]);
});
monthPicker.setOnValueChangedListener((picker, oldVal, newVal) -> {
selectedMonth[0] = newVal;
int maxDay = getDaysInMonth(selectedYear[0], selectedMonth[0]);
if (selectedDay[0] > maxDay) {
selectedDay[0] = maxDay;
}
updateDayPicker(dayPicker, selectedYear[0], selectedMonth[0], selectedDay[0]);
});
// 确认按钮
confirmButton.setOnClickListener(v -> {
int year = yearPicker.getValue();
int month = monthPicker.getValue();
int day = dayPicker.getValue();
// 格式化日期
Calendar selectedDate = Calendar.getInstance();
selectedDate.set(year, month - 1, day); // Calendar.MONTH 从0开始
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
String formattedDate = sdf.format(selectedDate.getTime());
// 更新输入框显示
binding.inputBirthday.setText(formattedDate);
// 立即保存到SharedPreferences
getSharedPreferences(PREFS_NAME, MODE_PRIVATE)
.edit()
.putString(KEY_BIRTHDAY, formattedDate)
.apply();
dialog.dismiss();
});
// 取消按钮
cancelButton.setOnClickListener(v -> dialog.dismiss());
dialog.show();
}
private void updateDayPicker(NumberPicker dayPicker, int year, int month, int selectedDay) {
int maxDay = getDaysInMonth(year, month);
dayPicker.setMinValue(1);
dayPicker.setMaxValue(maxDay);
dayPicker.setWrapSelectorWheel(false);
// 确保选中的日期不超过最大值
if (selectedDay > maxDay) {
selectedDay = maxDay;
}
dayPicker.setValue(selectedDay);
}
private int getDaysInMonth(int year, int month) {
Calendar cal = Calendar.getInstance();
cal.set(year, month - 1, 1); // Calendar.MONTH 从0开始
return cal.getActualMaximum(Calendar.DAY_OF_MONTH);
}
}

View File

@ -0,0 +1,61 @@
package com.example.livestreaming;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import com.example.livestreaming.databinding.ActivityFindGameBinding;
import com.google.android.material.bottomnavigation.BottomNavigationView;
public class FindGameActivity extends AppCompatActivity {
private ActivityFindGameBinding binding;
public static void start(Context context) {
Intent intent = new Intent(context, FindGameActivity.class);
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityFindGameBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setupUI();
}
private void setupUI() {
binding.backButton.setOnClickListener(v -> finish());
BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation;
bottomNavigation.setSelectedItemId(R.id.nav_friends);
bottomNavigation.setOnItemSelectedListener(item -> {
int id = item.getItemId();
if (id == R.id.nav_home) {
startActivity(new Intent(this, MainActivity.class));
finish();
return true;
}
if (id == R.id.nav_wish_tree) {
WishTreeActivity.start(this);
finish();
return true;
}
if (id == R.id.nav_messages) {
MessagesActivity.start(this);
finish();
return true;
}
if (id == R.id.nav_profile) {
ProfileActivity.start(this);
finish();
return true;
}
return true;
});
}
}

View File

@ -1,10 +1,12 @@
package com.example.livestreaming; package com.example.livestreaming;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.animation.ValueAnimator; import android.animation.ValueAnimator;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.text.TextUtils;
import android.view.HapticFeedbackConstants; import android.view.HapticFeedbackConstants;
import android.view.View; import android.view.View;
import android.view.animation.LinearInterpolator; import android.view.animation.LinearInterpolator;
@ -70,12 +72,18 @@ public class FishPondActivity extends AppCompatActivity {
setupOrbitUserInteractions(); setupOrbitUserInteractions();
setupCenterRefresh(); setupCenterRefresh();
setupQuickActions();
loadCurrentUserInfo();
binding.orbitContainer.post(this::updateOrbitLayout); binding.orbitContainer.post(this::updateOrbitLayout);
refreshUsers(); refreshUsers();
BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation; BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation;
bottomNavigation.setSelectedItemId(R.id.nav_friends); bottomNavigation.setSelectedItemId(R.id.nav_friends);
// 更新未读消息徽章
UnreadMessageManager.updateBadge(bottomNavigation);
bottomNavigation.setOnItemSelectedListener(item -> { bottomNavigation.setOnItemSelectedListener(item -> {
int id = item.getItemId(); int id = item.getItemId();
if (id == R.id.nav_home) { if (id == R.id.nav_home) {
@ -116,6 +124,54 @@ public class FishPondActivity extends AppCompatActivity {
}); });
} }
private void setupQuickActions() {
if (binding == null) return;
// 语音匹配按钮
binding.actionLeft.setOnClickListener(v -> {
VoiceMatchActivity.start(this);
});
// 心动信号按钮
binding.actionRight.setOnClickListener(v -> {
HeartbeatSignalActivity.start(this);
});
// 获取GridLayout中的卡片并设置点击事件
android.widget.GridLayout grid = binding.grid;
if (grid != null) {
int childCount = grid.getChildCount();
for (int i = 0; i < childCount && i < 6; i++) {
View card = grid.getChildAt(i);
if (card != null) {
final int index = i;
card.setOnClickListener(v -> {
switch (index) {
case 0: // 在线处对象
OnlineDatingActivity.start(this);
break;
case 1: // 找人玩游戏
FindGameActivity.start(this);
break;
case 2: // 一起KTV
KTVTogetherActivity.start(this);
break;
case 3: // 你画我猜
DrawGuessActivity.start(this);
break;
case 4: // 和平精英
PeaceEliteActivity.start(this);
break;
case 5: // 桌子游
TableGamesActivity.start(this);
break;
}
});
}
}
}
}
private void updateOrbitLayout() { private void updateOrbitLayout() {
if (binding == null) return; if (binding == null) return;
@ -227,6 +283,42 @@ public class FishPondActivity extends AppCompatActivity {
uiHandler.removeCallbacks(pulseRunnable); uiHandler.removeCallbacks(pulseRunnable);
} }
private void loadCurrentUserInfo() {
if (binding == null) return;
SharedPreferences prefs = getSharedPreferences("profile_prefs", MODE_PRIVATE);
String name = prefs.getString("profile_name", "爱你");
String location = prefs.getString("profile_location", "广西");
String gender = prefs.getString("profile_gender", "");
// 设置右侧名称
binding.rightName.setText(name);
// 设置右侧信息性别 | 地址 | 在线
String genderText = "";
if (!TextUtils.isEmpty(gender)) {
if (gender.contains("")) {
genderText = "真诚";
} else if (gender.contains("")) {
genderText = "真诚";
} else {
genderText = "真诚";
}
} else {
genderText = "真诚";
}
// 处理地址格式如果是"省份-城市"格式转换为"省份·城市"
String locationText = location;
if (!TextUtils.isEmpty(location) && location.contains("-")) {
locationText = location.replace("-", "·");
} else if (TextUtils.isEmpty(location)) {
locationText = "南宁";
}
binding.rightInfo.setText(genderText + " | " + locationText + " | 在线");
}
private void refreshUsers() { private void refreshUsers() {
if (binding == null) return; if (binding == null) return;
@ -330,9 +422,15 @@ public class FishPondActivity extends AppCompatActivity {
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
if (binding != null) { if (binding != null) {
binding.bottomNavInclude.bottomNavigation.setSelectedItemId(R.id.nav_friends); BottomNavigationView bottomNav = binding.bottomNavInclude.bottomNavigation;
bottomNav.setSelectedItemId(R.id.nav_friends);
// 更新未读消息徽章
UnreadMessageManager.updateBadge(bottomNav);
} }
// 刷新当前用户信息可能从编辑资料页面返回
loadCurrentUserInfo();
// ensure radius computed before starting // ensure radius computed before starting
if (binding != null) { if (binding != null) {
binding.orbitContainer.post(() -> { binding.orbitContainer.post(() -> {

View File

@ -0,0 +1,62 @@
package com.example.livestreaming;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
import com.example.livestreaming.databinding.ActivityHeartbeatSignalBinding;
import com.google.android.material.bottomnavigation.BottomNavigationView;
public class HeartbeatSignalActivity extends AppCompatActivity {
private ActivityHeartbeatSignalBinding binding;
public static void start(Context context) {
Intent intent = new Intent(context, HeartbeatSignalActivity.class);
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityHeartbeatSignalBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setupUI();
}
private void setupUI() {
binding.backButton.setOnClickListener(v -> finish());
BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation;
bottomNavigation.setSelectedItemId(R.id.nav_friends);
bottomNavigation.setOnItemSelectedListener(item -> {
int id = item.getItemId();
if (id == R.id.nav_home) {
startActivity(new Intent(this, MainActivity.class));
finish();
return true;
}
if (id == R.id.nav_wish_tree) {
WishTreeActivity.start(this);
finish();
return true;
}
if (id == R.id.nav_messages) {
MessagesActivity.start(this);
finish();
return true;
}
if (id == R.id.nav_profile) {
ProfileActivity.start(this);
finish();
return true;
}
return true;
});
}
}

View File

@ -0,0 +1,61 @@
package com.example.livestreaming;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import com.example.livestreaming.databinding.ActivityKtvTogetherBinding;
import com.google.android.material.bottomnavigation.BottomNavigationView;
public class KTVTogetherActivity extends AppCompatActivity {
private ActivityKtvTogetherBinding binding;
public static void start(Context context) {
Intent intent = new Intent(context, KTVTogetherActivity.class);
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityKtvTogetherBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setupUI();
}
private void setupUI() {
binding.backButton.setOnClickListener(v -> finish());
BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation;
bottomNavigation.setSelectedItemId(R.id.nav_friends);
bottomNavigation.setOnItemSelectedListener(item -> {
int id = item.getItemId();
if (id == R.id.nav_home) {
startActivity(new Intent(this, MainActivity.class));
finish();
return true;
}
if (id == R.id.nav_wish_tree) {
WishTreeActivity.start(this);
finish();
return true;
}
if (id == R.id.nav_messages) {
MessagesActivity.start(this);
finish();
return true;
}
if (id == R.id.nav_profile) {
ProfileActivity.start(this);
finish();
return true;
}
return true;
});
}
}

View File

@ -0,0 +1,142 @@
package com.example.livestreaming;
import android.content.Context;
import android.text.TextUtils;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class LocationDataManager {
private static LocationDataManager instance;
private List<Province> provinces;
private Map<String, Province> provinceMap;
private LocationDataManager() {
provinces = new ArrayList<>();
provinceMap = new HashMap<>();
}
public static synchronized LocationDataManager getInstance() {
if (instance == null) {
instance = new LocationDataManager();
}
return instance;
}
public void loadData(Context context) {
if (provinces != null && !provinces.isEmpty()) {
return; // 已经加载过
}
try {
InputStream is = context.getAssets().open("location_data.json");
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
String json = new String(buffer, "UTF-8");
JSONObject jsonObject = new JSONObject(json);
JSONArray provincesArray = jsonObject.getJSONArray("provinces");
provinces.clear();
provinceMap.clear();
for (int i = 0; i < provincesArray.length(); i++) {
JSONObject provinceObj = provincesArray.getJSONObject(i);
Province province = new Province();
province.name = provinceObj.getString("name");
JSONArray citiesArray = provinceObj.getJSONArray("cities");
province.cities = new ArrayList<>();
for (int j = 0; j < citiesArray.length(); j++) {
JSONObject cityObj = citiesArray.getJSONObject(j);
City city = new City();
city.name = cityObj.getString("name");
province.cities.add(city);
}
provinces.add(province);
provinceMap.put(province.name, province);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public List<String> getProvinceNames() {
List<String> names = new ArrayList<>();
for (Province province : provinces) {
names.add(province.name);
}
return names;
}
public List<String> getCityNames(String provinceName) {
List<String> names = new ArrayList<>();
Province province = provinceMap.get(provinceName);
if (province != null && province.cities != null) {
for (City city : province.cities) {
names.add(city.name);
}
}
return names;
}
public String formatLocation(String province, String city) {
if (TextUtils.isEmpty(province)) {
return "";
}
if (TextUtils.isEmpty(city)) {
return province;
}
return province + "-" + city;
}
public String[] parseLocation(String location) {
if (TextUtils.isEmpty(location)) {
return new String[]{"", ""};
}
if (location.contains("-")) {
String[] parts = location.split("-", 2);
return new String[]{parts[0], parts.length > 1 ? parts[1] : ""};
}
return new String[]{location, ""};
}
public int findProvinceIndex(String provinceName) {
for (int i = 0; i < provinces.size(); i++) {
if (provinces.get(i).name.equals(provinceName)) {
return i;
}
}
return 0;
}
public int findCityIndex(String provinceName, String cityName) {
Province province = provinceMap.get(provinceName);
if (province != null && province.cities != null) {
for (int i = 0; i < province.cities.size(); i++) {
if (province.cities.get(i).name.equals(cityName)) {
return i;
}
}
}
return 0;
}
static class Province {
String name;
List<City> cities;
}
static class City {
String name;
}
}

View File

@ -1,14 +1,21 @@
package com.example.livestreaming; package com.example.livestreaming;
import android.Manifest;
import android.content.res.AssetManager; import android.content.res.AssetManager;
import android.content.ClipData; import android.content.ClipData;
import android.content.ClipboardManager; import android.content.ClipboardManager;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.speech.RecognitionListener;
import android.speech.RecognizerIntent;
import android.speech.SpeechRecognizer;
import android.text.Editable;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
@ -16,6 +23,8 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.view.GravityCompat; import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout; import androidx.drawerlayout.widget.DrawerLayout;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
@ -56,6 +65,11 @@ public class MainActivity extends AppCompatActivity {
private boolean isFetching; private boolean isFetching;
private long lastFetchMs; private long lastFetchMs;
private static final int REQUEST_RECORD_AUDIO_PERMISSION = 200;
private SpeechRecognizer speechRecognizer;
private Intent speechRecognizerIntent;
private boolean isListening = false;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -66,6 +80,29 @@ public class MainActivity extends AppCompatActivity {
setupRecyclerView(); setupRecyclerView();
setupUI(); setupUI();
loadAvatarFromPrefs(); loadAvatarFromPrefs();
setupSpeechRecognizer();
// 初始化未读消息数量演示数据
if (UnreadMessageManager.getUnreadCount(this) == 0) {
// 从消息列表计算总未读数量
UnreadMessageManager.setUnreadCount(this, calculateTotalUnreadCount());
}
// 清除默认选中状态让所有标签页初始显示为未选中样式
if (binding != null && binding.topTabs != null) {
// 在布局完成后清除默认选中状态
binding.topTabs.post(() -> {
// 清除所有选中状态
for (int i = 0; i < binding.topTabs.getTabCount(); i++) {
TabLayout.Tab tab = binding.topTabs.getTabAt(i);
if (tab != null && tab.isSelected()) {
// 取消选中但不触发监听器
binding.topTabs.selectTab(null, false);
break;
}
}
});
}
// 异步加载资源文件避免阻塞主线程 // 异步加载资源文件避免阻塞主线程
loadCoverAssetsAsync(); loadCoverAssetsAsync();
@ -91,7 +128,7 @@ public class MainActivity extends AppCompatActivity {
items.add(new DrawerCardItem(DrawerCardItem.ACTION_PROFILE, "个人主页", "资料、作品、粉丝", R.drawable.ic_person_24)); items.add(new DrawerCardItem(DrawerCardItem.ACTION_PROFILE, "个人主页", "资料、作品、粉丝", R.drawable.ic_person_24));
items.add(new DrawerCardItem(DrawerCardItem.ACTION_MESSAGES, "消息", "私信、互动通知", R.drawable.ic_chat_24)); items.add(new DrawerCardItem(DrawerCardItem.ACTION_MESSAGES, "消息", "私信、互动通知", R.drawable.ic_chat_24));
items.add(new DrawerCardItem(DrawerCardItem.ACTION_MY_FRIENDS, "我的好友", "通讯录与挚友", R.drawable.ic_people_24)); items.add(new DrawerCardItem(DrawerCardItem.ACTION_MY_FRIENDS, "我的好友", "通讯录与挚友", R.drawable.ic_people_24));
items.add(new DrawerCardItem(DrawerCardItem.ACTION_FISH_POND, "鱼塘", "附近与社交圈", R.drawable.ic_globe_24)); items.add(new DrawerCardItem(DrawerCardItem.ACTION_FISH_POND, "缘池", "附近与社交圈", R.drawable.ic_people_24));
items.add(new DrawerCardItem(DrawerCardItem.ACTION_FOLLOWING, "我的关注", "你关注的主播", R.drawable.ic_people_24)); items.add(new DrawerCardItem(DrawerCardItem.ACTION_FOLLOWING, "我的关注", "你关注的主播", R.drawable.ic_people_24));
items.add(new DrawerCardItem(DrawerCardItem.ACTION_FANS, "粉丝", "关注你的人", R.drawable.ic_people_24)); items.add(new DrawerCardItem(DrawerCardItem.ACTION_FANS, "粉丝", "关注你的人", R.drawable.ic_people_24));
items.add(new DrawerCardItem(DrawerCardItem.ACTION_LIKES, "获赞", "收到的点赞", R.drawable.ic_heart_24)); items.add(new DrawerCardItem(DrawerCardItem.ACTION_LIKES, "获赞", "收到的点赞", R.drawable.ic_heart_24));
@ -204,12 +241,20 @@ public class MainActivity extends AppCompatActivity {
@Override @Override
public void onTabSelected(TabLayout.Tab tab) { public void onTabSelected(TabLayout.Tab tab) {
if (tab == null) return; if (tab == null) return;
// 用户点击后显示选中样式显示指示器改变文字颜色
binding.topTabs.setSelectedTabIndicatorHeight(2); // 显示指示器
// 更新布局属性以显示选中样式
binding.topTabs.setTabTextColors(
getResources().getColor(android.R.color.darker_gray, null), // 未选中颜色
getResources().getColor(R.color.purple_500, null) // 选中颜色
);
CharSequence title = tab.getText(); CharSequence title = tab.getText();
TabPlaceholderActivity.start(MainActivity.this, title != null ? title.toString() : ""); TabPlaceholderActivity.start(MainActivity.this, title != null ? title.toString() : "");
} }
@Override @Override
public void onTabUnselected(TabLayout.Tab tab) { public void onTabUnselected(TabLayout.Tab tab) {
// 取消选中时恢复默认样式
} }
@Override @Override
@ -271,8 +316,15 @@ public class MainActivity extends AppCompatActivity {
// 设置添加直播按钮点击事件 // 设置添加直播按钮点击事件
binding.fabAddLive.setOnClickListener(v -> showCreateRoomDialog()); binding.fabAddLive.setOnClickListener(v -> showCreateRoomDialog());
// 设置搜索框文本监听和图标切换
setupSearchBox();
BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation; BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation;
bottomNavigation.setSelectedItemId(R.id.nav_home); bottomNavigation.setSelectedItemId(R.id.nav_home);
// 更新未读消息徽章
UnreadMessageManager.updateBadge(bottomNavigation);
bottomNavigation.setOnItemSelectedListener(item -> { bottomNavigation.setOnItemSelectedListener(item -> {
int id = item.getItemId(); int id = item.getItemId();
if (id == R.id.nav_home) { if (id == R.id.nav_home) {
@ -302,6 +354,245 @@ public class MainActivity extends AppCompatActivity {
}); });
} }
private void setupSearchBox() {
// 监听搜索框文本变化
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) {
// 根据文本是否为空切换图标
String text = s != null ? s.toString().trim() : "";
if (text.isEmpty()) {
// 文本为空显示麦克风图标
binding.micIcon.setImageResource(R.drawable.ic_mic_24);
binding.micIcon.setContentDescription("mic");
} else {
// 文本不为空显示搜索图标
binding.micIcon.setImageResource(R.drawable.ic_search_24);
binding.micIcon.setContentDescription("search");
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
// 设置麦克风/搜索图标点击事件
binding.micIcon.setOnClickListener(v -> {
String searchText = binding.searchEdit.getText() != null ?
binding.searchEdit.getText().toString().trim() : "";
if (searchText.isEmpty()) {
// 如果文本为空启动语音识别
startVoiceRecognition();
} else {
// 如果文本不为空执行搜索
// 跳转到搜索页面并传递搜索关键词
SearchActivity.start(MainActivity.this, searchText);
}
});
}
private void setupSpeechRecognizer() {
// 检查设备是否支持语音识别
if (!SpeechRecognizer.isRecognitionAvailable(this)) {
return;
}
speechRecognizer = SpeechRecognizer.createSpeechRecognizer(this);
speechRecognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, "zh-CN"); // 设置为中文
speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true);
speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 1);
speechRecognizer.setRecognitionListener(new RecognitionListener() {
@Override
public void onReadyForSpeech(Bundle params) {
isListening = true;
// 可以在这里显示"正在聆听..."的提示
binding.micIcon.setAlpha(0.5f);
}
@Override
public void onBeginningOfSpeech() {
// 开始说话
}
@Override
public void onRmsChanged(float rmsdB) {
// 音量变化可以用来显示音量动画
}
@Override
public void onBufferReceived(byte[] buffer) {
// 接收音频数据
}
@Override
public void onEndOfSpeech() {
isListening = false;
binding.micIcon.setAlpha(1.0f);
}
@Override
public void onError(int error) {
isListening = false;
binding.micIcon.setAlpha(1.0f);
String errorMessage;
switch (error) {
case SpeechRecognizer.ERROR_AUDIO:
errorMessage = "音频错误";
break;
case SpeechRecognizer.ERROR_CLIENT:
errorMessage = "客户端错误";
break;
case SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS:
errorMessage = "权限不足";
requestAudioPermission();
return;
case SpeechRecognizer.ERROR_NETWORK:
errorMessage = "网络错误";
break;
case SpeechRecognizer.ERROR_NETWORK_TIMEOUT:
errorMessage = "网络超时";
break;
case SpeechRecognizer.ERROR_NO_MATCH:
errorMessage = "未识别到语音";
break;
case SpeechRecognizer.ERROR_RECOGNIZER_BUSY:
errorMessage = "识别器忙碌";
break;
case SpeechRecognizer.ERROR_SERVER:
errorMessage = "服务器错误";
break;
case SpeechRecognizer.ERROR_SPEECH_TIMEOUT:
errorMessage = "未检测到语音";
break;
default:
errorMessage = "未知错误";
break;
}
// 只在非用户取消的情况下显示错误提示
if (error != SpeechRecognizer.ERROR_CLIENT) {
Toast.makeText(MainActivity.this, errorMessage, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onResults(Bundle results) {
isListening = false;
binding.micIcon.setAlpha(1.0f);
ArrayList<String> matches = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
if (matches != null && !matches.isEmpty()) {
String spokenText = matches.get(0);
// 将识别结果填充到搜索框
binding.searchEdit.setText(spokenText);
binding.searchEdit.setSelection(spokenText.length());
}
}
@Override
public void onPartialResults(Bundle partialResults) {
// 部分结果可以实时显示
ArrayList<String> matches = partialResults.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
if (matches != null && !matches.isEmpty()) {
String partialText = matches.get(0);
// 可以在这里实时更新搜索框可选
// binding.searchEdit.setText(partialText);
}
}
@Override
public void onEvent(int eventType, Bundle params) {
// 事件回调
}
});
}
private void startVoiceRecognition() {
// 检查权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
!= PackageManager.PERMISSION_GRANTED) {
requestAudioPermission();
return;
}
// 检查是否正在监听
if (isListening) {
stopVoiceRecognition();
return;
}
// 检查SpeechRecognizer是否可用
if (speechRecognizer == null || speechRecognizerIntent == null) {
Toast.makeText(this, "语音识别功能不可用", Toast.LENGTH_SHORT).show();
return;
}
try {
speechRecognizer.startListening(speechRecognizerIntent);
} catch (Exception e) {
Toast.makeText(this, "启动语音识别失败", Toast.LENGTH_SHORT).show();
}
}
private void stopVoiceRecognition() {
if (speechRecognizer != null && isListening) {
speechRecognizer.stopListening();
isListening = false;
binding.micIcon.setAlpha(1.0f);
}
}
private void requestAudioPermission() {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO)) {
new AlertDialog.Builder(this)
.setTitle("需要麦克风权限")
.setMessage("语音搜索需要访问麦克风权限,请在设置中允许")
.setPositiveButton("确定", (dialog, which) -> {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.RECORD_AUDIO},
REQUEST_RECORD_AUDIO_PERMISSION);
})
.setNegativeButton("取消", null)
.show();
} else {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.RECORD_AUDIO},
REQUEST_RECORD_AUDIO_PERMISSION);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_RECORD_AUDIO_PERMISSION) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 权限已授予可以开始语音识别
startVoiceRecognition();
} else {
Toast.makeText(this, "需要麦克风权限才能使用语音搜索", Toast.LENGTH_SHORT).show();
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (speechRecognizer != null) {
speechRecognizer.destroy();
speechRecognizer = null;
}
}
private void loadAvatarFromPrefs() { private void loadAvatarFromPrefs() {
try { try {
String avatarUri = getSharedPreferences("profile_prefs", MODE_PRIVATE) String avatarUri = getSharedPreferences("profile_prefs", MODE_PRIVATE)
@ -340,9 +631,32 @@ public class MainActivity extends AppCompatActivity {
BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation; BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation;
bottomNavigation.setSelectedItemId(R.id.nav_home); bottomNavigation.setSelectedItemId(R.id.nav_home);
loadAvatarFromPrefs(); loadAvatarFromPrefs();
// 更新未读消息徽章
UnreadMessageManager.updateBadge(bottomNavigation);
} }
} }
/**
* 计算总未读消息数量从演示数据中计算
*/
private int calculateTotalUnreadCount() {
// 模拟从消息列表计算总未读数量
// 这里使用 MessagesActivity 中的演示数据
int total = 0;
total += 2; // 系统通知
total += 5; // 附近的人
total += 19; // 直播间群聊
total += 1; // 客服
return total;
}
@Override
protected void onPause() {
super.onPause();
// 暂停时停止语音识别
stopVoiceRecognition();
}
@Override @Override
protected void onStart() { protected void onStart() {
super.onStart(); super.onStart();

View File

@ -33,8 +33,11 @@ public class MessagesActivity extends AppCompatActivity {
private int swipedPosition = RecyclerView.NO_POSITION; private int swipedPosition = RecyclerView.NO_POSITION;
private RectF deleteButtonRect; private RectF deleteButtonRect;
private RectF markReadButtonRect;
private final Paint deleteBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private final Paint deleteBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint deleteTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private final Paint deleteTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint markReadBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint markReadTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private int lastSwipePosition = RecyclerView.NO_POSITION; private int lastSwipePosition = RecyclerView.NO_POSITION;
private float lastSwipeDx; private float lastSwipeDx;
@ -89,14 +92,35 @@ public class MessagesActivity extends AppCompatActivity {
} }
private void setupConversationList() { private void setupConversationList() {
// 删除按钮的背景颜色红色
deleteBackgroundPaint.setColor(0xFFE53935); deleteBackgroundPaint.setColor(0xFFE53935);
deleteTextPaint.setColor(Color.WHITE); deleteTextPaint.setColor(Color.WHITE);
deleteTextPaint.setTextAlign(Paint.Align.CENTER); deleteTextPaint.setTextAlign(Paint.Align.CENTER);
deleteTextPaint.setTextSize(sp(14)); deleteTextPaint.setTextSize(sp(14));
// 标记已读按钮的背景颜色蓝色
markReadBackgroundPaint.setColor(0xFF2196F3);
markReadTextPaint.setColor(Color.WHITE);
markReadTextPaint.setTextAlign(Paint.Align.CENTER);
markReadTextPaint.setTextSize(sp(14));
conversationsAdapter = new ConversationsAdapter(item -> { conversationsAdapter = new ConversationsAdapter(item -> {
if (item == null) return; if (item == null) return;
ConversationActivity.start(this, item.getId(), item.getTitle()); // 启动会话页面传递未读数量
Intent intent = new Intent(this, ConversationActivity.class);
intent.putExtra("extra_conversation_id", item.getId());
intent.putExtra("extra_conversation_title", item.getTitle());
intent.putExtra("extra_unread_count", item.getUnreadCount());
startActivity(intent);
// 用户点击会话时减少该会话的未读数量
if (item.getUnreadCount() > 0) {
// 更新该会话的未读数量为0在实际应用中这里应该更新数据源
// 然后更新总未读数量
UnreadMessageManager.decrementUnreadCount(this, item.getUnreadCount());
// 更新列表中的未读数量显示
updateConversationUnreadCount(item.getId(), 0);
}
}); });
binding.conversationsRecyclerView.setLayoutManager(new LinearLayoutManager(this)); binding.conversationsRecyclerView.setLayoutManager(new LinearLayoutManager(this));
binding.conversationsRecyclerView.setAdapter(conversationsAdapter); binding.conversationsRecyclerView.setAdapter(conversationsAdapter);
@ -109,7 +133,9 @@ public class MessagesActivity extends AppCompatActivity {
} }
private void attachSwipeToDelete(RecyclerView recyclerView) { private void attachSwipeToDelete(RecyclerView recyclerView) {
final float actionWidth = dp(96); // 两个按钮的总宽度删除按钮 + 标记已读按钮
final float buttonWidth = dp(96);
final float actionWidth = buttonWidth * 2; // 两个按钮并排显示
touchSlop = ViewConfiguration.get(this).getScaledTouchSlop(); touchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
ItemTouchHelper.SimpleCallback callback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) { ItemTouchHelper.SimpleCallback callback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override @Override
@ -140,6 +166,7 @@ public class MessagesActivity extends AppCompatActivity {
int old = swipedPosition; int old = swipedPosition;
swipedPosition = RecyclerView.NO_POSITION; swipedPosition = RecyclerView.NO_POSITION;
deleteButtonRect = null; deleteButtonRect = null;
markReadButtonRect = null;
conversationsAdapter.notifyItemChanged(old); conversationsAdapter.notifyItemChanged(old);
} }
swipedPosition = pos; swipedPosition = pos;
@ -148,6 +175,7 @@ public class MessagesActivity extends AppCompatActivity {
if (swipedPosition == pos) { if (swipedPosition == pos) {
swipedPosition = RecyclerView.NO_POSITION; swipedPosition = RecyclerView.NO_POSITION;
deleteButtonRect = null; deleteButtonRect = null;
markReadButtonRect = null;
} }
viewHolder.itemView.setTranslationX(0f); viewHolder.itemView.setTranslationX(0f);
} }
@ -207,20 +235,37 @@ public class MessagesActivity extends AppCompatActivity {
} }
View itemView = viewHolder.itemView; View itemView = viewHolder.itemView;
float left;
float top = itemView.getTop(); float top = itemView.getTop();
float bottom = itemView.getBottom(); float bottom = itemView.getBottom();
float right = itemView.getRight(); float right = itemView.getRight();
left = right + clampedDx; float left = right + clampedDx;
deleteButtonRect = new RectF(left, top, right, bottom); // 绘制两个按钮标记已读左侧和删除右侧
final float buttonWidth = dp(96);
// 标记已读按钮左侧蓝色
float markReadLeft = left;
float markReadRight = left + buttonWidth;
markReadButtonRect = new RectF(markReadLeft, top, markReadRight, bottom);
c.drawRect(markReadButtonRect, markReadBackgroundPaint);
float markReadCenterX = markReadButtonRect.centerX();
float markReadCenterY = markReadButtonRect.centerY();
Paint.FontMetrics markReadFm = markReadTextPaint.getFontMetrics();
float markReadTextY = markReadCenterY - (markReadFm.ascent + markReadFm.descent) / 2f;
c.drawText("标记已读", markReadCenterX, markReadTextY, markReadTextPaint);
// 删除按钮右侧红色
float deleteLeft = markReadRight;
float deleteRight = right;
deleteButtonRect = new RectF(deleteLeft, top, deleteRight, bottom);
c.drawRect(deleteButtonRect, deleteBackgroundPaint); c.drawRect(deleteButtonRect, deleteBackgroundPaint);
float centerX = deleteButtonRect.centerX(); float deleteCenterX = deleteButtonRect.centerX();
float centerY = deleteButtonRect.centerY(); float deleteCenterY = deleteButtonRect.centerY();
Paint.FontMetrics fm = deleteTextPaint.getFontMetrics(); Paint.FontMetrics deleteFm = deleteTextPaint.getFontMetrics();
float textY = centerY - (fm.ascent + fm.descent) / 2f; float deleteTextY = deleteCenterY - (deleteFm.ascent + deleteFm.descent) / 2f;
c.drawText("删除", centerX, textY, deleteTextPaint); c.drawText("删除", deleteCenterX, deleteTextY, deleteTextPaint);
super.onChildDraw(c, recyclerView, viewHolder, clampedDx, dY, actionState, isCurrentlyActive); super.onChildDraw(c, recyclerView, viewHolder, clampedDx, dY, actionState, isCurrentlyActive);
} }
@ -229,7 +274,7 @@ public class MessagesActivity extends AppCompatActivity {
new ItemTouchHelper(callback).attachToRecyclerView(recyclerView); new ItemTouchHelper(callback).attachToRecyclerView(recyclerView);
recyclerView.setOnTouchListener((v, event) -> { recyclerView.setOnTouchListener((v, event) -> {
if (swipedPosition == RecyclerView.NO_POSITION || deleteButtonRect == null) return false; if (swipedPosition == RecyclerView.NO_POSITION || (deleteButtonRect == null && markReadButtonRect == null)) return false;
int action = event.getActionMasked(); int action = event.getActionMasked();
if (action == MotionEvent.ACTION_DOWN) { if (action == MotionEvent.ACTION_DOWN) {
@ -251,10 +296,20 @@ public class MessagesActivity extends AppCompatActivity {
if (action == MotionEvent.ACTION_UP) { if (action == MotionEvent.ACTION_UP) {
if (!touchIsClick) return false; if (!touchIsClick) return false;
boolean hit = deleteButtonRect.contains(event.getX(), event.getY());
int pos = swipedPosition; int pos = swipedPosition;
float x = event.getX();
float y = event.getY();
// 检查点击的是哪个按钮
boolean hitMarkRead = markReadButtonRect != null && markReadButtonRect.contains(x, y);
boolean hitDelete = deleteButtonRect != null && deleteButtonRect.contains(x, y);
recoverSwipedItem(); recoverSwipedItem();
if (hit) {
if (hitMarkRead) {
markAsReadAt(pos);
return true;
} else if (hitDelete) {
deleteConversationAt(pos); deleteConversationAt(pos);
return true; return true;
} }
@ -267,17 +322,56 @@ public class MessagesActivity extends AppCompatActivity {
int pos = swipedPosition; int pos = swipedPosition;
swipedPosition = RecyclerView.NO_POSITION; swipedPosition = RecyclerView.NO_POSITION;
deleteButtonRect = null; deleteButtonRect = null;
markReadButtonRect = null;
if (pos != RecyclerView.NO_POSITION && conversationsAdapter != null) { if (pos != RecyclerView.NO_POSITION && conversationsAdapter != null) {
conversationsAdapter.notifyItemChanged(pos); conversationsAdapter.notifyItemChanged(pos);
} }
} }
/**
* 标记会话为已读
*/
private void markAsReadAt(int position) {
if (position < 0 || position >= conversations.size()) return;
ConversationItem item = conversations.get(position);
if (item == null) return;
// 获取该会话的未读数量
int unreadCount = item.getUnreadCount();
// 如果已经有未读消息才需要更新
if (unreadCount > 0) {
// 将该会话的未读数量设为0
// updateConversationUnreadCount 方法会自动重新计算总未读数量并更新徽章
updateConversationUnreadCount(item.getId(), 0);
}
}
/**
* 删除会话
*/
private void deleteConversationAt(int position) { private void deleteConversationAt(int position) {
if (position < 0 || position >= conversations.size()) return; if (position < 0 || position >= conversations.size()) return;
// 获取要删除的会话的未读数量
ConversationItem itemToDelete = conversations.get(position);
int unreadCountToRemove = itemToDelete != null ? itemToDelete.getUnreadCount() : 0;
// 删除会话
conversations.remove(position); conversations.remove(position);
if (conversationsAdapter != null) { if (conversationsAdapter != null) {
conversationsAdapter.submitList(new ArrayList<>(conversations)); conversationsAdapter.submitList(new ArrayList<>(conversations));
} }
// 更新总未读数量减少被删除会话的未读数量
if (unreadCountToRemove > 0) {
UnreadMessageManager.decrementUnreadCount(this, unreadCountToRemove);
// 更新底部导航栏徽章
if (binding != null) {
UnreadMessageManager.updateBadge(binding.bottomNavInclude.bottomNavigation);
}
}
} }
private float dp(float value) { private float dp(float value) {
@ -304,7 +398,65 @@ public class MessagesActivity extends AppCompatActivity {
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
if (binding != null) { if (binding != null) {
binding.bottomNavInclude.bottomNavigation.setSelectedItemId(R.id.nav_messages); BottomNavigationView bottomNav = binding.bottomNavInclude.bottomNavigation;
bottomNav.setSelectedItemId(R.id.nav_messages);
// 用户进入消息页面时计算并更新总未读数量
int totalUnread = calculateTotalUnreadCount();
UnreadMessageManager.setUnreadCount(this, totalUnread);
UnreadMessageManager.updateBadge(bottomNav);
// 刷新列表以更新未读数量显示
if (conversationsAdapter != null) {
conversationsAdapter.submitList(new ArrayList<>(conversations));
}
}
}
/**
* 计算总未读消息数量
*/
private int calculateTotalUnreadCount() {
int total = 0;
for (ConversationItem item : conversations) {
if (item != null) {
total += item.getUnreadCount();
}
}
return total;
}
/**
* 更新会话的未读数量
*/
private void updateConversationUnreadCount(String conversationId, int newUnreadCount) {
for (int i = 0; i < conversations.size(); i++) {
ConversationItem item = conversations.get(i);
if (item != null && item.getId().equals(conversationId)) {
// 创建新的 ConversationItem更新未读数量
conversations.set(i, new ConversationItem(
item.getId(),
item.getTitle(),
item.getLastMessage(),
item.getTimeText(),
newUnreadCount,
item.isMuted()
));
break;
}
}
// 通知适配器更新列表显示
if (conversationsAdapter != null) {
conversationsAdapter.submitList(new ArrayList<>(conversations));
}
// 更新总未读数量
int totalUnread = calculateTotalUnreadCount();
UnreadMessageManager.setUnreadCount(this, totalUnread);
// 更新底部导航栏徽章
if (binding != null) {
UnreadMessageManager.updateBadge(binding.bottomNavInclude.bottomNavigation);
} }
} }
} }

View File

@ -5,7 +5,6 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.recyclerview.widget.DiffUtil; import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter; import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -50,37 +49,25 @@ public class NearbyUsersAdapter extends ListAdapter<NearbyUser, NearbyUsersAdapt
void bind(NearbyUser user) { void bind(NearbyUser user) {
binding.userName.setText(user != null && user.getName() != null ? user.getName() : ""); 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()); String distanceText = user != null && user.getDistanceText() != null ? user.getDistanceText() : "";
int h = Math.abs(seed.hashCode()); if (!distanceText.isEmpty() && !distanceText.startsWith("距离")) {
int mode = h % 3; binding.distanceText.setText("距离 " + distanceText);
String ratio;
if (mode == 0) {
ratio = "1:1";
} else if (mode == 1) {
ratio = "3:4";
} else { } else {
ratio = "4:3"; binding.distanceText.setText(distanceText);
}
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.addButton.setOnClickListener(v -> {
if (user == null) return;
if (onUserClickListener != null) onUserClickListener.onUserClick(user);
});
// 整个item点击也可以触发添加
binding.getRoot().setOnClickListener(v -> { binding.getRoot().setOnClickListener(v -> {
if (user == null) return; if (user == null) return;
if (onUserClickListener != null) onUserClickListener.onUserClick(user); if (onUserClickListener != null) onUserClickListener.onUserClick(user);

View File

@ -0,0 +1,61 @@
package com.example.livestreaming;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import com.example.livestreaming.databinding.ActivityOnlineDatingBinding;
import com.google.android.material.bottomnavigation.BottomNavigationView;
public class OnlineDatingActivity extends AppCompatActivity {
private ActivityOnlineDatingBinding binding;
public static void start(Context context) {
Intent intent = new Intent(context, OnlineDatingActivity.class);
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityOnlineDatingBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setupUI();
}
private void setupUI() {
binding.backButton.setOnClickListener(v -> finish());
BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation;
bottomNavigation.setSelectedItemId(R.id.nav_friends);
bottomNavigation.setOnItemSelectedListener(item -> {
int id = item.getItemId();
if (id == R.id.nav_home) {
startActivity(new Intent(this, MainActivity.class));
finish();
return true;
}
if (id == R.id.nav_wish_tree) {
WishTreeActivity.start(this);
finish();
return true;
}
if (id == R.id.nav_messages) {
MessagesActivity.start(this);
finish();
return true;
}
if (id == R.id.nav_profile) {
ProfileActivity.start(this);
finish();
return true;
}
return true;
});
}
}

View File

@ -0,0 +1,61 @@
package com.example.livestreaming;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import com.example.livestreaming.databinding.ActivityPeaceEliteBinding;
import com.google.android.material.bottomnavigation.BottomNavigationView;
public class PeaceEliteActivity extends AppCompatActivity {
private ActivityPeaceEliteBinding binding;
public static void start(Context context) {
Intent intent = new Intent(context, PeaceEliteActivity.class);
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityPeaceEliteBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setupUI();
}
private void setupUI() {
binding.backButton.setOnClickListener(v -> finish());
BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation;
bottomNavigation.setSelectedItemId(R.id.nav_friends);
bottomNavigation.setOnItemSelectedListener(item -> {
int id = item.getItemId();
if (id == R.id.nav_home) {
startActivity(new Intent(this, MainActivity.class));
finish();
return true;
}
if (id == R.id.nav_wish_tree) {
WishTreeActivity.start(this);
finish();
return true;
}
if (id == R.id.nav_messages) {
MessagesActivity.start(this);
finish();
return true;
}
if (id == R.id.nav_profile) {
ProfileActivity.start(this);
finish();
return true;
}
return true;
});
}
}

View File

@ -4,6 +4,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.ClipData; import android.content.ClipData;
import android.content.ClipboardManager; import android.content.ClipboardManager;
import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.net.Uri; import android.net.Uri;
import android.text.TextUtils; import android.text.TextUtils;
@ -11,6 +12,9 @@ import android.view.View;
import android.widget.EditText; import android.widget.EditText;
import android.widget.Toast; import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
@ -18,6 +22,12 @@ import com.bumptech.glide.Glide;
import com.example.livestreaming.databinding.ActivityProfileBinding; import com.example.livestreaming.databinding.ActivityProfileBinding;
import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.bottomnavigation.BottomNavigationView;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
public class ProfileActivity extends AppCompatActivity { public class ProfileActivity extends AppCompatActivity {
private ActivityProfileBinding binding; private ActivityProfileBinding binding;
@ -36,6 +46,8 @@ public class ProfileActivity extends AppCompatActivity {
private static final String BIO_HINT_TEXT = "填写个人签名更容易获得关注,点击此处添加"; private static final String BIO_HINT_TEXT = "填写个人签名更容易获得关注,点击此处添加";
private ActivityResultLauncher<Intent> editProfileLauncher;
public static void start(Context context) { public static void start(Context context) {
Intent intent = new Intent(context, ProfileActivity.class); Intent intent = new Intent(context, ProfileActivity.class);
context.startActivity(intent); context.startActivity(intent);
@ -47,13 +59,31 @@ public class ProfileActivity extends AppCompatActivity {
binding = ActivityProfileBinding.inflate(getLayoutInflater()); binding = ActivityProfileBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
// 注册编辑资料页面的结果监听
editProfileLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
// 当从EditProfileActivity返回时立即刷新所有数据
loadProfileFromPrefs(); loadProfileFromPrefs();
loadAndDisplayTags();
loadProfileInfo();
}
);
loadProfileFromPrefs();
loadAndDisplayTags();
loadProfileInfo();
setupEditableAreas(); setupEditableAreas();
setupAvatarClick();
setupNavigationClicks(); setupNavigationClicks();
setupProfileTabs(); setupProfileTabs();
BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation; BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation;
bottomNavigation.setSelectedItemId(R.id.nav_profile); bottomNavigation.setSelectedItemId(R.id.nav_profile);
// 更新未读消息徽章
UnreadMessageManager.updateBadge(bottomNavigation);
bottomNavigation.setOnItemSelectedListener(item -> { bottomNavigation.setOnItemSelectedListener(item -> {
int id = item.getItemId(); int id = item.getItemId();
if (id == R.id.nav_home) { if (id == R.id.nav_home) {
@ -140,6 +170,32 @@ public class ProfileActivity extends AppCompatActivity {
} }
private void setupAvatarClick() {
binding.avatar.setOnClickListener(v -> {
AvatarViewerDialog dialog = AvatarViewerDialog.create(this);
// 优先从SharedPreferences读取最新的头像信息因为ImageView可能还在加载中
String avatarUri = getSharedPreferences(PREFS_NAME, MODE_PRIVATE).getString(KEY_AVATAR_URI, null);
int avatarRes = getSharedPreferences(PREFS_NAME, MODE_PRIVATE).getInt(KEY_AVATAR_RES, 0);
if (!TextUtils.isEmpty(avatarUri)) {
// 使用URI加载确保能正确显示
dialog.setAvatarUri(Uri.parse(avatarUri));
} else if (avatarRes != 0) {
dialog.setAvatarResId(avatarRes);
} else {
// 如果都没有尝试从ImageView获取Drawable
Drawable drawable = binding.avatar.getDrawable();
if (drawable != null) {
dialog.setAvatarDrawable(drawable);
} else {
dialog.setAvatarResId(R.drawable.ic_account_circle_24);
}
}
dialog.show();
});
}
private void setupNavigationClicks() { private void setupNavigationClicks() {
binding.topActionSearch.setOnClickListener(v -> TabPlaceholderActivity.start(this, "定位/发现")); binding.topActionSearch.setOnClickListener(v -> TabPlaceholderActivity.start(this, "定位/发现"));
binding.topActionClock.setOnClickListener(v -> WatchHistoryActivity.start(this)); binding.topActionClock.setOnClickListener(v -> WatchHistoryActivity.start(this));
@ -161,13 +217,14 @@ public class ProfileActivity extends AppCompatActivity {
binding.followers.setOnClickListener(v -> FansListActivity.start(this)); binding.followers.setOnClickListener(v -> FansListActivity.start(this));
binding.likes.setOnClickListener(v -> LikesListActivity.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.action1.setOnClickListener(v -> TabPlaceholderActivity.start(this, "公园勋章"));
binding.action2.setOnClickListener(v -> WatchHistoryActivity.start(this)); binding.action2.setOnClickListener(v -> WatchHistoryActivity.start(this));
binding.action3.setOnClickListener(v -> startActivity(new Intent(this, MyFriendsActivity.class))); binding.action3.setOnClickListener(v -> startActivity(new Intent(this, MyFriendsActivity.class)));
binding.editProfile.setOnClickListener(v -> EditProfileActivity.start(this)); binding.editProfile.setOnClickListener(v -> {
Intent intent = new Intent(this, EditProfileActivity.class);
editProfileLauncher.launch(intent);
});
binding.shareHome.setOnClickListener(v -> { binding.shareHome.setOnClickListener(v -> {
// TabPlaceholderActivity.start(this, "分享主页"); // TabPlaceholderActivity.start(this, "分享主页");
String idText = binding.idLine.getText() != null ? binding.idLine.getText().toString() : ""; String idText = binding.idLine.getText() != null ? binding.idLine.getText().toString() : "";
@ -209,14 +266,18 @@ public class ProfileActivity extends AppCompatActivity {
binding.worksPublishBtn.setOnClickListener(v -> Toast.makeText(this, "发布功能待接入", Toast.LENGTH_SHORT).show()); binding.worksPublishBtn.setOnClickListener(v -> Toast.makeText(this, "发布功能待接入", Toast.LENGTH_SHORT).show());
binding.likedGoBrowseBtn.setOnClickListener(v -> startActivity(new Intent(this, MainActivity.class))); binding.likedGoBrowseBtn.setOnClickListener(v -> startActivity(new Intent(this, MainActivity.class)));
binding.favGoBrowseBtn.setOnClickListener(v -> startActivity(new Intent(this, MainActivity.class))); binding.favGoBrowseBtn.setOnClickListener(v -> startActivity(new Intent(this, MainActivity.class)));
binding.profileEditFromTab.setOnClickListener(v -> EditProfileActivity.start(this)); binding.profileEditFromTab.setOnClickListener(v -> {
Intent intent = new Intent(this, EditProfileActivity.class);
editProfileLauncher.launch(intent);
});
} }
private void showTab(int index) { private void showTab(int index) {
// 标签页顺序0-作品, 1-收藏, 2-赞过
binding.tabWorks.setVisibility(index == 0 ? View.VISIBLE : View.GONE); binding.tabWorks.setVisibility(index == 0 ? View.VISIBLE : View.GONE);
binding.tabLiked.setVisibility(index == 1 ? View.VISIBLE : View.GONE); binding.tabFavorites.setVisibility(index == 1 ? View.VISIBLE : View.GONE);
binding.tabFavorites.setVisibility(index == 2 ? View.VISIBLE : View.GONE); binding.tabLiked.setVisibility(index == 2 ? View.VISIBLE : View.GONE);
binding.tabProfile.setVisibility(index == 3 ? View.VISIBLE : View.GONE); // "资料"标签页已移除
} }
private interface OnTextSaved { private interface OnTextSaved {
@ -251,7 +312,153 @@ public class ProfileActivity extends AppCompatActivity {
super.onResume(); super.onResume();
if (binding != null) { if (binding != null) {
loadProfileFromPrefs(); loadProfileFromPrefs();
binding.bottomNavInclude.bottomNavigation.setSelectedItemId(R.id.nav_profile); loadAndDisplayTags();
loadProfileInfo();
BottomNavigationView bottomNav = binding.bottomNavInclude.bottomNavigation;
bottomNav.setSelectedItemId(R.id.nav_profile);
// 更新未读消息徽章
UnreadMessageManager.updateBadge(bottomNav);
}
}
private void loadAndDisplayTags() {
String location = getSharedPreferences(PREFS_NAME, MODE_PRIVATE).getString(KEY_LOCATION, "");
String gender = getSharedPreferences(PREFS_NAME, MODE_PRIVATE).getString(KEY_GENDER, "");
String birthday = getSharedPreferences(PREFS_NAME, MODE_PRIVATE).getString(KEY_BIRTHDAY, "");
// 设置所在地标签 - 支持"省份-城市"格式
if (!TextUtils.isEmpty(location)) {
// "省份-城市"格式转换为"省份·城市"显示
String displayLocation = location.replace("-", "·");
binding.tagLocation.setText("IP" + displayLocation);
binding.tagLocation.setVisibility(View.VISIBLE);
} else {
binding.tagLocation.setText("IP广西");
binding.tagLocation.setVisibility(View.VISIBLE);
}
// 设置性别标签
if (!TextUtils.isEmpty(gender)) {
if (gender.contains("")) {
binding.tagGender.setText("");
} else if (gender.contains("")) {
binding.tagGender.setText("");
} else {
binding.tagGender.setText("H");
}
binding.tagGender.setVisibility(View.VISIBLE);
} else {
binding.tagGender.setText("H");
binding.tagGender.setVisibility(View.VISIBLE);
}
// 计算并设置年龄标签
if (!TextUtils.isEmpty(birthday)) {
int age = calculateAge(birthday);
if (age > 0) {
binding.tagAge.setText(age + "");
binding.tagAge.setVisibility(View.VISIBLE);
} else {
binding.tagAge.setVisibility(View.GONE);
}
} else {
binding.tagAge.setVisibility(View.GONE);
}
// 计算并设置星座标签
if (!TextUtils.isEmpty(birthday)) {
String constellation = calculateConstellation(birthday);
if (!TextUtils.isEmpty(constellation)) {
binding.tagConstellation.setText(constellation);
binding.tagConstellation.setVisibility(View.VISIBLE);
} else {
binding.tagConstellation.setVisibility(View.GONE);
}
} else {
binding.tagConstellation.setVisibility(View.GONE);
}
}
private void loadProfileInfo() {
String name = getSharedPreferences(PREFS_NAME, MODE_PRIVATE).getString(KEY_NAME, "爱你");
String location = getSharedPreferences(PREFS_NAME, MODE_PRIVATE).getString(KEY_LOCATION, "广西");
String bio = getSharedPreferences(PREFS_NAME, MODE_PRIVATE).getString(KEY_BIO, null);
if (binding.profileInfoLine1 != null) {
binding.profileInfoLine1.setText("昵称:" + name);
}
if (binding.profileInfoLine2 != null) {
String locationText = !TextUtils.isEmpty(location) ? location : "广西";
binding.profileInfoLine2.setText("地区:" + locationText);
}
if (binding.profileInfoLine3 != null) {
String bioText = (!TextUtils.isEmpty(bio) && !BIO_HINT_TEXT.equals(bio)) ? bio : "填写个人签名更容易获得关注";
binding.profileInfoLine3.setText("签名:" + bioText);
}
}
private int calculateAge(String birthdayStr) {
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
Date birthDate = sdf.parse(birthdayStr);
if (birthDate == null) return 0;
Calendar birth = Calendar.getInstance();
birth.setTime(birthDate);
Calendar now = Calendar.getInstance();
int age = now.get(Calendar.YEAR) - birth.get(Calendar.YEAR);
if (now.get(Calendar.DAY_OF_YEAR) < birth.get(Calendar.DAY_OF_YEAR)) {
age--;
}
return age;
} catch (ParseException e) {
return 0;
}
}
private String calculateConstellation(String birthdayStr) {
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
Date birthDate = sdf.parse(birthdayStr);
if (birthDate == null) return "";
Calendar cal = Calendar.getInstance();
cal.setTime(birthDate);
int month = cal.get(Calendar.MONTH) + 1; // Calendar.MONTH 从0开始
int day = cal.get(Calendar.DAY_OF_MONTH);
// 星座计算
if ((month == 3 && day >= 21) || (month == 4 && day <= 19)) {
return "白羊座";
} else if ((month == 4 && day >= 20) || (month == 5 && day <= 20)) {
return "金牛座";
} else if ((month == 5 && day >= 21) || (month == 6 && day <= 21)) {
return "双子座";
} else if ((month == 6 && day >= 22) || (month == 7 && day <= 22)) {
return "巨蟹座";
} else if ((month == 7 && day >= 23) || (month == 8 && day <= 22)) {
return "狮子座";
} else if ((month == 8 && day >= 23) || (month == 9 && day <= 22)) {
return "处女座";
} else if ((month == 9 && day >= 23) || (month == 10 && day <= 23)) {
return "天秤座";
} else if ((month == 10 && day >= 24) || (month == 11 && day <= 22)) {
return "天蝎座";
} else if ((month == 11 && day >= 23) || (month == 12 && day <= 21)) {
return "射手座";
} else if ((month == 12 && day >= 22) || (month == 1 && day <= 19)) {
return "摩羯座";
} else if ((month == 1 && day >= 20) || (month == 2 && day <= 18)) {
return "水瓶座";
} else if ((month == 2 && day >= 19) || (month == 3 && day <= 20)) {
return "双鱼座";
}
return "";
} catch (ParseException e) {
return "";
} }
} }
} }

View File

@ -23,8 +23,17 @@ public class SearchActivity extends AppCompatActivity {
private final List<Room> all = new ArrayList<>(); private final List<Room> all = new ArrayList<>();
private static final String EXTRA_SEARCH_QUERY = "search_query";
public static void start(Context context) { public static void start(Context context) {
start(context, null);
}
public static void start(Context context, String searchQuery) {
Intent intent = new Intent(context, SearchActivity.class); Intent intent = new Intent(context, SearchActivity.class);
if (searchQuery != null && !searchQuery.trim().isEmpty()) {
intent.putExtra(EXTRA_SEARCH_QUERY, searchQuery);
}
context.startActivity(intent); context.startActivity(intent);
} }
@ -40,6 +49,13 @@ public class SearchActivity extends AppCompatActivity {
binding.backButton.setOnClickListener(v -> finish()); binding.backButton.setOnClickListener(v -> finish());
binding.cancelBtn.setOnClickListener(v -> finish()); binding.cancelBtn.setOnClickListener(v -> finish());
// 如果从Intent中获取到搜索关键词自动填充到搜索框
String searchQuery = getIntent().getStringExtra(EXTRA_SEARCH_QUERY);
if (searchQuery != null && !searchQuery.trim().isEmpty()) {
binding.searchInput.setText(searchQuery);
binding.searchInput.setSelection(searchQuery.length());
}
binding.searchInput.requestFocus(); binding.searchInput.requestFocus();
} }

View File

@ -232,9 +232,8 @@ public class TabPlaceholderActivity extends AppCompatActivity {
Toast.makeText(this, "点击:" + user.getName(), Toast.LENGTH_SHORT).show(); Toast.makeText(this, "点击:" + user.getName(), Toast.LENGTH_SHORT).show();
}); });
StaggeredGridLayoutManager glm = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); LinearLayoutManager layoutManager = new LinearLayoutManager(this);
glm.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS); binding.nearbyRecyclerView.setLayoutManager(layoutManager);
binding.nearbyRecyclerView.setLayoutManager(glm);
binding.nearbyRecyclerView.setAdapter(adapter); binding.nearbyRecyclerView.setAdapter(adapter);
adapter.submitList(buildNearbyDemoUsers(18)); adapter.submitList(buildNearbyDemoUsers(18));
@ -254,10 +253,12 @@ public class TabPlaceholderActivity extends AppCompatActivity {
private List<NearbyUser> buildNearbyDemoUsers(int count) { private List<NearbyUser> buildNearbyDemoUsers(int count) {
List<NearbyUser> list = new ArrayList<>(); List<NearbyUser> list = new ArrayList<>();
String[] names = {"小王", "小李", "安安", "小陈", "小美", "老张", "小七", "阿杰",
"小雨", "阿宁", "小星", "小林", "小杨", "小刘", "小赵", "小孙", "小周", "小吴"};
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
String id = "nearby-" + i; String id = "user-" + i;
String name = "附近主播" + (i + 1); String name = i < names.length ? names[i] : "用户" + (i + 1);
boolean live = i % 3 == 0; boolean live = false; // 不再显示直播状态
String distanceText; String distanceText;
if (i < 3) { if (i < 3) {
distanceText = (300 + i * 120) + "m"; distanceText = (300 + i * 120) + "m";
@ -491,9 +492,7 @@ public class TabPlaceholderActivity extends AppCompatActivity {
Toast.makeText(this, "已发送好友请求:" + user.getName(), Toast.LENGTH_SHORT).show(); Toast.makeText(this, "已发送好友请求:" + user.getName(), Toast.LENGTH_SHORT).show();
}); });
StaggeredGridLayoutManager glm = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); binding.addFriendRecyclerView.setLayoutManager(new LinearLayoutManager(this));
glm.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS);
binding.addFriendRecyclerView.setLayoutManager(glm);
binding.addFriendRecyclerView.setAdapter(addFriendAdapter); binding.addFriendRecyclerView.setAdapter(addFriendAdapter);
addFriendAllUsers.clear(); addFriendAllUsers.clear();

View File

@ -0,0 +1,61 @@
package com.example.livestreaming;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import com.example.livestreaming.databinding.ActivityTableGamesBinding;
import com.google.android.material.bottomnavigation.BottomNavigationView;
public class TableGamesActivity extends AppCompatActivity {
private ActivityTableGamesBinding binding;
public static void start(Context context) {
Intent intent = new Intent(context, TableGamesActivity.class);
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityTableGamesBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setupUI();
}
private void setupUI() {
binding.backButton.setOnClickListener(v -> finish());
BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation;
bottomNavigation.setSelectedItemId(R.id.nav_friends);
bottomNavigation.setOnItemSelectedListener(item -> {
int id = item.getItemId();
if (id == R.id.nav_home) {
startActivity(new Intent(this, MainActivity.class));
finish();
return true;
}
if (id == R.id.nav_wish_tree) {
WishTreeActivity.start(this);
finish();
return true;
}
if (id == R.id.nav_messages) {
MessagesActivity.start(this);
finish();
return true;
}
if (id == R.id.nav_profile) {
ProfileActivity.start(this);
finish();
return true;
}
return true;
});
}
}

View File

@ -0,0 +1,74 @@
package com.example.livestreaming;
import android.content.Context;
import android.content.SharedPreferences;
import com.google.android.material.badge.BadgeDrawable;
import com.google.android.material.bottomnavigation.BottomNavigationView;
public class UnreadMessageManager {
private static final String PREFS_NAME = "unread_messages";
private static final String KEY_UNREAD_COUNT = "unread_count";
/**
* 获取总未读消息数量
*/
public static int getUnreadCount(Context context) {
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
return prefs.getInt(KEY_UNREAD_COUNT, 0);
}
/**
* 设置总未读消息数量
*/
public static void setUnreadCount(Context context, int count) {
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
prefs.edit().putInt(KEY_UNREAD_COUNT, Math.max(0, count)).apply();
}
/**
* 增加未读消息数量
*/
public static void incrementUnreadCount(Context context, int increment) {
int current = getUnreadCount(context);
setUnreadCount(context, current + increment);
}
/**
* 减少未读消息数量
*/
public static void decrementUnreadCount(Context context, int decrement) {
int current = getUnreadCount(context);
setUnreadCount(context, Math.max(0, current - decrement));
}
/**
* 更新底部导航栏的消息徽章
*/
public static void updateBadge(BottomNavigationView bottomNav) {
if (bottomNav == null) return;
Context context = bottomNav.getContext();
int unreadCount = getUnreadCount(context);
BadgeDrawable badge = bottomNav.getOrCreateBadge(R.id.nav_messages);
if (unreadCount > 0) {
badge.setVisible(true);
badge.setNumber(unreadCount);
// BadgeDrawable 会自动处理超过 99 的情况显示 "99+"
} else {
badge.setVisible(false);
badge.clearNumber();
}
}
/**
* 清除未读消息数量当用户进入消息页面时
*/
public static void clearUnreadCount(Context context) {
setUnreadCount(context, 0);
}
}

View File

@ -58,6 +58,13 @@ public class UserProfileReadOnlyActivity extends AppCompatActivity {
binding.bio.setText(bio != null ? bio : ""); binding.bio.setText(bio != null ? bio : "");
binding.avatar.setImageResource(avatarRes); binding.avatar.setImageResource(avatarRes);
// 设置头像点击放大功能
binding.avatar.setOnClickListener(v -> {
AvatarViewerDialog dialog = AvatarViewerDialog.create(this);
dialog.setAvatarResId(avatarRes);
dialog.show();
});
setupTabsAndWorks(); setupTabsAndWorks();
bindDemoStatsAndWorks(userId); bindDemoStatsAndWorks(userId);
@ -86,8 +93,8 @@ public class UserProfileReadOnlyActivity extends AppCompatActivity {
if (binding.tabs.getTabCount() == 0) { if (binding.tabs.getTabCount() == 0) {
binding.tabs.addTab(binding.tabs.newTab().setText("作品")); binding.tabs.addTab(binding.tabs.newTab().setText("作品"));
binding.tabs.addTab(binding.tabs.newTab().setText("动态")); binding.tabs.addTab(binding.tabs.newTab().setText("收藏"));
binding.tabs.addTab(binding.tabs.newTab().setText("资料")); binding.tabs.addTab(binding.tabs.newTab().setText("赞过"));
} }
if (worksAdapter == null) { if (worksAdapter == null) {
@ -119,9 +126,10 @@ public class UserProfileReadOnlyActivity extends AppCompatActivity {
private void showTab(int index) { private void showTab(int index) {
if (binding == null) return; if (binding == null) return;
// 标签页顺序0-作品, 1-收藏, 2-赞过
binding.worksRecycler.setVisibility(index == 0 ? android.view.View.VISIBLE : android.view.View.GONE); binding.worksRecycler.setVisibility(index == 0 ? android.view.View.VISIBLE : android.view.View.GONE);
binding.dynamicsPlaceholder.setVisibility(index == 1 ? android.view.View.VISIBLE : android.view.View.GONE); binding.favoritesPlaceholder.setVisibility(index == 1 ? android.view.View.VISIBLE : android.view.View.GONE);
binding.profilePlaceholder.setVisibility(index == 2 ? android.view.View.VISIBLE : android.view.View.GONE); binding.likedPlaceholder.setVisibility(index == 2 ? android.view.View.VISIBLE : android.view.View.GONE);
} }
private void bindDemoStatsAndWorks(String userId) { private void bindDemoStatsAndWorks(String userId) {

View File

@ -0,0 +1,62 @@
package com.example.livestreaming;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
import com.example.livestreaming.databinding.ActivityVoiceMatchBinding;
import com.google.android.material.bottomnavigation.BottomNavigationView;
public class VoiceMatchActivity extends AppCompatActivity {
private ActivityVoiceMatchBinding binding;
public static void start(Context context) {
Intent intent = new Intent(context, VoiceMatchActivity.class);
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityVoiceMatchBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setupUI();
}
private void setupUI() {
binding.backButton.setOnClickListener(v -> finish());
BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation;
bottomNavigation.setSelectedItemId(R.id.nav_friends);
bottomNavigation.setOnItemSelectedListener(item -> {
int id = item.getItemId();
if (id == R.id.nav_home) {
startActivity(new Intent(this, MainActivity.class));
finish();
return true;
}
if (id == R.id.nav_wish_tree) {
WishTreeActivity.start(this);
finish();
return true;
}
if (id == R.id.nav_messages) {
MessagesActivity.start(this);
finish();
return true;
}
if (id == R.id.nav_profile) {
ProfileActivity.start(this);
finish();
return true;
}
return true;
});
}
}

View File

@ -6,7 +6,7 @@
android:viewportHeight="24"> android:viewportHeight="24">
<path <path
android:fillColor="#FFFFFF" android:fillColor="#777777"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM4.07,13h3.02c0.13,2.03 0.8,3.94 1.83,5.41C6.49,17.38 4.69,15.36 4.07,13zM7.09,11H4.07c0.62,-2.36 2.42,-4.38 4.85,-5.41C7.89,7.06 7.22,8.97 7.09,11zM11,19.93c-1.75,-0.31 -3.26,-2.62 -3.46,-5.93H11v5.93zM11,12H7.54C7.74,8.69 9.25,6.38 11,6.07V12zM19.93,11h-3.02c-0.13,-2.03 -0.8,-3.94 -1.83,-5.41C17.51,6.62 19.31,8.64 19.93,11zM13,6.07c1.75,0.31 3.26,2.62 3.46,5.93H13V6.07zM13,19.93V14h3.46c-0.2,3.31 -1.71,5.62 -3.46,5.93zM15.08,18.41c1.03,-1.47 1.7,-3.38 1.83,-5.41h3.02c-0.62,2.36 -2.42,4.38 -4.85,5.41z"/> android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM4.07,13h3.02c0.13,2.03 0.8,3.94 1.83,5.41C6.49,17.38 4.69,15.36 4.07,13zM7.09,11H4.07c0.62,-2.36 2.42,-4.38 4.85,-5.41C7.89,7.06 7.22,8.97 7.09,11zM11,19.93c-1.75,-0.31 -3.26,-2.62 -3.46,-5.93H11v5.93zM11,12H7.54C7.74,8.69 9.25,6.38 11,6.07V12zM19.93,11h-3.02c-0.13,-2.03 -0.8,-3.94 -1.83,-5.41C17.51,6.62 19.31,8.64 19.93,11zM13,6.07c1.75,0.31 3.26,2.62 3.46,5.93H13V6.07zM13,19.93V14h3.46c-0.2,3.31 -1.71,5.62 -3.46,5.93zM15.08,18.41c1.03,-1.47 1.7,-3.38 1.83,-5.41h3.02c-0.62,2.36 -2.42,4.38 -4.85,5.41z"/>
</vector> </vector>

View File

@ -0,0 +1,56 @@
<?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"
android:background="#FFFFFF">
<ImageView
android:id="@+id/backButton"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:contentDescription="返回"
android:src="@drawable/ic_arrow_back_24"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/titleText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="你画我猜"
android:textColor="#111111"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/backButton" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:layout_marginTop="32dp"
android:gravity="center"
android:text="发挥你的想象力,画出你的创意"
android:textColor="#666666"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/titleText" />
<include
android:id="@+id/bottomNavInclude"
layout="@layout/include_bottom_nav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -186,7 +186,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:hint="生日(例如 2001-08-08" android:hint="生日"
app:boxBackgroundMode="filled" app:boxBackgroundMode="filled"
app:boxBackgroundColor="@android:color/white" app:boxBackgroundColor="@android:color/white"
app:boxCornerRadiusTopStart="14dp" app:boxCornerRadiusTopStart="14dp"
@ -202,7 +202,13 @@
android:id="@+id/inputBirthday" android:id="@+id/inputBirthday"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:maxLines="1" /> android:maxLines="1"
android:focusable="false"
android:focusableInTouchMode="false"
android:clickable="true"
android:cursorVisible="false"
android:enabled="true"
android:inputType="none" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
@ -211,7 +217,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:hint="性别(男/女/保密)" android:hint="性别"
app:boxBackgroundMode="filled" app:boxBackgroundMode="filled"
app:boxBackgroundColor="@android:color/white" app:boxBackgroundColor="@android:color/white"
app:boxCornerRadiusTopStart="14dp" app:boxCornerRadiusTopStart="14dp"
@ -227,7 +233,10 @@
android:id="@+id/inputGender" android:id="@+id/inputGender"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:maxLines="1" /> android:maxLines="1"
android:focusable="false"
android:clickable="true"
android:cursorVisible="false" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
@ -236,7 +245,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:hint="所在地(例如 北京·朝阳)" android:hint="所在地"
app:boxBackgroundMode="filled" app:boxBackgroundMode="filled"
app:boxBackgroundColor="@android:color/white" app:boxBackgroundColor="@android:color/white"
app:boxCornerRadiusTopStart="14dp" app:boxCornerRadiusTopStart="14dp"
@ -252,7 +261,10 @@
android:id="@+id/inputLocation" android:id="@+id/inputLocation"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:maxLines="1" /> android:maxLines="1"
android:focusable="false"
android:clickable="true"
android:cursorVisible="false" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>

View File

@ -0,0 +1,56 @@
<?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"
android:background="#FFFFFF">
<ImageView
android:id="@+id/backButton"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:contentDescription="返回"
android:src="@drawable/ic_arrow_back_24"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/titleText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="找人玩游戏"
android:textColor="#111111"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/backButton" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:layout_marginTop="32dp"
android:gravity="center"
android:text="找到志同道合的游戏伙伴,一起开黑"
android:textColor="#666666"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/titleText" />
<include
android:id="@+id/bottomNavInclude"
layout="@layout/include_bottom_nav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -4,7 +4,9 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@drawable/bg_fishpond_gradient" android:background="@drawable/bg_fishpond_gradient"
android:paddingTop="18dp"> android:paddingTop="18dp"
android:clipChildren="false"
android:clipToPadding="false">
<ImageView <ImageView
android:id="@+id/titleText" android:id="@+id/titleText"
@ -444,6 +446,7 @@
</LinearLayout> </LinearLayout>
<!--
<ImageView <ImageView
android:id="@+id/floatingMenu" android:id="@+id/floatingMenu"
android:layout_width="44dp" android:layout_width="44dp"
@ -455,6 +458,7 @@
android:src="@drawable/ic_grid_24" android:src="@drawable/ic_grid_24"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" /> app:layout_constraintEnd_toEndOf="parent" />
-->
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
@ -472,7 +476,7 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/orbitContainer" app:layout_constraintTop_toBottomOf="@id/orbitContainer"
app:layout_constraintBottom_toTopOf="@id/bottomAppBar"> app:layout_constraintBottom_toTopOf="@id/bottomNavInclude">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -486,7 +490,6 @@
android:background="@drawable/bg_pill_left" android:background="@drawable/bg_pill_left"
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
android:foreground="?attr/selectableItemBackground"
android:paddingStart="14dp" android:paddingStart="14dp"
android:paddingEnd="14dp" android:paddingEnd="14dp"
app:layout_constraintEnd_toStartOf="@id/actionRight" app:layout_constraintEnd_toStartOf="@id/actionRight"
@ -526,7 +529,6 @@
android:background="@drawable/bg_pill_right" android:background="@drawable/bg_pill_right"
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
android:foreground="?attr/selectableItemBackground"
android:paddingStart="14dp" android:paddingStart="14dp"
android:paddingEnd="14dp" android:paddingEnd="14dp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"

View File

@ -0,0 +1,56 @@
<?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"
android:background="#FFF2F7">
<ImageView
android:id="@+id/backButton"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:contentDescription="返回"
android:src="@drawable/ic_arrow_back_24"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/titleText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="心动信号"
android:textColor="#111111"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/backButton" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:layout_marginTop="32dp"
android:gravity="center"
android:text="发现让你心动的那个TA"
android:textColor="#666666"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/titleText" />
<include
android:id="@+id/bottomNavInclude"
layout="@layout/include_bottom_nav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,56 @@
<?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"
android:background="#FFFFFF">
<ImageView
android:id="@+id/backButton"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:contentDescription="返回"
android:src="@drawable/ic_arrow_back_24"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/titleText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="一起KTV"
android:textColor="#111111"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/backButton" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:layout_marginTop="32dp"
android:gravity="center"
android:text="和好友一起K歌享受音乐时光"
android:textColor="#666666"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/titleText" />
<include
android:id="@+id/bottomNavInclude"
layout="@layout/include_bottom_nav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -7,7 +7,8 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:fitsSystemWindows="false">
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBar" android:id="@+id/appBar"
@ -39,9 +40,11 @@
android:background="@android:color/transparent" android:background="@android:color/transparent"
app:tabIndicatorColor="@color/purple_500" app:tabIndicatorColor="@color/purple_500"
app:tabIndicatorFullWidth="false" app:tabIndicatorFullWidth="false"
app:tabSelectedTextColor="@color/purple_500" app:tabIndicatorHeight="0dp"
app:tabSelectedTextColor="#666666"
app:tabTextColor="#666666" app:tabTextColor="#666666"
app:layout_constraintEnd_toStartOf="@id/notificationButton" app:tabRippleColor="@android:color/transparent"
app:layout_constraintEnd_toStartOf="@id/avatarButton"
app:layout_constraintStart_toEndOf="@id/menuButton" app:layout_constraintStart_toEndOf="@id/menuButton"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
@ -61,17 +64,6 @@
android:text="附近" /> android:text="附近" />
</com.google.android.material.tabs.TabLayout> </com.google.android.material.tabs.TabLayout>
<ImageView
android:id="@+id/notificationButton"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="14dp"
android:contentDescription="notification"
android:src="@drawable/ic_notifications_24"
app:layout_constraintBottom_toBottomOf="@id/topTabs"
app:layout_constraintEnd_toStartOf="@id/avatarButton"
app:layout_constraintTop_toTopOf="@id/topTabs" />
<ImageView <ImageView
android:id="@+id/avatarButton" android:id="@+id/avatarButton"
android:layout_width="28dp" android:layout_width="28dp"

View File

@ -0,0 +1,56 @@
<?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"
android:background="#FFFFFF">
<ImageView
android:id="@+id/backButton"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:contentDescription="返回"
android:src="@drawable/ic_arrow_back_24"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/titleText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="在线处对象"
android:textColor="#111111"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/backButton" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:layout_marginTop="32dp"
android:gravity="center"
android:text="寻找你的另一半,开启一段美好的缘分"
android:textColor="#666666"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/titleText" />
<include
android:id="@+id/bottomNavInclude"
layout="@layout/include_bottom_nav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,56 @@
<?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"
android:background="#FFFFFF">
<ImageView
android:id="@+id/backButton"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:contentDescription="返回"
android:src="@drawable/ic_arrow_back_24"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/titleText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="和平精英"
android:textColor="#111111"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/backButton" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:layout_marginTop="32dp"
android:gravity="center"
android:text="组队开黑,一起吃鸡"
android:textColor="#666666"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/titleText" />
<include
android:id="@+id/bottomNavInclude"
layout="@layout/include_bottom_nav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -167,13 +167,6 @@
android:src="@drawable/ic_copy_24" android:src="@drawable/ic_copy_24"
android:tint="#E6FFFFFF" /> android:tint="#E6FFFFFF" />
<ImageView
android:layout_width="18dp"
android:layout_height="18dp"
android:layout_marginStart="12dp"
android:src="@drawable/ic_grid_24"
android:tint="#E6FFFFFF" />
</LinearLayout> </LinearLayout>
<ImageView <ImageView
@ -228,7 +221,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
app:layout_constraintEnd_toStartOf="@id/recordBtn" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
@ -264,18 +257,6 @@
</LinearLayout> </LinearLayout>
<TextView
android:id="@+id/recordBtn"
android:layout_width="92dp"
android:layout_height="32dp"
android:background="@drawable/bg_gray_12"
android:gravity="center"
android:text="录制声音"
android:textColor="#111111"
android:textSize="12sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/statsRow" />
<TextView <TextView
android:id="@+id/bioText" android:id="@+id/bioText"
android:layout_width="0dp" android:layout_width="0dp"
@ -299,6 +280,7 @@
app:layout_constraintTop_toBottomOf="@id/bioText"> app:layout_constraintTop_toBottomOf="@id/bioText">
<TextView <TextView
android:id="@+id/tagLocation"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="24dp" android:layout_height="24dp"
android:background="@drawable/bg_gray_12" android:background="@drawable/bg_gray_12"
@ -310,6 +292,7 @@
android:textSize="11sp" /> android:textSize="11sp" />
<TextView <TextView
android:id="@+id/tagGender"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="24dp" android:layout_height="24dp"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
@ -322,6 +305,7 @@
android:textSize="11sp" /> android:textSize="11sp" />
<TextView <TextView
android:id="@+id/tagAge"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="24dp" android:layout_height="24dp"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
@ -334,6 +318,7 @@
android:textSize="11sp" /> android:textSize="11sp" />
<TextView <TextView
android:id="@+id/tagConstellation"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="24dp" android:layout_height="24dp"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
@ -346,6 +331,7 @@
android:textSize="11sp" /> android:textSize="11sp" />
<TextView <TextView
android:id="@+id/tagPersonality"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="24dp" android:layout_height="24dp"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
@ -601,11 +587,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="作品" /> android:text="作品" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="赞过" />
<com.google.android.material.tabs.TabItem <com.google.android.material.tabs.TabItem
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -614,7 +595,7 @@
<com.google.android.material.tabs.TabItem <com.google.android.material.tabs.TabItem
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="资料" /> android:text="赞过" />
</com.google.android.material.tabs.TabLayout> </com.google.android.material.tabs.TabLayout>

View File

@ -0,0 +1,56 @@
<?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"
android:background="#FFFFFF">
<ImageView
android:id="@+id/backButton"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:contentDescription="返回"
android:src="@drawable/ic_arrow_back_24"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/titleText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="桌子游"
android:textColor="#111111"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/backButton" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:layout_marginTop="32dp"
android:gravity="center"
android:text="经典桌游,欢乐无限"
android:textColor="#666666"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/titleText" />
<include
android:id="@+id/bottomNavInclude"
layout="@layout/include_bottom_nav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -18,20 +18,109 @@
android:paddingTop="14dp" android:paddingTop="14dp"
android:paddingBottom="16dp"> android:paddingBottom="16dp">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.04" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_end"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.96" />
<!-- 垂直方向的百分比参考线 (用于水平边距) -->
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_3pct_w"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.03" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_3_5pct_w"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.035" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_1pct_w"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.01" />
<!-- 水平方向的百分比参考线 (用于垂直边距) -->
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_2_5pct_h"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.025" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_3pct_h"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.03" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_0_8pct_h"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.008" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_2_8pct_h"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.028" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_2pct_h"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.02" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_1_7pct_h"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.017" />
<ImageView <ImageView
android:id="@+id/backButton" android:id="@+id/backButton"
android:layout_width="24dp" android:layout_width="0dp"
android:layout_height="24dp" android:layout_height="0dp"
android:contentDescription="back" android:contentDescription="back"
android:src="@drawable/ic_arrow_back_24" android:src="@drawable/ic_arrow_back_24"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintWidth_percent="0.06"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<Space
android:id="@+id/space_title_margin"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintWidth_percent="0.03"
app:layout_constraintStart_toEndOf="@id/backButton"
app:layout_constraintTop_toTopOf="@id/backButton"
app:layout_constraintBottom_toBottomOf="@id/backButton" />
<TextView <TextView
android:id="@+id/title" android:id="@+id/title"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="1" android:maxLines="1"
android:text="TA的主页" android:text="TA的主页"
@ -40,37 +129,71 @@
android:textStyle="bold" android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@id/backButton" app:layout_constraintBottom_toBottomOf="@id/backButton"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/backButton" app:layout_constraintStart_toEndOf="@id/space_title_margin"
app:layout_constraintTop_toTopOf="@id/backButton" /> app:layout_constraintTop_toTopOf="@id/backButton" />
<ImageView <Space
android:id="@+id/avatarRing" android:id="@+id/space_avatar_margin_top"
android:layout_width="96dp" android:layout_width="0dp"
android:layout_height="96dp" android:layout_height="0dp"
android:layout_marginTop="18dp" app:layout_constraintHeight_percent="0.025"
android:background="@drawable/bg_avatar_ring_pink"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/backButton" /> app:layout_constraintTop_toBottomOf="@id/backButton" />
<ImageView
android:id="@+id/avatarRing"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@drawable/bg_avatar_ring_pink"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintWidth_percent="0.25"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/space_avatar_margin_top" />
<Space
android:id="@+id/space_avatar_margin"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintWidth_percent="0.01"
app:layout_constraintHeight_percent="0.01"
app:layout_constraintStart_toStartOf="@id/avatarRing"
app:layout_constraintTop_toTopOf="@id/avatarRing" />
<com.google.android.material.imageview.ShapeableImageView <com.google.android.material.imageview.ShapeableImageView
android:id="@+id/avatar" android:id="@+id/avatar"
android:layout_width="90dp" android:layout_width="0dp"
android:layout_height="90dp" android:layout_height="0dp"
android:layout_margin="3dp"
android:scaleType="centerCrop" android:scaleType="centerCrop"
android:src="@drawable/wish_tree_checker_backup" android:src="@drawable/wish_tree_checker_backup"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintWidth_percent="0.23"
app:layout_constraintBottom_toBottomOf="@id/avatarRing" app:layout_constraintBottom_toBottomOf="@id/avatarRing"
app:layout_constraintEnd_toEndOf="@id/avatarRing" app:layout_constraintEnd_toEndOf="@id/avatarRing"
app:layout_constraintStart_toStartOf="@id/avatarRing" app:layout_constraintStart_toEndOf="@id/space_avatar_margin"
app:layout_constraintTop_toTopOf="@id/avatarRing" app:layout_constraintTop_toBottomOf="@id/space_avatar_margin"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.MaterialComponents.Circular" /> app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.MaterialComponents.Circular" />
<Space
android:id="@+id/space_name_margin_start"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintWidth_percent="0.035"
app:layout_constraintStart_toEndOf="@id/avatarRing"
app:layout_constraintTop_toBottomOf="@id/backButton"
app:layout_constraintBottom_toBottomOf="@id/backButton" />
<Space
android:id="@+id/space_name_margin_top"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintHeight_percent="0.03"
app:layout_constraintStart_toEndOf="@id/space_name_margin_start"
app:layout_constraintTop_toBottomOf="@id/backButton" />
<TextView <TextView
android:id="@+id/name" android:id="@+id/name"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="14dp"
android:layout_marginTop="22dp"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="1" android:maxLines="1"
android:text="昵称" android:text="昵称"
@ -78,61 +201,86 @@
android:textSize="20sp" android:textSize="20sp"
android:textStyle="bold" android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/space_name_margin_start"
app:layout_constraintTop_toBottomOf="@id/space_name_margin_top" />
<Space
android:id="@+id/space_location_margin_start"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintWidth_percent="0.035"
app:layout_constraintStart_toEndOf="@id/avatarRing" app:layout_constraintStart_toEndOf="@id/avatarRing"
app:layout_constraintTop_toBottomOf="@id/backButton" /> app:layout_constraintTop_toBottomOf="@id/name"
app:layout_constraintBottom_toBottomOf="@id/name" />
<Space
android:id="@+id/space_location_margin_top"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintHeight_percent="0.008"
app:layout_constraintStart_toEndOf="@id/space_location_margin_start"
app:layout_constraintTop_toBottomOf="@id/name" />
<TextView <TextView
android:id="@+id/location" android:id="@+id/location"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="14dp"
android:layout_marginTop="6dp"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="1" android:maxLines="1"
android:text="城市" android:text="城市"
android:textColor="#666666" android:textColor="#666666"
android:textSize="13sp" android:textSize="13sp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/avatarRing" app:layout_constraintStart_toEndOf="@id/space_location_margin_start"
app:layout_constraintTop_toBottomOf="@id/name" /> app:layout_constraintTop_toBottomOf="@id/space_location_margin_top" />
<Space
android:id="@+id/space_bio_margin_top"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintHeight_percent="0.025"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/avatarRing" />
<TextView <TextView
android:id="@+id/bio" android:id="@+id/bio"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="18dp"
android:text="签名" android:text="签名"
android:textColor="#333333" android:textColor="#333333"
android:textSize="14sp" android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/avatarRing" /> app:layout_constraintTop_toBottomOf="@id/space_bio_margin_top" />
<Space
android:id="@+id/space_addFriend_margin_top"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintHeight_percent="0.028"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/bio" />
<TextView <TextView
android:id="@+id/addFriendButton" android:id="@+id/addFriendButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="44dp" android:layout_height="0dp"
android:layout_marginTop="20dp"
android:background="@drawable/bg_purple_999" android:background="@drawable/bg_purple_999"
android:gravity="center" android:gravity="center"
android:text="加好友" android:text="加好友"
android:textColor="@android:color/white" android:textColor="@android:color/white"
android:textSize="15sp" android:textSize="15sp"
android:textStyle="bold" android:textStyle="bold"
app:layout_constraintHeight_percent="0.055"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/bio" /> app:layout_constraintTop_toBottomOf="@id/space_addFriend_margin_top" />
<TextView <Space
android:id="@+id/readOnlyHint" android:id="@+id/space_statsRow_margin_top"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="0dp"
android:layout_marginTop="10dp" app:layout_constraintHeight_percent="0.025"
android:text="仅可查看对方资料,不能编辑"
android:textColor="#999999"
android:textSize="12sp"
android:gravity="center"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/addFriendButton" /> app:layout_constraintTop_toBottomOf="@id/addFriendButton" />
@ -140,10 +288,9 @@
android:id="@+id/statsRow" android:id="@+id/statsRow"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="18dp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/readOnlyHint"> app:layout_constraintTop_toBottomOf="@id/space_statsRow_margin_top">
<LinearLayout <LinearLayout
android:id="@+id/statWorks" android:id="@+id/statWorks"
@ -263,51 +410,80 @@
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<Space
android:id="@+id/space_tabs_margin_top"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintHeight_percent="0.02"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/statsRow" />
<com.google.android.material.tabs.TabLayout <com.google.android.material.tabs.TabLayout
android:id="@+id/tabs" android:id="@+id/tabs"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="14dp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/statsRow" /> app:layout_constraintTop_toBottomOf="@id/space_tabs_margin_top" />
<Space
android:id="@+id/space_worksRecycler_margin_top"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintHeight_percent="0.017"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tabs" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/worksRecycler" android:id="@+id/worksRecycler"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:nestedScrollingEnabled="false" android:nestedScrollingEnabled="false"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/space_worksRecycler_margin_top" />
<Space
android:id="@+id/space_favoritesPlaceholder_margin_top"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintHeight_percent="0.025"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tabs" /> app:layout_constraintTop_toBottomOf="@id/tabs" />
<TextView <TextView
android:id="@+id/dynamicsPlaceholder" android:id="@+id/favoritesPlaceholder"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="18dp"
android:gravity="center" android:gravity="center"
android:text="暂无动态" android:text="收藏夹空空的"
android:textColor="#999999" android:textColor="#999999"
android:textSize="14sp" android:textSize="14sp"
android:visibility="gone" android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/space_favoritesPlaceholder_margin_top" />
<Space
android:id="@+id/space_likedPlaceholder_margin_top"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintHeight_percent="0.025"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tabs" /> app:layout_constraintTop_toBottomOf="@id/tabs" />
<TextView <TextView
android:id="@+id/profilePlaceholder" android:id="@+id/likedPlaceholder"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="18dp" android:gravity="center"
android:text="资料\n\n生日--\n性别--\n所在地--" android:text="还没有赞过的内容"
android:textColor="#333333" android:textColor="#999999"
android:textSize="14sp" android:textSize="14sp"
android:visibility="gone" android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tabs" /> app:layout_constraintTop_toBottomOf="@id/space_likedPlaceholder_margin_top" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>

View File

@ -0,0 +1,56 @@
<?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"
android:background="#F7F2FF">
<ImageView
android:id="@+id/backButton"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:contentDescription="返回"
android:src="@drawable/ic_arrow_back_24"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/titleText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="语音匹配"
android:textColor="#111111"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/backButton" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:layout_marginTop="32dp"
android:gravity="center"
android:text="通过语音快速匹配志同道合的朋友"
android:textColor="#666666"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/titleText" />
<include
android:id="@+id/bottomNavInclude"
layout="@layout/include_bottom_nav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,107 @@
<?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:background="@drawable/bg_white_16"
android:padding="16dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="选择生日"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="#111111"
android:paddingBottom="16dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="200dp"
android:orientation="horizontal"
android:gravity="center_vertical"
android:paddingTop="8dp"
android:paddingBottom="8dp">
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical"
android:gravity="center">
<NumberPicker
android:id="@+id/yearPicker"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical"
android:gravity="center">
<NumberPicker
android:id="@+id/monthPicker"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical"
android:gravity="center">
<NumberPicker
android:id="@+id/dayPicker"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="16dp"
android:gravity="end">
<TextView
android:id="@+id/cancelButton"
android:layout_width="wrap_content"
android:layout_height="44dp"
android:paddingStart="24dp"
android:paddingEnd="24dp"
android:gravity="center"
android:text="取消"
android:textColor="#666666"
android:textSize="15sp"
android:layout_marginEnd="12dp" />
<TextView
android:id="@+id/confirmButton"
android:layout_width="wrap_content"
android:layout_height="44dp"
android:paddingStart="24dp"
android:paddingEnd="24dp"
android:gravity="center"
android:text="确定"
android:textColor="#FFFFFF"
android:textSize="15sp"
android:background="@drawable/bg_purple_999" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,80 @@
<?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:background="@drawable/bg_white_16"
android:padding="16dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="选择地址"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="#111111"
android:paddingBottom="16dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="400dp"
android:orientation="horizontal">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/provinceRecycler"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginEnd="8dp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/cityRecycler"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/districtRecycler"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginStart="8dp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="16dp"
android:gravity="end">
<TextView
android:id="@+id/cancelButton"
android:layout_width="wrap_content"
android:layout_height="44dp"
android:paddingStart="24dp"
android:paddingEnd="24dp"
android:gravity="center"
android:text="取消"
android:textColor="#666666"
android:textSize="15sp"
android:layout_marginEnd="12dp" />
<TextView
android:id="@+id/confirmButton"
android:layout_width="wrap_content"
android:layout_height="44dp"
android:paddingStart="24dp"
android:paddingEnd="24dp"
android:gravity="center"
android:text="确定"
android:textColor="#FFFFFF"
android:textSize="15sp"
android:background="@drawable/bg_purple_999" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,100 @@
<?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:background="@drawable/bg_white_16"
android:padding="16dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="选择地址"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="#111111"
android:paddingBottom="16dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="省份"
android:textColor="#666666"
android:textSize="14sp"
android:paddingEnd="8dp" />
<Spinner
android:id="@+id/provinceSpinner"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="2"
android:background="@drawable/bg_gray_12" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginTop="12dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="城市"
android:textColor="#666666"
android:textSize="14sp"
android:paddingEnd="8dp" />
<Spinner
android:id="@+id/citySpinner"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="2"
android:background="@drawable/bg_gray_12" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="16dp"
android:gravity="end">
<TextView
android:id="@+id/cancelButton"
android:layout_width="wrap_content"
android:layout_height="44dp"
android:paddingStart="24dp"
android:paddingEnd="24dp"
android:gravity="center"
android:text="取消"
android:textColor="#666666"
android:textSize="15sp"
android:layout_marginEnd="12dp" />
<TextView
android:id="@+id/confirmButton"
android:layout_width="wrap_content"
android:layout_height="44dp"
android:paddingStart="24dp"
android:paddingEnd="24dp"
android:gravity="center"
android:text="确定"
android:textColor="#FFFFFF"
android:textSize="15sp"
android:background="@drawable/bg_purple_999" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000"
android:clickable="true"
android:focusable="true">
<ImageView
android:id="@+id/avatarImageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:scaleType="fitCenter"
android:adjustViewBounds="true"
android:contentDescription="头像预览" />
</FrameLayout>

View File

@ -1,26 +1,20 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.bottomappbar.BottomAppBar xmlns:android="http://schemas.android.com/apk/res/android" <com.google.android.material.bottomnavigation.BottomNavigationView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/bottomAppBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="0dp"
android:paddingEnd="0dp"
app:backgroundTint="@android:color/white"
app:contentInsetStart="0dp"
app:contentInsetEnd="0dp"
app:contentInsetStartWithNavigation="0dp"
app:hideOnScroll="false">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavigation" android:id="@+id/bottomNavigation"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="48dp"
android:minHeight="48dp"
android:background="@android:color/white" android:background="@android:color/white"
android:paddingTop="2dp"
android:paddingBottom="2dp"
android:fitsSystemWindows="false"
android:elevation="8dp"
app:itemIconTint="@color/bottom_nav_item_color" app:itemIconTint="@color/bottom_nav_item_color"
app:itemTextColor="@color/bottom_nav_item_color" app:itemTextColor="@color/bottom_nav_item_color"
app:itemIconSize="22dp"
app:itemTextAppearanceActive="@style/BottomNavigationView.TextAppearance"
app:itemTextAppearanceInactive="@style/BottomNavigationView.TextAppearance"
app:labelVisibilityMode="labeled" app:labelVisibilityMode="labeled"
app:menu="@menu/bottom_nav_main" /> app:menu="@menu/bottom_nav_main" />
</com.google.android.material.bottomappbar.BottomAppBar>

View File

@ -11,8 +11,9 @@
android:id="@+id/avatar" android:id="@+id/avatar"
android:layout_width="44dp" android:layout_width="44dp"
android:layout_height="44dp" android:layout_height="44dp"
android:background="@drawable/bg_avatar_circle" android:background="@drawable/bg_avatar_circle_transparent"
android:padding="6dp" android:clipToOutline="true"
android:scaleType="centerCrop"
android:src="@drawable/ic_account_circle_24" android:src="@drawable/ic_account_circle_24"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"

View File

@ -19,8 +19,9 @@
android:id="@+id/avatarView" android:id="@+id/avatarView"
android:layout_width="34dp" android:layout_width="34dp"
android:layout_height="34dp" android:layout_height="34dp"
android:background="@drawable/bg_avatar_circle" android:background="@drawable/bg_avatar_circle_transparent"
android:padding="5dp" android:clipToOutline="true"
android:scaleType="centerCrop"
android:src="@drawable/ic_account_circle_24" android:src="@drawable/ic_account_circle_24"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />

View File

@ -19,8 +19,9 @@
android:id="@+id/avatarView" android:id="@+id/avatarView"
android:layout_width="34dp" android:layout_width="34dp"
android:layout_height="34dp" android:layout_height="34dp"
android:background="@drawable/bg_avatar_circle" android:background="@drawable/bg_avatar_circle_transparent"
android:padding="5dp" android:clipToOutline="true"
android:scaleType="centerCrop"
android:src="@drawable/ic_account_circle_24" android:src="@drawable/ic_account_circle_24"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />

View File

@ -1,78 +1,78 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="72dp"
android:layout_margin="6dp" android:background="@android:color/white"
app:cardCornerRadius="14dp" android:paddingStart="16dp"
app:cardElevation="0dp" android:paddingEnd="16dp">
app:strokeColor="#14000000"
app:strokeWidth="1dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="12dp">
<ImageView <ImageView
android:id="@+id/avatarImage" android:id="@+id/avatarImage"
android:layout_width="0dp" android:layout_width="44dp"
android:layout_height="0dp" android:layout_height="44dp"
android:background="#0A000000" android:background="@drawable/bg_avatar_circle"
android:padding="6dp"
android:scaleType="centerCrop" android:scaleType="centerCrop"
android:src="@drawable/ic_account_circle_24" android:src="@drawable/ic_account_circle_24"
app:layout_constraintDimensionRatio="1:1" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<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 <TextView
android:id="@+id/userName" android:id="@+id/userName"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp" android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="1" android:maxLines="1"
android:text="附近主播" android:text="用户昵称"
android:textColor="#111111" android:textColor="#111111"
android:textSize="13sp" android:textSize="15sp"
android:textStyle="bold" android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toStartOf="@id/addButton"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toEndOf="@id/avatarImage"
app:layout_constraintTop_toBottomOf="@id/avatarImage" /> app:layout_constraintTop_toTopOf="@id/avatarImage" />
<TextView <TextView
android:id="@+id/distanceText" android:id="@+id/distanceText"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:layout_marginEnd="12dp"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="1" android:maxLines="1"
android:text="1.2km" android:text="距离 1.2km"
android:textColor="#666666" android:textColor="#666666"
android:textSize="12sp" android:textSize="13sp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toStartOf="@id/addButton"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toEndOf="@id/avatarImage"
app:layout_constraintTop_toBottomOf="@id/userName" /> app:layout_constraintTop_toBottomOf="@id/userName" />
</androidx.constraintlayout.widget.ConstraintLayout> <TextView
android:id="@+id/addButton"
android:layout_width="wrap_content"
android:layout_height="32dp"
android:background="@drawable/bg_purple_999"
android:gravity="center"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:text="添加"
android:textColor="@android:color/white"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</com.google.android.material.card.MaterialCardView> <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/avatarImage" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -34,4 +34,9 @@
<item name="cornerSize">50%</item> <item name="cornerSize">50%</item>
</style> </style>
<!-- 底部导航栏文字样式 -->
<style name="BottomNavigationView.TextAppearance" parent="TextAppearance.AppCompat">
<item name="android:textSize">10sp</item>
</style>
</resources> </resources>