移动端,首页、缘池、许愿树、我的,页面优化,功能完善。
This commit is contained in:
parent
6e8f5c4011
commit
7042137e5b
|
|
@ -55,3 +55,12 @@ export function memberPackageToggleHotApi(id, data) {
|
|||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 切换启用/禁用状态
|
||||
export function memberPackageToggleStatusApi(id, data) {
|
||||
return request({
|
||||
url: `/admin/member/package/toggleStatus/${id}`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,20 +11,50 @@
|
|||
|
||||
<!-- 数据表格 -->
|
||||
<el-table :data="tableData" v-loading="loading" border>
|
||||
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||
<el-table-column prop="months" label="月数" width="100" align="center" />
|
||||
<el-table-column prop="price" label="价格" width="120" align="center" />
|
||||
<el-table-column prop="ios_price" label="ios端价格" width="120" align="center" />
|
||||
<el-table-column prop="label" label="标识" align="center" />
|
||||
<el-table-column prop="ios_product_id" label="ios产品ID" align="center" />
|
||||
<el-table-column label="操作" width="300" align="center" fixed="right">
|
||||
<el-table-column prop="id" label="ID" width="60" align="center" />
|
||||
<el-table-column prop="name" label="套餐名称" width="120" align="center" />
|
||||
<el-table-column prop="duration" label="有效期(天)" width="100" align="center" />
|
||||
<el-table-column prop="original_price" label="原价" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
¥{{ scope.row.original_price }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="price" label="现价" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
¥{{ scope.row.price }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="description" label="描述" min-width="150" align="center" show-overflow-tooltip />
|
||||
<el-table-column prop="sort" label="排序" width="80" align="center" />
|
||||
<el-table-column label="热销" width="80" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.is_hot === 1 ? 'danger' : 'info'" size="small">
|
||||
{{ scope.row.is_hot === 1 ? '是' : '否' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="80" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.status === 1 ? 'success' : 'info'" size="small">
|
||||
{{ scope.row.status === 1 ? '启用' : '禁用' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="280" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
:type="scope.row.is_hot === 1 ? 'success' : 'info'"
|
||||
:type="scope.row.is_hot === 1 ? 'danger' : 'default'"
|
||||
size="mini"
|
||||
@click="handleToggleHot(scope.row)"
|
||||
>
|
||||
{{ scope.row.is_hot === 1 ? '热销推荐' : '热销推荐' }}
|
||||
{{ scope.row.is_hot === 1 ? '取消热销' : '设为热销' }}
|
||||
</el-button>
|
||||
<el-button
|
||||
:type="scope.row.status === 1 ? 'info' : 'success'"
|
||||
size="mini"
|
||||
@click="handleToggleStatus(scope.row)"
|
||||
>
|
||||
{{ scope.row.status === 1 ? '禁用' : '启用' }}
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
|
|
@ -64,7 +94,7 @@
|
|||
<el-dialog
|
||||
:title="dialogTitle"
|
||||
:visible.sync="dialogVisible"
|
||||
width="600px"
|
||||
width="650px"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-form
|
||||
|
|
@ -73,48 +103,104 @@
|
|||
ref="form"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-form-item label="月数" prop="months">
|
||||
<el-form-item label="套餐名称" prop="name">
|
||||
<el-input
|
||||
v-model="form.name"
|
||||
placeholder="请输入套餐名称,如:月度会员"
|
||||
maxlength="64"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="有效期(天)" prop="duration">
|
||||
<el-input-number
|
||||
v-model="form.months"
|
||||
v-model="form.duration"
|
||||
:min="1"
|
||||
placeholder="请输入月数"
|
||||
placeholder="请输入有效期天数"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="价格" prop="price">
|
||||
<el-input-number
|
||||
v-model="form.price"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
placeholder="请输入价格"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="ios端价格" prop="iosPrice">
|
||||
<el-input-number
|
||||
v-model="form.iosPrice"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
placeholder="请输入ios端价格"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="标识" prop="label">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="原价" prop="originalPrice">
|
||||
<el-input-number
|
||||
v-model="form.originalPrice"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
placeholder="请输入原价"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="现价" prop="price">
|
||||
<el-input-number
|
||||
v-model="form.price"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
placeholder="请输入现价"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="套餐描述" prop="description">
|
||||
<el-input
|
||||
v-model="form.label"
|
||||
placeholder="请输入标识"
|
||||
maxlength="50"
|
||||
v-model="form.description"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请输入套餐描述"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="ios产品ID" prop="iosProductId">
|
||||
<el-form-item label="套餐图标" prop="icon">
|
||||
<el-input
|
||||
v-model="form.iosProductId"
|
||||
placeholder="请输入ios产品ID"
|
||||
maxlength="100"
|
||||
show-word-limit
|
||||
v-model="form.icon"
|
||||
placeholder="请输入图标URL或图标名称"
|
||||
maxlength="255"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="特权列表" prop="privileges">
|
||||
<el-input
|
||||
v-model="form.privileges"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder='请输入特权列表JSON,如:["无限聊天","专属标识","优先推荐"]'
|
||||
maxlength="1000"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="排序" prop="sort">
|
||||
<el-input-number
|
||||
v-model="form.sort"
|
||||
:min="0"
|
||||
placeholder="排序"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="热销推荐" prop="isHot">
|
||||
<el-switch
|
||||
v-model="form.isHot"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-switch
|
||||
v-model="form.status"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
active-text="启用"
|
||||
inactive-text="禁用"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div slot="footer">
|
||||
<el-button @click="dialogVisible = false">返回</el-button>
|
||||
|
|
@ -132,7 +218,8 @@ import {
|
|||
memberPackageAddApi,
|
||||
memberPackageUpdateApi,
|
||||
memberPackageDeleteApi,
|
||||
memberPackageToggleHotApi
|
||||
memberPackageToggleHotApi,
|
||||
memberPackageToggleStatusApi
|
||||
} from '@/api/memberPackage';
|
||||
|
||||
export default {
|
||||
|
|
@ -151,27 +238,26 @@ export default {
|
|||
submitting: false,
|
||||
form: {
|
||||
id: null,
|
||||
months: null,
|
||||
price: null,
|
||||
iosPrice: null,
|
||||
label: '',
|
||||
iosProductId: ''
|
||||
name: '',
|
||||
description: '',
|
||||
icon: '',
|
||||
duration: 30,
|
||||
originalPrice: 0,
|
||||
price: 0,
|
||||
privileges: '[]',
|
||||
isHot: 0,
|
||||
sort: 0,
|
||||
status: 1
|
||||
},
|
||||
formRules: {
|
||||
months: [
|
||||
{ required: true, message: '请输入月数', trigger: 'blur' }
|
||||
name: [
|
||||
{ required: true, message: '请输入套餐名称', trigger: 'blur' }
|
||||
],
|
||||
duration: [
|
||||
{ required: true, message: '请输入有效期', trigger: 'blur' }
|
||||
],
|
||||
price: [
|
||||
{ required: true, message: '请输入价格', trigger: 'blur' }
|
||||
],
|
||||
iosPrice: [
|
||||
{ required: true, message: '请输入ios端价格', trigger: 'blur' }
|
||||
],
|
||||
label: [
|
||||
{ required: true, message: '请输入标识', trigger: 'blur' }
|
||||
],
|
||||
iosProductId: [
|
||||
{ required: true, message: '请输入ios产品ID', trigger: 'blur' }
|
||||
{ required: true, message: '请输入现价', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
|
@ -208,14 +294,19 @@ export default {
|
|||
|
||||
// 添加
|
||||
handleAdd() {
|
||||
this.dialogTitle = '添加';
|
||||
this.dialogTitle = '添加会员套餐';
|
||||
this.form = {
|
||||
id: null,
|
||||
months: null,
|
||||
price: null,
|
||||
iosPrice: null,
|
||||
label: '',
|
||||
iosProductId: ''
|
||||
name: '',
|
||||
description: '',
|
||||
icon: '',
|
||||
duration: 30,
|
||||
originalPrice: 0,
|
||||
price: 0,
|
||||
privileges: '[]',
|
||||
isHot: 0,
|
||||
sort: 0,
|
||||
status: 1
|
||||
};
|
||||
this.dialogVisible = true;
|
||||
this.$nextTick(() => {
|
||||
|
|
@ -225,14 +316,19 @@ export default {
|
|||
|
||||
// 编辑
|
||||
handleEdit(row) {
|
||||
this.dialogTitle = '编辑';
|
||||
this.dialogTitle = '编辑会员套餐';
|
||||
this.form = {
|
||||
id: row.id,
|
||||
months: row.months,
|
||||
price: row.price,
|
||||
iosPrice: row.ios_price,
|
||||
label: row.label,
|
||||
iosProductId: row.ios_product_id
|
||||
name: row.name || '',
|
||||
description: row.description || '',
|
||||
icon: row.icon || '',
|
||||
duration: row.duration || 30,
|
||||
originalPrice: row.original_price || 0,
|
||||
price: row.price || 0,
|
||||
privileges: row.privileges || '[]',
|
||||
isHot: row.is_hot || 0,
|
||||
sort: row.sort || 0,
|
||||
status: row.status || 1
|
||||
};
|
||||
this.dialogVisible = true;
|
||||
this.$nextTick(() => {
|
||||
|
|
@ -266,7 +362,7 @@ export default {
|
|||
|
||||
// 删除
|
||||
handleDelete(row) {
|
||||
this.$confirm('确定要删除该会员包吗?', '提示', {
|
||||
this.$confirm('确定要删除该会员套餐吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
|
|
@ -297,6 +393,25 @@ export default {
|
|||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
|
||||
// 切换启用/禁用状态
|
||||
handleToggleStatus(row) {
|
||||
const newStatus = row.status === 1 ? 0 : 1;
|
||||
const msg = newStatus === 1 ? '启用' : '禁用';
|
||||
|
||||
this.$confirm(`确定要${msg}该套餐吗?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
.then(() => {
|
||||
memberPackageToggleStatusApi(row.id, { status: newStatus }).then(() => {
|
||||
this.$message.success('状态更新成功');
|
||||
this.getList();
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<el-form-item label="用户地址:" prop="addres">
|
||||
<el-input v-model="ruleForm.addres"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户备注:" prop="mark">
|
||||
<el-form-item label="个性签名:" prop="mark">
|
||||
<el-input v-model="ruleForm.mark" type="textarea"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户分组:" prop="groupId">
|
||||
|
|
@ -73,7 +73,7 @@ export default {
|
|||
rules: {
|
||||
id: [{ required: true, message: '请输入用户编号', trigger: 'change' }],
|
||||
addres: [{ required: true, message: '请输入用户地址', trigger: 'change' }],
|
||||
mark: [{ required: true, message: '请输入用户备注', trigger: 'blur' }],
|
||||
mark: [{ required: true, message: '请输入个性签名', trigger: 'blur' }],
|
||||
groupId: [{ required: true, message: '请选择用户分组', trigger: 'blur' }],
|
||||
isPromoter: [{ required: true, message: '请选择状态', trigger: 'blur' }],
|
||||
status: [{ required: true, message: '请选择状态', trigger: 'blur' }],
|
||||
|
|
|
|||
|
|
@ -164,10 +164,10 @@
|
|||
</div>
|
||||
<div class="user-info">
|
||||
<div class="section">
|
||||
<div class="section-hd">用户备注</div>
|
||||
<div class="section-hd">个性签名</div>
|
||||
<div class="section-bd">
|
||||
<div class="item">
|
||||
<div>备注:</div>
|
||||
<div>签名:</div>
|
||||
<div class="value">{{ userDetailData.mark || '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ public class MemberPackageController {
|
|||
Integer total = jdbcTemplate.queryForObject(countSql, Integer.class);
|
||||
|
||||
// 分页查询
|
||||
String sql = "SELECT * FROM eb_member_package ORDER BY id ASC LIMIT ?, ?";
|
||||
String sql = "SELECT * FROM eb_member_package ORDER BY sort ASC, id ASC LIMIT ?, ?";
|
||||
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, (page - 1) * limit, limit);
|
||||
|
||||
CommonPage<Map<String, Object>> result = new CommonPage<>();
|
||||
|
|
@ -82,13 +82,18 @@ public class MemberPackageController {
|
|||
@RequestMapping(value = "/add", method = RequestMethod.POST)
|
||||
public CommonResult<String> add(@RequestBody Map<String, Object> params) {
|
||||
try {
|
||||
String sql = "INSERT INTO eb_member_package (months, price, ios_price, label, ios_product_id) VALUES (?, ?, ?, ?, ?)";
|
||||
String sql = "INSERT INTO eb_member_package (name, description, icon, duration, original_price, price, privileges, is_hot, sort, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
jdbcTemplate.update(sql,
|
||||
params.get("months"),
|
||||
params.get("price"),
|
||||
params.get("iosPrice"),
|
||||
params.get("label"),
|
||||
params.get("iosProductId")
|
||||
params.getOrDefault("name", ""),
|
||||
params.getOrDefault("description", ""),
|
||||
params.getOrDefault("icon", ""),
|
||||
params.getOrDefault("duration", 30),
|
||||
params.getOrDefault("originalPrice", 0),
|
||||
params.getOrDefault("price", 0),
|
||||
params.getOrDefault("privileges", "[]"),
|
||||
params.getOrDefault("isHot", 0),
|
||||
params.getOrDefault("sort", 0),
|
||||
params.getOrDefault("status", 1)
|
||||
);
|
||||
return CommonResult.success("添加成功");
|
||||
} catch (Exception e) {
|
||||
|
|
@ -104,13 +109,18 @@ public class MemberPackageController {
|
|||
@RequestMapping(value = "/update", method = RequestMethod.POST)
|
||||
public CommonResult<String> update(@RequestBody Map<String, Object> params) {
|
||||
try {
|
||||
String sql = "UPDATE eb_member_package SET months = ?, price = ?, ios_price = ?, label = ?, ios_product_id = ? WHERE id = ?";
|
||||
String sql = "UPDATE eb_member_package SET name = ?, description = ?, icon = ?, duration = ?, original_price = ?, price = ?, privileges = ?, is_hot = ?, sort = ?, status = ? WHERE id = ?";
|
||||
jdbcTemplate.update(sql,
|
||||
params.get("months"),
|
||||
params.get("price"),
|
||||
params.get("iosPrice"),
|
||||
params.get("label"),
|
||||
params.get("iosProductId"),
|
||||
params.getOrDefault("name", ""),
|
||||
params.getOrDefault("description", ""),
|
||||
params.getOrDefault("icon", ""),
|
||||
params.getOrDefault("duration", 30),
|
||||
params.getOrDefault("originalPrice", 0),
|
||||
params.getOrDefault("price", 0),
|
||||
params.getOrDefault("privileges", "[]"),
|
||||
params.getOrDefault("isHot", 0),
|
||||
params.getOrDefault("sort", 0),
|
||||
params.getOrDefault("status", 1),
|
||||
params.get("id")
|
||||
);
|
||||
return CommonResult.success("更新成功");
|
||||
|
|
@ -151,4 +161,20 @@ public class MemberPackageController {
|
|||
return CommonResult.failed("状态更新失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换状态
|
||||
*/
|
||||
@ApiOperation(value = "切换状态")
|
||||
@RequestMapping(value = "/toggleStatus/{id}", method = RequestMethod.POST)
|
||||
public CommonResult<String> toggleStatus(@PathVariable Integer id, @RequestBody Map<String, Object> params) {
|
||||
try {
|
||||
String sql = "UPDATE eb_member_package SET status = ? WHERE id = ?";
|
||||
jdbcTemplate.update(sql, params.get("status"), id);
|
||||
return CommonResult.success("状态更新成功");
|
||||
} catch (Exception e) {
|
||||
log.error("切换状态失败", e);
|
||||
return CommonResult.failed("状态更新失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,21 @@ public class UserEditRequest implements Serializable {
|
|||
private String nickname;
|
||||
|
||||
@ApiModelProperty(value = "用户头像")
|
||||
@NotBlank(message = "请上传用户头像")
|
||||
@Length(max = 255, message = "用户头像不能超过255个字符")
|
||||
private String avatar;
|
||||
|
||||
@ApiModelProperty(value = "生日,格式:yyyy-MM-dd")
|
||||
@Length(max = 32, message = "生日格式不正确")
|
||||
private String birthday;
|
||||
|
||||
@ApiModelProperty(value = "性别:0=未知,1=男,2=女,3=保密")
|
||||
private Integer sex;
|
||||
|
||||
@ApiModelProperty(value = "地址")
|
||||
@Length(max = 255, message = "地址不能超过255个字符")
|
||||
private String addres;
|
||||
|
||||
@ApiModelProperty(value = "个人签名/备注")
|
||||
@Length(max = 255, message = "个人签名不能超过255个字符")
|
||||
private String mark;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ public class UserResponse {
|
|||
@ApiModelProperty(value = "身份证号码")
|
||||
private String cardId;
|
||||
|
||||
@ApiModelProperty(value = "用户备注")
|
||||
@ApiModelProperty(value = "个性签名")
|
||||
private String mark;
|
||||
|
||||
@ApiModelProperty(value = "合伙人id")
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ public class NearbyUserVO implements Serializable {
|
|||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty(value = "用户ID")
|
||||
private Integer uid;
|
||||
private Integer id;
|
||||
|
||||
@ApiModelProperty(value = "用户昵称")
|
||||
private String nickname;
|
||||
|
|
@ -24,18 +24,33 @@ public class NearbyUserVO implements Serializable {
|
|||
@ApiModelProperty(value = "用户头像")
|
||||
private String avatar;
|
||||
|
||||
@ApiModelProperty(value = "性别 0未知 1男 2女")
|
||||
private Integer sex;
|
||||
@ApiModelProperty(value = "性别")
|
||||
private String gender;
|
||||
|
||||
@ApiModelProperty(value = "年龄")
|
||||
private Integer age;
|
||||
|
||||
@ApiModelProperty(value = "位置/地址")
|
||||
private String location;
|
||||
|
||||
@ApiModelProperty(value = "个人简介")
|
||||
private String bio;
|
||||
|
||||
@ApiModelProperty(value = "距离(米)")
|
||||
private Double distance;
|
||||
|
||||
@ApiModelProperty(value = "是否在线")
|
||||
private Boolean isOnline;
|
||||
|
||||
@ApiModelProperty(value = "个性签名")
|
||||
private String signature;
|
||||
@ApiModelProperty(value = "最后活跃时间")
|
||||
private String lastActiveTime;
|
||||
|
||||
// 兼容旧字段
|
||||
public void setOnline(Boolean online) {
|
||||
this.isOnline = online;
|
||||
}
|
||||
|
||||
public Boolean getOnline() {
|
||||
return this.isOnline;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,11 +58,21 @@ public class CommunityFrontController {
|
|||
|
||||
@ApiOperation("获取附近用户")
|
||||
@GetMapping("/nearby-users")
|
||||
public CommonResult<List<NearbyUserVO>> getNearbyUsers(
|
||||
public CommonResult<java.util.Map<String, Object>> getNearbyUsers(
|
||||
@RequestParam Double latitude,
|
||||
@RequestParam Double longitude,
|
||||
@RequestParam(defaultValue = "5000") Integer radius,
|
||||
@RequestParam(defaultValue = "6") Integer limit) {
|
||||
return CommonResult.success(communityService.getNearbyUsers(latitude, longitude, radius, limit));
|
||||
List<NearbyUserVO> users = communityService.getNearbyUsers(latitude, longitude, radius, limit);
|
||||
java.util.Map<String, Object> result = new java.util.HashMap<>();
|
||||
result.put("list", users);
|
||||
result.put("total", users.size());
|
||||
return CommonResult.success(result);
|
||||
}
|
||||
|
||||
@ApiOperation("获取可匹配用户总数")
|
||||
@GetMapping("/user-count")
|
||||
public CommonResult<Integer> getMatchableUserCount() {
|
||||
return CommonResult.success(communityService.getMatchableUserCount());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -179,4 +179,19 @@ public class FriendController {
|
|||
return CommonResult.failed("获取失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查好友状态(是否是好友、是否有待处理的好友请求)
|
||||
*/
|
||||
@ApiOperation(value = "检查好友状态")
|
||||
@GetMapping("/friends/check/{userId}")
|
||||
public CommonResult<Map<String, Object>> checkFriendStatus(@PathVariable Integer userId) {
|
||||
try {
|
||||
Map<String, Object> result = friendService.checkFriendStatus(userId);
|
||||
return CommonResult.success(result);
|
||||
} catch (Exception e) {
|
||||
log.error("检查好友状态失败: {}", e.getMessage());
|
||||
return CommonResult.failed("检查失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ crmeb:
|
|||
# 配置端口
|
||||
server:
|
||||
port: 8081
|
||||
address: 0.0.0.0 # 绑定到所有网络接口,允许外部访问
|
||||
servlet:
|
||||
context-path: / # 访问path
|
||||
tomcat:
|
||||
|
|
|
|||
|
|
@ -88,4 +88,9 @@ public interface CommunityService {
|
|||
* 获取附近用户
|
||||
*/
|
||||
List<NearbyUserVO> getNearbyUsers(Double latitude, Double longitude, Integer radius, Integer limit);
|
||||
|
||||
/**
|
||||
* 获取可匹配用户总数(排除当前用户)
|
||||
*/
|
||||
Integer getMatchableUserCount();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,4 +55,9 @@ public interface FriendService {
|
|||
* 获取黑名单列表
|
||||
*/
|
||||
CommonPage<Map<String, Object>> getBlockedList(Integer page, Integer pageSize);
|
||||
|
||||
/**
|
||||
* 检查好友状态(是否是好友、是否有待处理的好友请求)
|
||||
*/
|
||||
Map<String, Object> checkFriendStatus(Integer targetUserId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import com.zbkj.common.exception.CrmebException;
|
|||
import com.zbkj.common.model.community.CommunityCategory;
|
||||
import com.zbkj.common.model.community.CommunityMatchConfig;
|
||||
import com.zbkj.common.model.community.CommunityMessage;
|
||||
import com.zbkj.common.model.user.User;
|
||||
import com.zbkj.common.request.CommunityMessageRequest;
|
||||
import com.zbkj.common.vo.CommunityMessageVO;
|
||||
import com.zbkj.common.vo.NearbyUserVO;
|
||||
|
|
@ -181,9 +182,91 @@ public class CommunityServiceImpl implements CommunityService {
|
|||
// ==================== 用户匹配 ====================
|
||||
@Override
|
||||
public List<NearbyUserVO> getNearbyUsers(Double latitude, Double longitude, Integer radius, Integer limit) {
|
||||
// TODO: 实现基于位置的用户匹配逻辑
|
||||
// 这里需要根据用户表中的经纬度字段进行距离计算
|
||||
return new ArrayList<>();
|
||||
// 获取当前登录用户ID
|
||||
Integer currentUserId = null;
|
||||
try {
|
||||
currentUserId = userService.getUserIdException();
|
||||
} catch (Exception e) {
|
||||
// 未登录用户
|
||||
}
|
||||
|
||||
// 从数据库随机获取用户(排除当前用户)
|
||||
List<NearbyUserVO> result = new ArrayList<>();
|
||||
|
||||
// 构建查询条件
|
||||
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(User::getStatus, true); // 只查询正常状态的用户
|
||||
if (currentUserId != null) {
|
||||
wrapper.ne(User::getUid, currentUserId); // 排除当前用户
|
||||
}
|
||||
wrapper.last("ORDER BY RAND() LIMIT " + limit); // 随机获取
|
||||
|
||||
List<User> users = userService.list(wrapper);
|
||||
|
||||
for (User user : users) {
|
||||
NearbyUserVO vo = new NearbyUserVO();
|
||||
vo.setId(user.getUid());
|
||||
vo.setNickname(user.getNickname() != null ? user.getNickname() : "用户" + user.getUid());
|
||||
vo.setAvatar(user.getAvatar());
|
||||
vo.setGender(user.getSex() == 1 ? "男" : (user.getSex() == 2 ? "女" : "保密"));
|
||||
vo.setAge(0); // 用户表没有年龄字段
|
||||
|
||||
// 处理地址:优先使用addres字段,否则使用country
|
||||
String location = user.getAddres();
|
||||
if (location == null || location.isEmpty()) {
|
||||
location = user.getCountry();
|
||||
}
|
||||
if (location == null || location.isEmpty()) {
|
||||
location = "附近";
|
||||
}
|
||||
// 简化地址显示(只取城市部分)
|
||||
if (location.contains("-")) {
|
||||
String[] parts = location.split("-");
|
||||
location = parts.length > 1 ? parts[1] : parts[0];
|
||||
} else if (location.contains("·")) {
|
||||
String[] parts = location.split("·");
|
||||
location = parts.length > 1 ? parts[1] : parts[0];
|
||||
}
|
||||
vo.setLocation(location);
|
||||
|
||||
vo.setBio(user.getMark() != null ? user.getMark() : "");
|
||||
vo.setDistance(Math.random() * radius); // 模拟距离
|
||||
|
||||
// 判断在线状态(最后登录时间在5分钟内视为在线)
|
||||
boolean isOnline = false;
|
||||
if (user.getLastLoginTime() != null) {
|
||||
long diff = System.currentTimeMillis() - user.getLastLoginTime().getTime();
|
||||
isOnline = diff < 5 * 60 * 1000; // 5分钟
|
||||
}
|
||||
vo.setOnline(isOnline);
|
||||
vo.setLastActiveTime(user.getLastLoginTime() != null ?
|
||||
new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm").format(user.getLastLoginTime()) : null);
|
||||
|
||||
result.add(vo);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// ==================== 获取可匹配用户总数 ====================
|
||||
@Override
|
||||
public Integer getMatchableUserCount() {
|
||||
// 获取当前登录用户ID
|
||||
Integer currentUserId = null;
|
||||
try {
|
||||
currentUserId = userService.getUserIdException();
|
||||
} catch (Exception e) {
|
||||
// 未登录用户
|
||||
}
|
||||
|
||||
// 构建查询条件
|
||||
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(User::getStatus, true); // 只查询正常状态的用户
|
||||
if (currentUserId != null) {
|
||||
wrapper.ne(User::getUid, currentUserId); // 排除当前用户
|
||||
}
|
||||
|
||||
return (int) userService.count(wrapper);
|
||||
}
|
||||
|
||||
// ==================== 自动审核 ====================
|
||||
|
|
|
|||
|
|
@ -277,4 +277,28 @@ public class FriendServiceImpl implements FriendService {
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> checkFriendStatus(Integer targetUserId) {
|
||||
Integer currentUserId = userService.getUserId();
|
||||
|
||||
Map<String, Object> result = new java.util.HashMap<>();
|
||||
|
||||
// 检查是否是好友
|
||||
Long friendCount = friendDao.checkFriendship(currentUserId, targetUserId);
|
||||
boolean isFriend = friendCount != null && friendCount > 0;
|
||||
result.put("isFriend", isFriend);
|
||||
|
||||
// 检查是否有待处理的好友请求(我发给对方的)
|
||||
Long requestCount = friendRequestDao.checkExistingRequest(currentUserId, targetUserId);
|
||||
boolean hasPendingRequest = requestCount != null && requestCount > 0;
|
||||
result.put("hasPendingRequest", hasPendingRequest);
|
||||
|
||||
// 检查对方是否发给我好友请求
|
||||
Long receivedRequestCount = friendRequestDao.checkExistingRequest(targetUserId, currentUserId);
|
||||
boolean hasReceivedRequest = receivedRequestCount != null && receivedRequestCount > 0;
|
||||
result.put("hasReceivedRequest", hasReceivedRequest);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1711,8 +1711,35 @@ public class UserServiceImpl extends ServiceImpl<UserDao, User> implements UserS
|
|||
@Override
|
||||
public Boolean editUser(UserEditRequest request) {
|
||||
User user = getInfo();
|
||||
user.setAvatar(systemAttachmentService.clearPrefix(request.getAvatar()));
|
||||
|
||||
// 更新头像(如果提供)
|
||||
if (request.getAvatar() != null && !request.getAvatar().isEmpty()) {
|
||||
user.setAvatar(systemAttachmentService.clearPrefix(request.getAvatar()));
|
||||
}
|
||||
|
||||
// 更新昵称
|
||||
user.setNickname(request.getNickname());
|
||||
|
||||
// 更新生日(如果提供)
|
||||
if (request.getBirthday() != null && !request.getBirthday().isEmpty()) {
|
||||
user.setBirthday(request.getBirthday());
|
||||
}
|
||||
|
||||
// 更新性别(如果提供)
|
||||
if (request.getSex() != null) {
|
||||
user.setSex(request.getSex());
|
||||
}
|
||||
|
||||
// 更新地址(如果提供)
|
||||
if (request.getAddres() != null) {
|
||||
user.setAddres(request.getAddres());
|
||||
}
|
||||
|
||||
// 更新个人签名/备注(如果提供)
|
||||
if (request.getMark() != null) {
|
||||
user.setMark(request.getMark());
|
||||
}
|
||||
|
||||
user.setUpdateTime(DateUtil.date());
|
||||
return updateById(user);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
u.uid as userId,
|
||||
u.nickname,
|
||||
u.avatar,
|
||||
u.mark as signature,
|
||||
fr.create_time as followTime,
|
||||
CASE
|
||||
WHEN TIMESTAMPDIFF(MINUTE, u.last_login_time, NOW()) < 5 THEN 1
|
||||
|
|
@ -37,6 +38,7 @@
|
|||
u.uid as userId,
|
||||
u.nickname,
|
||||
u.avatar,
|
||||
u.mark as signature,
|
||||
fr.create_time as followTime,
|
||||
CASE
|
||||
WHEN TIMESTAMPDIFF(MINUTE, u.last_login_time, NOW()) < 5 THEN 1
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
u.nickname as name,
|
||||
u.avatar as avatarUrl,
|
||||
u.phone,
|
||||
u.mark as signature,
|
||||
u.last_login_time as lastOnlineTime,
|
||||
CASE WHEN TIMESTAMPDIFF(MINUTE, u.last_login_time, NOW()) < 5 THEN 1 ELSE 0 END as isOnline
|
||||
FROM eb_friend f
|
||||
|
|
|
|||
|
|
@ -103,6 +103,11 @@
|
|||
android:name="com.example.livestreaming.HeartbeatSignalActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name="com.example.livestreaming.DynamicCommunityActivity"
|
||||
android:exported="false"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.example.livestreaming.OnlineDatingActivity"
|
||||
android:exported="false" />
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ public class AvatarViewerDialog extends Dialog {
|
|||
private Uri avatarUri;
|
||||
private Integer avatarResId;
|
||||
private Drawable avatarDrawable;
|
||||
private String avatarUrl;
|
||||
private Context activityContext;
|
||||
|
||||
public AvatarViewerDialog(@NonNull Context context) {
|
||||
|
|
@ -36,6 +37,7 @@ public class AvatarViewerDialog extends Dialog {
|
|||
this.avatarUri = uri;
|
||||
this.avatarResId = null;
|
||||
this.avatarDrawable = null;
|
||||
this.avatarUrl = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -43,6 +45,7 @@ public class AvatarViewerDialog extends Dialog {
|
|||
this.avatarResId = resId;
|
||||
this.avatarUri = null;
|
||||
this.avatarDrawable = null;
|
||||
this.avatarUrl = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -50,6 +53,15 @@ public class AvatarViewerDialog extends Dialog {
|
|||
this.avatarDrawable = drawable;
|
||||
this.avatarUri = null;
|
||||
this.avatarResId = null;
|
||||
this.avatarUrl = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AvatarViewerDialog setAvatarUrl(String url) {
|
||||
this.avatarUrl = url;
|
||||
this.avatarUri = null;
|
||||
this.avatarResId = null;
|
||||
this.avatarDrawable = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -123,7 +135,14 @@ public class AvatarViewerDialog extends Dialog {
|
|||
}
|
||||
|
||||
try {
|
||||
if (avatarUri != null) {
|
||||
if (avatarUrl != null && !avatarUrl.isEmpty()) {
|
||||
Glide.with(context)
|
||||
.load(avatarUrl)
|
||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
||||
.placeholder(R.drawable.ic_account_circle_24)
|
||||
.error(R.drawable.ic_account_circle_24)
|
||||
.into(avatarImageView);
|
||||
} else if (avatarUri != null) {
|
||||
Glide.with(context)
|
||||
.load(avatarUri)
|
||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
||||
|
|
@ -158,5 +177,4 @@ public class AvatarViewerDialog extends Dialog {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,296 @@
|
|||
package com.example.livestreaming;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import com.example.livestreaming.net.ApiClient;
|
||||
import com.example.livestreaming.net.ApiResponse;
|
||||
import com.example.livestreaming.net.ApiService;
|
||||
import com.example.livestreaming.net.CommunityResponse;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
/**
|
||||
* 板块消息页面基类
|
||||
* 从后端API加载对应板块的消息列表
|
||||
*/
|
||||
public abstract class BaseCategoryActivity extends AppCompatActivity {
|
||||
|
||||
protected ApiService apiService;
|
||||
protected PostAdapter postAdapter;
|
||||
protected RecyclerView recyclerView;
|
||||
protected View emptyView;
|
||||
protected SwipeRefreshLayout swipeRefresh;
|
||||
protected View fabPublish;
|
||||
|
||||
private int currentPage = 1;
|
||||
private boolean isLoading = false;
|
||||
private boolean hasMore = true;
|
||||
private int resolvedCategoryId = -1; // 从API获取的实际板块ID
|
||||
|
||||
/**
|
||||
* 获取板块ID(子类实现,如果返回-1则通过名称查找)
|
||||
*/
|
||||
protected abstract int getCategoryId();
|
||||
|
||||
/**
|
||||
* 获取板块名称(子类实现)
|
||||
*/
|
||||
protected abstract String getCategoryName();
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
apiService = ApiClient.getService(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化消息列表(子类在setupUI后调用)
|
||||
*/
|
||||
protected void initMessageList(RecyclerView recyclerView, View emptyView,
|
||||
SwipeRefreshLayout swipeRefresh, View fabPublish) {
|
||||
this.recyclerView = recyclerView;
|
||||
this.emptyView = emptyView;
|
||||
this.swipeRefresh = swipeRefresh;
|
||||
this.fabPublish = fabPublish;
|
||||
|
||||
// 设置RecyclerView
|
||||
postAdapter = new PostAdapter(this);
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||||
recyclerView.setAdapter(postAdapter);
|
||||
|
||||
// 下拉刷新
|
||||
if (swipeRefresh != null) {
|
||||
swipeRefresh.setOnRefreshListener(this::refreshMessages);
|
||||
}
|
||||
|
||||
// 上拉加载更多
|
||||
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
@Override
|
||||
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
|
||||
super.onScrolled(recyclerView, dx, dy);
|
||||
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
|
||||
if (layoutManager != null && !isLoading && hasMore) {
|
||||
int lastVisible = layoutManager.findLastVisibleItemPosition();
|
||||
int total = layoutManager.getItemCount();
|
||||
if (lastVisible >= total - 3) {
|
||||
loadMoreMessages();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 发布按钮
|
||||
if (fabPublish != null) {
|
||||
fabPublish.setOnClickListener(v -> showPublishDialog());
|
||||
}
|
||||
|
||||
// 先获取板块ID,再加载数据
|
||||
resolveCategoryIdAndLoad();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析板块ID并加载数据
|
||||
*/
|
||||
private void resolveCategoryIdAndLoad() {
|
||||
int staticId = getCategoryId();
|
||||
if (staticId > 0) {
|
||||
resolvedCategoryId = staticId;
|
||||
loadMessages();
|
||||
return;
|
||||
}
|
||||
|
||||
// 通过名称查找板块ID
|
||||
apiService.getCommunityCategories().enqueue(new Callback<ApiResponse<List<CommunityResponse.Category>>>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse<List<CommunityResponse.Category>>> call,
|
||||
@NonNull Response<ApiResponse<List<CommunityResponse.Category>>> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().isOk()) {
|
||||
List<CommunityResponse.Category> categories = response.body().getData();
|
||||
if (categories != null) {
|
||||
String targetName = getCategoryName();
|
||||
for (CommunityResponse.Category cat : categories) {
|
||||
if (targetName.equals(cat.getName())) {
|
||||
resolvedCategoryId = cat.getId();
|
||||
android.util.Log.d("BaseCategoryActivity", "找到板块: " + targetName + " -> ID=" + resolvedCategoryId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
loadMessages();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse<List<CommunityResponse.Category>>> call, @NonNull Throwable t) {
|
||||
android.util.Log.e("BaseCategoryActivity", "获取板块列表失败", t);
|
||||
loadMessages();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新消息
|
||||
*/
|
||||
protected void refreshMessages() {
|
||||
currentPage = 1;
|
||||
hasMore = true;
|
||||
loadMessages();
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载更多消息
|
||||
*/
|
||||
protected void loadMoreMessages() {
|
||||
if (!isLoading && hasMore) {
|
||||
currentPage++;
|
||||
loadMessages();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从API加载消息
|
||||
*/
|
||||
protected void loadMessages() {
|
||||
if (isLoading) return;
|
||||
isLoading = true;
|
||||
|
||||
int categoryId = resolvedCategoryId > 0 ? resolvedCategoryId : getCategoryId();
|
||||
android.util.Log.d("BaseCategoryActivity", "加载消息: categoryId=" + categoryId + ", page=" + currentPage);
|
||||
|
||||
apiService.getCommunityMessages(categoryId, currentPage, 20)
|
||||
.enqueue(new Callback<ApiResponse<CommunityResponse.MessagePage>>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse<CommunityResponse.MessagePage>> call,
|
||||
@NonNull Response<ApiResponse<CommunityResponse.MessagePage>> response) {
|
||||
isLoading = false;
|
||||
if (swipeRefresh != null) {
|
||||
swipeRefresh.setRefreshing(false);
|
||||
}
|
||||
|
||||
if (response.isSuccessful() && response.body() != null && response.body().isOk()) {
|
||||
CommunityResponse.MessagePage data = response.body().getData();
|
||||
if (data != null && data.list != null) {
|
||||
List<Post> posts = convertToPosts(data.list);
|
||||
android.util.Log.d("BaseCategoryActivity", "加载到 " + posts.size() + " 条消息");
|
||||
|
||||
if (currentPage == 1) {
|
||||
postAdapter.setPosts(posts);
|
||||
} else {
|
||||
postAdapter.addPosts(posts);
|
||||
}
|
||||
|
||||
hasMore = data.list.size() >= 20;
|
||||
} else {
|
||||
if (currentPage == 1) {
|
||||
postAdapter.setPosts(new ArrayList<>());
|
||||
}
|
||||
hasMore = false;
|
||||
}
|
||||
} else {
|
||||
android.util.Log.e("BaseCategoryActivity", "加载消息失败: " + response.code());
|
||||
if (currentPage == 1) {
|
||||
postAdapter.setPosts(new ArrayList<>());
|
||||
}
|
||||
}
|
||||
updateEmptyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse<CommunityResponse.MessagePage>> call,
|
||||
@NonNull Throwable t) {
|
||||
isLoading = false;
|
||||
if (swipeRefresh != null) {
|
||||
swipeRefresh.setRefreshing(false);
|
||||
}
|
||||
android.util.Log.e("BaseCategoryActivity", "网络错误", t);
|
||||
Toast.makeText(BaseCategoryActivity.this, "加载失败,请检查网络", Toast.LENGTH_SHORT).show();
|
||||
updateEmptyView();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 将API返回的Message转换为Post对象
|
||||
*/
|
||||
protected List<Post> convertToPosts(List<CommunityResponse.Message> messages) {
|
||||
List<Post> posts = new ArrayList<>();
|
||||
for (CommunityResponse.Message msg : messages) {
|
||||
Post post = new Post();
|
||||
post.setId(msg.id);
|
||||
post.setAuthorName(msg.nickname);
|
||||
post.setAuthorAvatar(msg.avatar);
|
||||
post.setContent(msg.content);
|
||||
post.setCategory(getCategoryName());
|
||||
post.setLikeCount(msg.likeCount);
|
||||
post.setCommentCount(msg.commentCount);
|
||||
post.setCreateTime(msg.createTime);
|
||||
post.setLiked(msg.isLiked);
|
||||
|
||||
// 处理图片(后端返回逗号分隔的字符串)
|
||||
List<String> imageList = msg.getImageList();
|
||||
if (imageList != null && !imageList.isEmpty()) {
|
||||
post.setImagesList(imageList);
|
||||
}
|
||||
|
||||
posts.add(post);
|
||||
}
|
||||
return posts;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新空视图
|
||||
*/
|
||||
protected void updateEmptyView() {
|
||||
if (emptyView != null && recyclerView != null) {
|
||||
if (postAdapter.getItemCount() == 0) {
|
||||
emptyView.setVisibility(View.VISIBLE);
|
||||
recyclerView.setVisibility(View.GONE);
|
||||
} else {
|
||||
emptyView.setVisibility(View.GONE);
|
||||
recyclerView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示发布对话框
|
||||
*/
|
||||
protected void showPublishDialog() {
|
||||
int categoryId = resolvedCategoryId > 0 ? resolvedCategoryId : getCategoryId();
|
||||
PublishPostHelper.showPublishDialog(this, getCategoryName(), categoryId,
|
||||
new PublishPostHelper.PublishCallback() {
|
||||
@Override
|
||||
public void onSuccess(Post post) {
|
||||
// 刷新列表
|
||||
refreshMessages();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String error) {
|
||||
Toast.makeText(BaseCategoryActivity.this, error, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
// 每次返回页面时刷新数据
|
||||
if (postAdapter != null && resolvedCategoryId > 0) {
|
||||
refreshMessages();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,324 @@
|
|||
package com.example.livestreaming;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 板块适配器 - 动态加载后端板块数据
|
||||
* 支持 Element UI 图标映射到 Android drawable
|
||||
*/
|
||||
public class CategoryAdapter extends RecyclerView.Adapter<CategoryAdapter.ViewHolder> {
|
||||
|
||||
private final Context context;
|
||||
private final List<CategoryItem> categories = new ArrayList<>();
|
||||
private OnCategoryClickListener listener;
|
||||
|
||||
// Element UI 图标映射到 Android drawable
|
||||
private static final Map<String, Integer> ICON_MAP = new HashMap<>();
|
||||
static {
|
||||
// 默认图标
|
||||
ICON_MAP.put("default", R.drawable.ic_grid3x3_24);
|
||||
|
||||
// ========== Element UI 图标 (管理端使用) ==========
|
||||
// 用户/社交类 - 粉色用户图标
|
||||
ICON_MAP.put("el-icon-user", R.drawable.ic_user_24);
|
||||
ICON_MAP.put("el-icon-user-solid", R.drawable.ic_user_24);
|
||||
ICON_MAP.put("el-icon-s-custom", R.drawable.ic_user_24);
|
||||
|
||||
// 星星/收藏类 - 黄色星星
|
||||
ICON_MAP.put("el-icon-star-on", R.drawable.ic_star_24);
|
||||
ICON_MAP.put("el-icon-star-off", R.drawable.ic_star_24);
|
||||
ICON_MAP.put("el-icon-collection", R.drawable.ic_star_24);
|
||||
|
||||
// 手机/移动设备类 - 橙色手机
|
||||
ICON_MAP.put("el-icon-mobile", R.drawable.ic_smartphone_24);
|
||||
ICON_MAP.put("el-icon-mobile-phone", R.drawable.ic_smartphone_24);
|
||||
ICON_MAP.put("el-icon-phone", R.drawable.ic_smartphone_24);
|
||||
|
||||
// 耳机/KTV类 - 青色耳机
|
||||
ICON_MAP.put("el-icon-headset", R.drawable.ic_headset_24);
|
||||
ICON_MAP.put("el-icon-service", R.drawable.ic_headset_24);
|
||||
|
||||
// 麦克风/语音类 - 蓝色麦克风
|
||||
ICON_MAP.put("el-icon-microphone", R.drawable.ic_voice_24);
|
||||
ICON_MAP.put("el-icon-mic", R.drawable.ic_voice_24);
|
||||
|
||||
// 播放/视频类 - 橙红色播放
|
||||
ICON_MAP.put("el-icon-video-play", R.drawable.ic_play_circle_24);
|
||||
ICON_MAP.put("el-icon-caret-right", R.drawable.ic_play_circle_24);
|
||||
ICON_MAP.put("el-icon-video-camera", R.drawable.ic_play_circle_24);
|
||||
|
||||
// 瞄准/射击类 - 橙色瞄准镜
|
||||
ICON_MAP.put("el-icon-aim", R.drawable.ic_target_24);
|
||||
ICON_MAP.put("el-icon-position", R.drawable.ic_target_24);
|
||||
ICON_MAP.put("el-icon-place", R.drawable.ic_target_24);
|
||||
ICON_MAP.put("el-icon-location", R.drawable.ic_target_24);
|
||||
|
||||
// 九宫格/桌游类 - 青绿色网格
|
||||
ICON_MAP.put("el-icon-grid", R.drawable.ic_grid3x3_24);
|
||||
ICON_MAP.put("el-icon-menu", R.drawable.ic_grid3x3_24);
|
||||
ICON_MAP.put("el-icon-s-grid", R.drawable.ic_grid3x3_24);
|
||||
ICON_MAP.put("el-icon-s-operation", R.drawable.ic_grid3x3_24);
|
||||
|
||||
// 游戏手柄类
|
||||
ICON_MAP.put("el-icon-coordinate", R.drawable.ic_game_24);
|
||||
ICON_MAP.put("el-icon-trophy", R.drawable.ic_game_24);
|
||||
|
||||
// 编辑/绘画类
|
||||
ICON_MAP.put("el-icon-edit", R.drawable.ic_palette_24);
|
||||
ICON_MAP.put("el-icon-edit-outline", R.drawable.ic_palette_24);
|
||||
ICON_MAP.put("el-icon-brush", R.drawable.ic_palette_24);
|
||||
|
||||
// 爱心类
|
||||
ICON_MAP.put("el-icon-chat-dot-round", R.drawable.ic_heart_24);
|
||||
ICON_MAP.put("el-icon-chat-line-round", R.drawable.ic_heart_24);
|
||||
|
||||
// ========== Lucide 图标名称 ==========
|
||||
ICON_MAP.put("Microphone", R.drawable.ic_voice_24);
|
||||
ICON_MAP.put("Star", R.drawable.ic_star_24);
|
||||
ICON_MAP.put("User", R.drawable.ic_user_24);
|
||||
ICON_MAP.put("Users", R.drawable.ic_user_24);
|
||||
ICON_MAP.put("Heart", R.drawable.ic_heart_24);
|
||||
ICON_MAP.put("Gamepad2", R.drawable.ic_game_24);
|
||||
ICON_MAP.put("Gamepad", R.drawable.ic_game_24);
|
||||
ICON_MAP.put("Smartphone", R.drawable.ic_smartphone_24);
|
||||
ICON_MAP.put("Music", R.drawable.ic_headset_24);
|
||||
ICON_MAP.put("Music2", R.drawable.ic_headset_24);
|
||||
ICON_MAP.put("Headphones", R.drawable.ic_headset_24);
|
||||
ICON_MAP.put("Pencil", R.drawable.ic_palette_24);
|
||||
ICON_MAP.put("Palette", R.drawable.ic_palette_24);
|
||||
ICON_MAP.put("Target", R.drawable.ic_target_24);
|
||||
ICON_MAP.put("Crosshair", R.drawable.ic_target_24);
|
||||
ICON_MAP.put("Dices", R.drawable.ic_grid3x3_24);
|
||||
ICON_MAP.put("Grid3x3", R.drawable.ic_grid3x3_24);
|
||||
ICON_MAP.put("Grid", R.drawable.ic_grid3x3_24);
|
||||
ICON_MAP.put("CirclePlay", R.drawable.ic_play_circle_24);
|
||||
ICON_MAP.put("Play", R.drawable.ic_play_circle_24);
|
||||
|
||||
// ========== 简单名称映射 ==========
|
||||
ICON_MAP.put("user", R.drawable.ic_user_24);
|
||||
ICON_MAP.put("star", R.drawable.ic_star_24);
|
||||
ICON_MAP.put("heart", R.drawable.ic_heart_24);
|
||||
ICON_MAP.put("smartphone", R.drawable.ic_smartphone_24);
|
||||
ICON_MAP.put("headset", R.drawable.ic_headset_24);
|
||||
ICON_MAP.put("play", R.drawable.ic_play_circle_24);
|
||||
ICON_MAP.put("target", R.drawable.ic_target_24);
|
||||
ICON_MAP.put("grid", R.drawable.ic_grid3x3_24);
|
||||
ICON_MAP.put("game", R.drawable.ic_game_24);
|
||||
ICON_MAP.put("palette", R.drawable.ic_palette_24);
|
||||
ICON_MAP.put("message", R.drawable.ic_message_24);
|
||||
ICON_MAP.put("ChatDotRound", R.drawable.ic_message_24);
|
||||
}
|
||||
|
||||
public interface OnCategoryClickListener {
|
||||
void onCategoryClick(CategoryItem category);
|
||||
}
|
||||
|
||||
public CategoryAdapter(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public void setOnCategoryClickListener(OnCategoryClickListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void setCategories(List<CategoryItem> newCategories) {
|
||||
categories.clear();
|
||||
if (newCategories != null) {
|
||||
categories.addAll(newCategories);
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(context).inflate(R.layout.item_category_card, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
CategoryItem item = categories.get(position);
|
||||
holder.bind(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return categories.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据图标名称获取对应的 drawable 资源
|
||||
*/
|
||||
private int getIconResource(String iconName) {
|
||||
if (iconName == null || iconName.isEmpty()) {
|
||||
android.util.Log.d("CategoryAdapter", "图标名称为空,使用默认图标");
|
||||
return ICON_MAP.get("default");
|
||||
}
|
||||
|
||||
// 1. 精确匹配
|
||||
Integer res = ICON_MAP.get(iconName);
|
||||
if (res != null) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// 2. 去除空格后匹配
|
||||
String trimmed = iconName.trim();
|
||||
res = ICON_MAP.get(trimmed);
|
||||
if (res != null) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// 3. 小写匹配(遍历所有key)
|
||||
String lowerName = trimmed.toLowerCase();
|
||||
for (Map.Entry<String, Integer> entry : ICON_MAP.entrySet()) {
|
||||
if (entry.getKey().toLowerCase().equals(lowerName)) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 部分匹配(图标名称包含关键词)
|
||||
if (lowerName.contains("star") || lowerName.contains("collection")) {
|
||||
return R.drawable.ic_star_24;
|
||||
}
|
||||
if (lowerName.contains("user") || lowerName.contains("custom")) {
|
||||
return R.drawable.ic_user_24;
|
||||
}
|
||||
if (lowerName.contains("heart") || lowerName.contains("chat")) {
|
||||
return R.drawable.ic_heart_24;
|
||||
}
|
||||
if (lowerName.contains("mobile") || lowerName.contains("phone") || lowerName.contains("smartphone")) {
|
||||
return R.drawable.ic_smartphone_24;
|
||||
}
|
||||
if (lowerName.contains("headset") || lowerName.contains("headphone") || lowerName.contains("service") || lowerName.contains("music")) {
|
||||
return R.drawable.ic_headset_24;
|
||||
}
|
||||
if (lowerName.contains("video") || lowerName.contains("play") || lowerName.contains("caret")) {
|
||||
return R.drawable.ic_play_circle_24;
|
||||
}
|
||||
if (lowerName.contains("game") || lowerName.contains("trophy") || lowerName.contains("coordinate")) {
|
||||
return R.drawable.ic_game_24;
|
||||
}
|
||||
if (lowerName.contains("mic") || lowerName.contains("voice")) {
|
||||
return R.drawable.ic_voice_24;
|
||||
}
|
||||
if (lowerName.contains("edit") || lowerName.contains("pencil") || lowerName.contains("brush") || lowerName.contains("palette")) {
|
||||
return R.drawable.ic_palette_24;
|
||||
}
|
||||
if (lowerName.contains("aim") || lowerName.contains("target") || lowerName.contains("crosshair") || lowerName.contains("position") || lowerName.contains("location")) {
|
||||
return R.drawable.ic_target_24;
|
||||
}
|
||||
if (lowerName.contains("grid") || lowerName.contains("dice") || lowerName.contains("menu")) {
|
||||
return R.drawable.ic_grid3x3_24;
|
||||
}
|
||||
if (lowerName.contains("message")) {
|
||||
return R.drawable.ic_message_24;
|
||||
}
|
||||
|
||||
android.util.Log.w("CategoryAdapter", "未找到图标映射: " + iconName + ",使用默认图标");
|
||||
return ICON_MAP.get("default");
|
||||
}
|
||||
|
||||
class ViewHolder extends RecyclerView.ViewHolder {
|
||||
private final ImageView iconView;
|
||||
private final TextView nameView;
|
||||
private final View cardView;
|
||||
|
||||
ViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
cardView = itemView;
|
||||
iconView = itemView.findViewById(R.id.categoryIcon);
|
||||
nameView = itemView.findViewById(R.id.categoryName);
|
||||
}
|
||||
|
||||
void bind(CategoryItem item) {
|
||||
nameView.setText(item.getName());
|
||||
iconView.setImageResource(getIconResource(item.getIcon()));
|
||||
|
||||
cardView.setOnClickListener(v -> {
|
||||
if (listener != null) {
|
||||
listener.onCategoryClick(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 板块数据类
|
||||
*/
|
||||
public static class CategoryItem {
|
||||
private int id;
|
||||
private String name;
|
||||
private String icon;
|
||||
private String description;
|
||||
private String jumpPage; // 跳转页面Activity名称
|
||||
private String type; // 类型: quick=快捷入口 card=功能卡片
|
||||
private int sort;
|
||||
private int status;
|
||||
|
||||
public CategoryItem() {}
|
||||
|
||||
public CategoryItem(int id, String name, String icon) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
public CategoryItem(int id, String name, String icon, String jumpPage) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.icon = icon;
|
||||
this.jumpPage = jumpPage;
|
||||
}
|
||||
|
||||
public int getId() { return id; }
|
||||
public void setId(int id) { this.id = id; }
|
||||
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
|
||||
public String getIcon() { return icon; }
|
||||
public void setIcon(String icon) { this.icon = icon; }
|
||||
|
||||
public String getDescription() { return description; }
|
||||
public void setDescription(String description) { this.description = description; }
|
||||
|
||||
public String getJumpPage() { return jumpPage; }
|
||||
public void setJumpPage(String jumpPage) { this.jumpPage = jumpPage; }
|
||||
|
||||
public String getType() { return type; }
|
||||
public void setType(String type) { this.type = type; }
|
||||
|
||||
public int getSort() { return sort; }
|
||||
public void setSort(int sort) { this.sort = sort; }
|
||||
|
||||
public int getStatus() { return status; }
|
||||
public void setStatus(int status) { this.status = status; }
|
||||
|
||||
/**
|
||||
* 从 CommunityResponse.Category 转换
|
||||
*/
|
||||
public static CategoryItem fromCategory(com.example.livestreaming.net.CommunityResponse.Category cat) {
|
||||
CategoryItem item = new CategoryItem();
|
||||
item.setId(cat.getId());
|
||||
item.setName(cat.getName());
|
||||
item.setIcon(cat.getIcon());
|
||||
item.setJumpPage(cat.getJumpPage());
|
||||
item.setType(cat.getType());
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
package com.example.livestreaming;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 频道管理适配器
|
||||
* 用于显示我的频道和推荐频道
|
||||
*/
|
||||
public class ChannelManagerAdapter extends RecyclerView.Adapter<ChannelManagerAdapter.ViewHolder> {
|
||||
|
||||
public interface OnChannelClickListener {
|
||||
void onChannelClick(ChannelItem item, int position);
|
||||
void onChannelDelete(ChannelItem item, int position);
|
||||
void onChannelAdd(ChannelItem item, int position);
|
||||
}
|
||||
|
||||
private final List<ChannelItem> items = new ArrayList<>();
|
||||
private OnChannelClickListener listener;
|
||||
private boolean isEditMode = false;
|
||||
private boolean isRecommendMode = false; // 是否是推荐频道模式
|
||||
private int fixedCount = 4; // 前4个固定不能删除
|
||||
|
||||
public void setOnChannelClickListener(OnChannelClickListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void setEditMode(boolean editMode) {
|
||||
this.isEditMode = editMode;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void setRecommendMode(boolean recommendMode) {
|
||||
this.isRecommendMode = recommendMode;
|
||||
}
|
||||
|
||||
public void setFixedCount(int count) {
|
||||
this.fixedCount = count;
|
||||
}
|
||||
|
||||
public void submitList(List<ChannelItem> newItems) {
|
||||
items.clear();
|
||||
if (newItems != null) {
|
||||
items.addAll(newItems);
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public List<ChannelItem> getItems() {
|
||||
return new ArrayList<>(items);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_channel_tag, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
ChannelItem item = items.get(position);
|
||||
holder.bind(item, position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return items.size();
|
||||
}
|
||||
|
||||
class ViewHolder extends RecyclerView.ViewHolder {
|
||||
private final TextView channelName;
|
||||
private final ImageView fixedIcon;
|
||||
private final ImageView deleteIcon;
|
||||
private final ImageView addIcon;
|
||||
|
||||
ViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
channelName = itemView.findViewById(R.id.channelName);
|
||||
fixedIcon = itemView.findViewById(R.id.fixedIcon);
|
||||
deleteIcon = itemView.findViewById(R.id.deleteIcon);
|
||||
addIcon = itemView.findViewById(R.id.addIcon);
|
||||
}
|
||||
|
||||
void bind(ChannelItem item, int position) {
|
||||
channelName.setText(item.getName());
|
||||
|
||||
// 重置所有图标状态
|
||||
fixedIcon.setVisibility(View.GONE);
|
||||
deleteIcon.setVisibility(View.GONE);
|
||||
addIcon.setVisibility(View.GONE);
|
||||
|
||||
if (isRecommendMode) {
|
||||
// 推荐频道模式:显示添加图标
|
||||
addIcon.setVisibility(View.VISIBLE);
|
||||
itemView.setOnClickListener(v -> {
|
||||
if (listener != null) {
|
||||
listener.onChannelAdd(item, position);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 我的频道模式
|
||||
boolean isFixed = position < fixedCount;
|
||||
|
||||
if (isEditMode) {
|
||||
// 编辑模式
|
||||
if (isFixed) {
|
||||
fixedIcon.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
deleteIcon.setVisibility(View.VISIBLE);
|
||||
deleteIcon.setOnClickListener(v -> {
|
||||
if (listener != null) {
|
||||
listener.onChannelDelete(item, position);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
itemView.setOnClickListener(v -> {
|
||||
if (listener != null) {
|
||||
listener.onChannelClick(item, position);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 频道项数据类
|
||||
*/
|
||||
public static class ChannelItem {
|
||||
private final String id;
|
||||
private final String name;
|
||||
private boolean isFixed;
|
||||
|
||||
public ChannelItem(String id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.isFixed = false;
|
||||
}
|
||||
|
||||
public ChannelItem(String id, String name, boolean isFixed) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.isFixed = isFixed;
|
||||
}
|
||||
|
||||
public String getId() { return id; }
|
||||
public String getName() { return name; }
|
||||
public boolean isFixed() { return isFixed; }
|
||||
public void setFixed(boolean fixed) { this.isFixed = fixed; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
package com.example.livestreaming;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
import androidx.recyclerview.widget.ListAdapter;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
/**
|
||||
* 频道标签适配器(发现页面使用)
|
||||
*/
|
||||
public class ChannelTagAdapter extends ListAdapter<ChannelTagAdapter.ChannelTag, ChannelTagAdapter.ViewHolder> {
|
||||
|
||||
public interface OnTagClickListener {
|
||||
void onTagClick(ChannelTag tag, int position);
|
||||
void onTagAddClick(ChannelTag tag, int position); // 推荐频道的添加点击
|
||||
}
|
||||
|
||||
private OnTagClickListener listener;
|
||||
private boolean isRecommendMode = false; // 是否是推荐频道模式(显示+号)
|
||||
private int selectedPosition = -1;
|
||||
|
||||
public ChannelTagAdapter() {
|
||||
super(DIFF_CALLBACK);
|
||||
}
|
||||
|
||||
public void setOnTagClickListener(OnTagClickListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void setRecommendMode(boolean recommendMode) {
|
||||
this.isRecommendMode = recommendMode;
|
||||
}
|
||||
|
||||
public void setSelectedPosition(int position) {
|
||||
int oldPosition = this.selectedPosition;
|
||||
this.selectedPosition = position;
|
||||
if (oldPosition >= 0) notifyItemChanged(oldPosition);
|
||||
if (position >= 0) notifyItemChanged(position);
|
||||
}
|
||||
|
||||
private static final DiffUtil.ItemCallback<ChannelTag> DIFF_CALLBACK = new DiffUtil.ItemCallback<ChannelTag>() {
|
||||
@Override
|
||||
public boolean areItemsTheSame(@NonNull ChannelTag oldItem, @NonNull ChannelTag newItem) {
|
||||
return oldItem.getId() == newItem.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(@NonNull ChannelTag oldItem, @NonNull ChannelTag newItem) {
|
||||
return oldItem.equals(newItem);
|
||||
}
|
||||
};
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_channel_tag, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
ChannelTag tag = getItem(position);
|
||||
holder.bind(tag, position);
|
||||
}
|
||||
|
||||
class ViewHolder extends RecyclerView.ViewHolder {
|
||||
private final TextView tagText;
|
||||
|
||||
ViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
tagText = itemView.findViewById(R.id.channelName);
|
||||
}
|
||||
|
||||
void bind(ChannelTag tag, int position) {
|
||||
// 显示文本
|
||||
if (isRecommendMode) {
|
||||
tagText.setText("+ " + tag.getName());
|
||||
} else {
|
||||
tagText.setText(tag.getName());
|
||||
}
|
||||
|
||||
// 选中状态
|
||||
boolean isSelected = position == selectedPosition;
|
||||
if (isSelected) {
|
||||
tagText.setBackgroundResource(R.drawable.bg_channel_tag_selected);
|
||||
tagText.setTextColor(itemView.getContext().getResources().getColor(android.R.color.black, null));
|
||||
} else {
|
||||
tagText.setBackgroundResource(R.drawable.bg_channel_tag);
|
||||
tagText.setTextColor(itemView.getContext().getResources().getColor(android.R.color.darker_gray, null));
|
||||
}
|
||||
|
||||
// 点击事件
|
||||
itemView.setOnClickListener(v -> {
|
||||
if (listener != null) {
|
||||
if (isRecommendMode) {
|
||||
listener.onTagAddClick(tag, position);
|
||||
} else {
|
||||
listener.onTagClick(tag, position);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 频道标签数据类
|
||||
*/
|
||||
public static class ChannelTag {
|
||||
private int id;
|
||||
private String name;
|
||||
private String icon;
|
||||
private boolean isMyChannel; // 是否是我的频道
|
||||
|
||||
public ChannelTag(int id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.isMyChannel = false;
|
||||
}
|
||||
|
||||
public ChannelTag(int id, String name, boolean isMyChannel) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.isMyChannel = isMyChannel;
|
||||
}
|
||||
|
||||
public int getId() { return id; }
|
||||
public String getName() { return name; }
|
||||
public String getIcon() { return icon; }
|
||||
public void setIcon(String icon) { this.icon = icon; }
|
||||
public boolean isMyChannel() { return isMyChannel; }
|
||||
public void setMyChannel(boolean myChannel) { isMyChannel = myChannel; }
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ChannelTag that = (ChannelTag) o;
|
||||
return id == that.id && isMyChannel == that.isMyChannel
|
||||
&& java.util.Objects.equals(name, that.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return java.util.Objects.hash(id, name, isMyChannel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,25 +3,32 @@ 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 androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.example.livestreaming.databinding.ActivityDrawGuessBinding;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
|
||||
import java.util.List;
|
||||
/**
|
||||
* 电影讨论(你画我猜) - 从后端API加载消息
|
||||
*/
|
||||
public class DrawGuessActivity extends BaseCategoryActivity {
|
||||
|
||||
public class DrawGuessActivity extends AppCompatActivity {
|
||||
|
||||
private static final String CATEGORY = "你画我猜";
|
||||
private static final int CATEGORY_ID = -1; // 通过名称自动查找
|
||||
private static final String CATEGORY_NAME = "电影讨论";
|
||||
|
||||
private ActivityDrawGuessBinding binding;
|
||||
private PostAdapter postAdapter;
|
||||
|
||||
public static void start(Context context) {
|
||||
Intent intent = new Intent(context, DrawGuessActivity.class);
|
||||
context.startActivity(intent);
|
||||
context.startActivity(new Intent(context, DrawGuessActivity.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getCategoryId() {
|
||||
return CATEGORY_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCategoryName() {
|
||||
return CATEGORY_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -31,27 +38,12 @@ public class DrawGuessActivity extends AppCompatActivity {
|
|||
setContentView(binding.getRoot());
|
||||
|
||||
setupUI();
|
||||
setupRecyclerView();
|
||||
loadPosts();
|
||||
initMessageList(binding.recyclerPosts, binding.emptyView, null, binding.fabPublish);
|
||||
}
|
||||
|
||||
private void setupUI() {
|
||||
binding.backButton.setOnClickListener(v -> finish());
|
||||
|
||||
binding.fabPublish.setOnClickListener(v -> {
|
||||
PublishPostHelper.showPublishDialog(this, CATEGORY, new PublishPostHelper.PublishCallback() {
|
||||
@Override
|
||||
public void onSuccess(Post post) {
|
||||
postAdapter.addPost(post);
|
||||
binding.recyclerPosts.scrollToPosition(0);
|
||||
updateEmptyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String error) {}
|
||||
});
|
||||
});
|
||||
|
||||
BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation;
|
||||
bottomNavigation.setSelectedItemId(R.id.nav_friends);
|
||||
UnreadMessageManager.updateBadge(bottomNavigation);
|
||||
|
|
@ -81,28 +73,6 @@ public class DrawGuessActivity extends AppCompatActivity {
|
|||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void setupRecyclerView() {
|
||||
postAdapter = new PostAdapter();
|
||||
binding.recyclerPosts.setLayoutManager(new LinearLayoutManager(this));
|
||||
binding.recyclerPosts.setAdapter(postAdapter);
|
||||
}
|
||||
|
||||
private void loadPosts() {
|
||||
List<Post> posts = PostManager.getPostsByCategory(this, CATEGORY);
|
||||
postAdapter.setPosts(posts);
|
||||
updateEmptyView();
|
||||
}
|
||||
|
||||
private void updateEmptyView() {
|
||||
if (postAdapter.getItemCount() == 0) {
|
||||
binding.emptyView.setVisibility(View.VISIBLE);
|
||||
binding.recyclerPosts.setVisibility(View.GONE);
|
||||
} else {
|
||||
binding.emptyView.setVisibility(View.GONE);
|
||||
binding.recyclerPosts.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
|
|
@ -111,7 +81,6 @@ public class DrawGuessActivity extends AppCompatActivity {
|
|||
BottomNavigationView bottomNav = binding.bottomNavInclude.bottomNavigation;
|
||||
bottomNav.setSelectedItemId(R.id.nav_friends);
|
||||
UnreadMessageManager.updateBadge(bottomNav);
|
||||
loadPosts();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,233 @@
|
|||
package com.example.livestreaming;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import com.example.livestreaming.databinding.ActivityDynamicCommunityBinding;
|
||||
import com.example.livestreaming.net.ApiClient;
|
||||
import com.example.livestreaming.net.ApiResponse;
|
||||
import com.example.livestreaming.net.ApiService;
|
||||
import com.example.livestreaming.net.CommunityResponse;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
/**
|
||||
* 动态社区页面 - 根据板块ID动态加载消息列表
|
||||
*/
|
||||
public class DynamicCommunityActivity extends AppCompatActivity {
|
||||
|
||||
private static final String EXTRA_CATEGORY_ID = "category_id";
|
||||
private static final String EXTRA_CATEGORY_NAME = "category_name";
|
||||
|
||||
private ActivityDynamicCommunityBinding binding;
|
||||
private ApiService apiService;
|
||||
private PostAdapter postAdapter;
|
||||
|
||||
private int categoryId;
|
||||
private String categoryName;
|
||||
private int currentPage = 1;
|
||||
private boolean isLoading = false;
|
||||
private boolean hasMore = true;
|
||||
|
||||
public static void start(Context context, int categoryId, String categoryName) {
|
||||
Intent intent = new Intent(context, DynamicCommunityActivity.class);
|
||||
intent.putExtra(EXTRA_CATEGORY_ID, categoryId);
|
||||
intent.putExtra(EXTRA_CATEGORY_NAME, categoryName);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
binding = ActivityDynamicCommunityBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
categoryId = getIntent().getIntExtra(EXTRA_CATEGORY_ID, 0);
|
||||
categoryName = getIntent().getStringExtra(EXTRA_CATEGORY_NAME);
|
||||
|
||||
apiService = ApiClient.getService(this);
|
||||
|
||||
setupUI();
|
||||
setupRecyclerView();
|
||||
setupSwipeRefresh();
|
||||
setupBottomNav();
|
||||
|
||||
loadMessages(true);
|
||||
}
|
||||
|
||||
private void setupUI() {
|
||||
binding.titleText.setText(categoryName != null ? categoryName : "动态社区");
|
||||
binding.backButton.setOnClickListener(v -> finish());
|
||||
|
||||
// 发布按钮
|
||||
binding.fabPublish.setOnClickListener(v -> {
|
||||
// TODO: 打开发布消息页面
|
||||
Toast.makeText(this, "发布功能开发中", Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
}
|
||||
|
||||
private void setupRecyclerView() {
|
||||
postAdapter = new PostAdapter(this);
|
||||
binding.recyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||||
binding.recyclerView.setAdapter(postAdapter);
|
||||
|
||||
postAdapter.setOnItemClickListener(post -> {
|
||||
// 点击帖子查看详情
|
||||
Toast.makeText(this, "查看: " + post.getContent(), Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
|
||||
// 加载更多
|
||||
binding.recyclerView.addOnScrollListener(new androidx.recyclerview.widget.RecyclerView.OnScrollListener() {
|
||||
@Override
|
||||
public void onScrolled(@NonNull androidx.recyclerview.widget.RecyclerView recyclerView, int dx, int dy) {
|
||||
super.onScrolled(recyclerView, dx, dy);
|
||||
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
|
||||
if (layoutManager != null) {
|
||||
int totalItemCount = layoutManager.getItemCount();
|
||||
int lastVisibleItem = layoutManager.findLastVisibleItemPosition();
|
||||
if (!isLoading && hasMore && lastVisibleItem >= totalItemCount - 3) {
|
||||
loadMessages(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefresh.setColorSchemeResources(R.color.purple_500, R.color.pink_500);
|
||||
binding.swipeRefresh.setOnRefreshListener(() -> loadMessages(true));
|
||||
}
|
||||
|
||||
private void setupBottomNav() {
|
||||
BottomNavigationView bottomNav = binding.bottomNavInclude.bottomNavigation;
|
||||
bottomNav.setSelectedItemId(R.id.nav_friends);
|
||||
UnreadMessageManager.updateBadge(bottomNav);
|
||||
|
||||
bottomNav.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_friends) {
|
||||
startActivity(new Intent(this, FishPondActivity.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;
|
||||
});
|
||||
}
|
||||
|
||||
private void loadMessages(boolean refresh) {
|
||||
if (isLoading) return;
|
||||
isLoading = true;
|
||||
|
||||
if (refresh) {
|
||||
currentPage = 1;
|
||||
hasMore = true;
|
||||
}
|
||||
|
||||
apiService.getCommunityMessages(categoryId, currentPage, 20).enqueue(new Callback<ApiResponse<CommunityResponse.MessagePage>>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse<CommunityResponse.MessagePage>> call,
|
||||
@NonNull Response<ApiResponse<CommunityResponse.MessagePage>> response) {
|
||||
isLoading = false;
|
||||
binding.swipeRefresh.setRefreshing(false);
|
||||
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getData() != null) {
|
||||
CommunityResponse.MessagePage page = response.body().getData();
|
||||
List<Post> posts = convertToPosts(page.list);
|
||||
|
||||
if (refresh) {
|
||||
postAdapter.setPosts(posts);
|
||||
} else {
|
||||
postAdapter.addPosts(posts);
|
||||
}
|
||||
|
||||
hasMore = page.list != null && page.list.size() >= 20;
|
||||
currentPage++;
|
||||
|
||||
updateEmptyState(postAdapter.getItemCount() == 0);
|
||||
} else {
|
||||
if (refresh) {
|
||||
updateEmptyState(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse<CommunityResponse.MessagePage>> call, @NonNull Throwable t) {
|
||||
isLoading = false;
|
||||
binding.swipeRefresh.setRefreshing(false);
|
||||
Toast.makeText(DynamicCommunityActivity.this, "加载失败", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private List<Post> convertToPosts(List<CommunityResponse.Message> messages) {
|
||||
List<Post> posts = new ArrayList<>();
|
||||
if (messages == null) return posts;
|
||||
|
||||
for (CommunityResponse.Message msg : messages) {
|
||||
Post post = new Post();
|
||||
post.setId(msg.id);
|
||||
post.setContent(msg.content);
|
||||
post.setAuthorName(msg.nickname);
|
||||
post.setAuthorAvatar(msg.avatar);
|
||||
post.setImagesList(msg.getImageList()); // 使用getImageList方法获取图片列表
|
||||
post.setLikeCount(msg.likeCount);
|
||||
post.setCommentCount(msg.commentCount);
|
||||
post.setCreateTime(msg.createTime);
|
||||
posts.add(post);
|
||||
}
|
||||
return posts;
|
||||
}
|
||||
|
||||
private void updateEmptyState(boolean isEmpty) {
|
||||
if (isEmpty) {
|
||||
binding.emptyView.setVisibility(View.VISIBLE);
|
||||
binding.recyclerView.setVisibility(View.GONE);
|
||||
} else {
|
||||
binding.emptyView.setVisibility(View.GONE);
|
||||
binding.recyclerView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
if (binding != null) {
|
||||
UnreadMessageManager.updateBadge(binding.bottomNavInclude.bottomNavigation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,10 +4,13 @@ import android.Manifest;
|
|||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.OpenableColumns;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
|
@ -28,6 +31,11 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.example.livestreaming.databinding.ActivityEditProfileBinding;
|
||||
import com.example.livestreaming.net.ApiClient;
|
||||
import com.example.livestreaming.net.ApiResponse;
|
||||
import com.example.livestreaming.net.ApiService;
|
||||
import com.example.livestreaming.net.FileUploadResponse;
|
||||
import com.example.livestreaming.net.UserEditRequest;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog;
|
||||
import android.widget.NumberPicker;
|
||||
|
||||
|
|
@ -40,14 +48,24 @@ import java.util.Calendar;
|
|||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.MultipartBody;
|
||||
import okhttp3.RequestBody;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
public class EditProfileActivity extends AppCompatActivity {
|
||||
|
||||
private static final String TAG = "EditProfileActivity";
|
||||
private ActivityEditProfileBinding binding;
|
||||
private ApiService apiService;
|
||||
|
||||
private static final String PREFS_NAME = "profile_prefs";
|
||||
private static final String KEY_NAME = "profile_name";
|
||||
private static final String KEY_BIO = "profile_bio";
|
||||
private static final String KEY_AVATAR_URI = "profile_avatar_uri";
|
||||
private static final String KEY_AVATAR_URL = "profile_avatar_url"; // 服务器返回的URL
|
||||
private static final String KEY_AVATAR_RES = "profile_avatar_res";
|
||||
private static final String KEY_BIRTHDAY = "profile_birthday";
|
||||
private static final String KEY_GENDER = "profile_gender";
|
||||
|
|
@ -57,6 +75,8 @@ public class EditProfileActivity extends AppCompatActivity {
|
|||
|
||||
private Uri selectedAvatarUri = null;
|
||||
private Uri pendingCameraUri = null;
|
||||
private String uploadedAvatarUrl = null; // 上传后的服务器URL
|
||||
private boolean isUploading = false;
|
||||
|
||||
private ActivityResultLauncher<String> pickImageLauncher;
|
||||
private ActivityResultLauncher<Uri> takePictureLauncher;
|
||||
|
|
@ -79,10 +99,13 @@ public class EditProfileActivity extends AppCompatActivity {
|
|||
|
||||
binding = ActivityEditProfileBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
apiService = ApiClient.getService(this);
|
||||
|
||||
pickImageLauncher = registerForActivityResult(new ActivityResultContracts.GetContent(), uri -> {
|
||||
if (uri == null) return;
|
||||
selectedAvatarUri = uri;
|
||||
uploadedAvatarUrl = null; // 重置上传URL
|
||||
Glide.with(this).load(uri).circleCrop().into(binding.avatarPreview);
|
||||
});
|
||||
|
||||
|
|
@ -90,6 +113,7 @@ public class EditProfileActivity extends AppCompatActivity {
|
|||
if (!success) return;
|
||||
if (pendingCameraUri == null) return;
|
||||
selectedAvatarUri = pendingCameraUri;
|
||||
uploadedAvatarUrl = null; // 重置上传URL
|
||||
Glide.with(this).load(pendingCameraUri).circleCrop().into(binding.avatarPreview);
|
||||
});
|
||||
|
||||
|
|
@ -142,95 +166,266 @@ public class EditProfileActivity extends AppCompatActivity {
|
|||
dialog.show();
|
||||
});
|
||||
|
||||
binding.saveButton.setOnClickListener(v -> {
|
||||
// TODO: 接入后端接口 - 上传头像
|
||||
// 接口路径: POST /api/users/{userId}/avatar
|
||||
// 请求参数:
|
||||
// - userId: 用户ID(从token中获取)
|
||||
// - avatar: 头像文件(multipart/form-data)
|
||||
// 返回数据格式: ApiResponse<{avatarUrl: string}>
|
||||
// 上传成功后,保存avatarUrl到本地,并更新界面显示
|
||||
// TODO: 接入后端接口 - 更新用户资料
|
||||
// 接口路径: PUT /api/users/{userId}/profile
|
||||
// 请求参数:
|
||||
// - userId: 用户ID(从token中获取)
|
||||
// - name: 昵称
|
||||
// - bio: 个人签名
|
||||
// - birthday: 生日(格式:yyyy-MM-dd)
|
||||
// - gender: 性别(男/女/保密)
|
||||
// - location: 所在地(格式:省份-城市)
|
||||
// 返回数据格式: ApiResponse<UserProfile>
|
||||
// 更新成功后,同步更新本地缓存和界面显示
|
||||
String name = binding.inputName.getText() != null ? binding.inputName.getText().toString().trim() : "";
|
||||
String bio = binding.inputBio.getText() != null ? binding.inputBio.getText().toString().trim() : "";
|
||||
String birthday = binding.inputBirthday.getText() != null ? binding.inputBirthday.getText().toString().trim() : "";
|
||||
String gender = binding.inputGender.getText() != null ? binding.inputGender.getText().toString().trim() : "";
|
||||
String location = binding.inputLocation.getText() != null ? binding.inputLocation.getText().toString().trim() : "";
|
||||
binding.saveButton.setOnClickListener(v -> saveProfile());
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(name)) {
|
||||
Toast.makeText(this, "昵称不能为空", Toast.LENGTH_SHORT).show();
|
||||
/**
|
||||
* 保存用户资料
|
||||
*/
|
||||
private void saveProfile() {
|
||||
if (isUploading) {
|
||||
Toast.makeText(this, "正在保存中,请稍候...", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
String name = binding.inputName.getText() != null ? binding.inputName.getText().toString().trim() : "";
|
||||
String bio = binding.inputBio.getText() != null ? binding.inputBio.getText().toString().trim() : "";
|
||||
String birthday = binding.inputBirthday.getText() != null ? binding.inputBirthday.getText().toString().trim() : "";
|
||||
String gender = binding.inputGender.getText() != null ? binding.inputGender.getText().toString().trim() : "";
|
||||
String location = binding.inputLocation.getText() != null ? binding.inputLocation.getText().toString().trim() : "";
|
||||
|
||||
if (TextUtils.isEmpty(name)) {
|
||||
Toast.makeText(this, "昵称不能为空", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果选择了新头像,先上传头像
|
||||
if (selectedAvatarUri != null && uploadedAvatarUrl == null) {
|
||||
isUploading = true;
|
||||
binding.saveButton.setEnabled(false);
|
||||
Toast.makeText(this, "正在上传头像...", Toast.LENGTH_SHORT).show();
|
||||
|
||||
uploadAvatar(selectedAvatarUri, avatarUrl -> {
|
||||
uploadedAvatarUrl = avatarUrl;
|
||||
// 头像上传成功后,更新用户资料
|
||||
updateUserProfile(name, bio, birthday, gender, location, avatarUrl);
|
||||
}, error -> {
|
||||
isUploading = false;
|
||||
binding.saveButton.setEnabled(true);
|
||||
Toast.makeText(this, "头像上传失败: " + error, Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
} else {
|
||||
// 没有新头像,直接更新资料
|
||||
String existingAvatarUrl = getSharedPreferences(PREFS_NAME, MODE_PRIVATE).getString(KEY_AVATAR_URL, null);
|
||||
updateUserProfile(name, bio, birthday, gender, location, uploadedAvatarUrl != null ? uploadedAvatarUrl : existingAvatarUrl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传头像到服务器
|
||||
*/
|
||||
private void uploadAvatar(Uri imageUri, OnAvatarUploadSuccess onSuccess, OnAvatarUploadError onError) {
|
||||
try {
|
||||
// 获取文件名
|
||||
String fileName = getFileName(imageUri);
|
||||
if (fileName == null) {
|
||||
fileName = "avatar_" + System.currentTimeMillis() + ".jpg";
|
||||
}
|
||||
|
||||
// 读取文件内容
|
||||
InputStream inputStream = getContentResolver().openInputStream(imageUri);
|
||||
if (inputStream == null) {
|
||||
onError.onError("无法读取图片文件");
|
||||
return;
|
||||
}
|
||||
|
||||
getSharedPreferences(PREFS_NAME, MODE_PRIVATE)
|
||||
.edit()
|
||||
.putString(KEY_NAME, name)
|
||||
.apply();
|
||||
|
||||
if (TextUtils.isEmpty(bio) || BIO_HINT_TEXT.equals(bio)) {
|
||||
getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().remove(KEY_BIO).apply();
|
||||
} else {
|
||||
getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().putString(KEY_BIO, bio).apply();
|
||||
// 读取所有字节
|
||||
java.io.ByteArrayOutputStream buffer = new java.io.ByteArrayOutputStream();
|
||||
byte[] data = new byte[8192];
|
||||
int nRead;
|
||||
while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
|
||||
buffer.write(data, 0, nRead);
|
||||
}
|
||||
buffer.flush();
|
||||
byte[] fileBytes = buffer.toByteArray();
|
||||
inputStream.close();
|
||||
|
||||
if (selectedAvatarUri != null) {
|
||||
Uri persisted = persistAvatarToInternalStorage(selectedAvatarUri);
|
||||
if (persisted != null) {
|
||||
getSharedPreferences(PREFS_NAME, MODE_PRIVATE)
|
||||
.edit()
|
||||
.putString(KEY_AVATAR_URI, persisted.toString())
|
||||
.remove(KEY_AVATAR_RES)
|
||||
.apply();
|
||||
// 创建请求体
|
||||
RequestBody requestFile = RequestBody.create(MediaType.parse("image/*"), fileBytes);
|
||||
MultipartBody.Part body = MultipartBody.Part.createFormData("file", fileName, requestFile);
|
||||
RequestBody model = RequestBody.create(MediaType.parse("text/plain"), "user");
|
||||
RequestBody pid = RequestBody.create(MediaType.parse("text/plain"), "7"); // 7表示前台用户
|
||||
|
||||
// 调用上传接口
|
||||
apiService.uploadImage(body, model, pid).enqueue(new Callback<ApiResponse<FileUploadResponse>>() {
|
||||
@Override
|
||||
public void onResponse(Call<ApiResponse<FileUploadResponse>> call, Response<ApiResponse<FileUploadResponse>> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().isOk()) {
|
||||
FileUploadResponse data = response.body().getData();
|
||||
if (data != null && data.getUrl() != null) {
|
||||
Log.d(TAG, "头像上传成功: " + data.getUrl());
|
||||
onSuccess.onSuccess(data.getUrl());
|
||||
} else {
|
||||
onError.onError("服务器返回数据异常");
|
||||
}
|
||||
} else {
|
||||
String msg = response.body() != null ? response.body().getMessage() : "上传失败";
|
||||
onError.onError(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<ApiResponse<FileUploadResponse>> call, Throwable t) {
|
||||
Log.e(TAG, "头像上传网络错误: " + t.getMessage());
|
||||
onError.onError("网络错误: " + t.getMessage());
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "头像上传异常: " + e.getMessage());
|
||||
onError.onError("上传异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户资料到服务器
|
||||
*/
|
||||
private void updateUserProfile(String name, String bio, String birthday, String gender, String location, String avatarUrl) {
|
||||
UserEditRequest request = new UserEditRequest();
|
||||
request.setNickname(name);
|
||||
if (avatarUrl != null) {
|
||||
request.setAvatar(avatarUrl);
|
||||
}
|
||||
if (!TextUtils.isEmpty(birthday)) {
|
||||
request.setBirthday(birthday);
|
||||
}
|
||||
if (!TextUtils.isEmpty(gender)) {
|
||||
// 转换性别:男=1, 女=2, 保密=3
|
||||
int sex = 0;
|
||||
if ("男".equals(gender)) sex = 1;
|
||||
else if ("女".equals(gender)) sex = 2;
|
||||
else if ("保密".equals(gender)) sex = 3;
|
||||
request.setSex(sex);
|
||||
}
|
||||
if (!TextUtils.isEmpty(location)) {
|
||||
request.setAddres(location);
|
||||
}
|
||||
if (!TextUtils.isEmpty(bio) && !BIO_HINT_TEXT.equals(bio)) {
|
||||
request.setMark(bio);
|
||||
}
|
||||
|
||||
apiService.updateUserInfo(request).enqueue(new Callback<ApiResponse<Object>>() {
|
||||
@Override
|
||||
public void onResponse(Call<ApiResponse<Object>> call, Response<ApiResponse<Object>> response) {
|
||||
isUploading = false;
|
||||
binding.saveButton.setEnabled(true);
|
||||
|
||||
if (response.isSuccessful() && response.body() != null && response.body().isOk()) {
|
||||
Log.d(TAG, "用户资料更新成功");
|
||||
// 保存到本地
|
||||
saveToLocalPrefs(name, bio, birthday, gender, location, avatarUrl);
|
||||
Toast.makeText(EditProfileActivity.this, "保存成功", Toast.LENGTH_SHORT).show();
|
||||
setResult(RESULT_OK);
|
||||
finish();
|
||||
} else {
|
||||
String msg = response.body() != null ? response.body().getMessage() : "更新失败";
|
||||
Log.e(TAG, "用户资料更新失败: " + msg);
|
||||
// 即使服务器更新失败,也保存到本地
|
||||
saveToLocalPrefs(name, bio, birthday, gender, location, avatarUrl);
|
||||
Toast.makeText(EditProfileActivity.this, "已保存到本地,服务器同步失败: " + msg, Toast.LENGTH_SHORT).show();
|
||||
setResult(RESULT_OK);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
// 生日已经在日期选择器中实时保存,这里只需要确保同步
|
||||
// 如果输入框为空,则清除
|
||||
if (TextUtils.isEmpty(birthday)) {
|
||||
getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().remove(KEY_BIRTHDAY).apply();
|
||||
} else {
|
||||
// 确保使用输入框中的最新值
|
||||
getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().putString(KEY_BIRTHDAY, birthday).apply();
|
||||
@Override
|
||||
public void onFailure(Call<ApiResponse<Object>> call, Throwable t) {
|
||||
isUploading = false;
|
||||
binding.saveButton.setEnabled(true);
|
||||
Log.e(TAG, "用户资料更新网络错误: " + t.getMessage());
|
||||
// 网络失败也保存到本地
|
||||
saveToLocalPrefs(name, bio, birthday, gender, location, avatarUrl);
|
||||
Toast.makeText(EditProfileActivity.this, "已保存到本地,网络同步失败", Toast.LENGTH_SHORT).show();
|
||||
setResult(RESULT_OK);
|
||||
finish();
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(gender)) {
|
||||
getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().remove(KEY_GENDER).apply();
|
||||
} else {
|
||||
getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().putString(KEY_GENDER, gender).apply();
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(location)) {
|
||||
getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().remove(KEY_LOCATION).apply();
|
||||
} else {
|
||||
getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().putString(KEY_LOCATION, location).apply();
|
||||
}
|
||||
|
||||
// 设置结果,通知ProfileActivity需要刷新
|
||||
setResult(RESULT_OK);
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存到本地SharedPreferences
|
||||
*/
|
||||
private void saveToLocalPrefs(String name, String bio, String birthday, String gender, String location, String avatarUrl) {
|
||||
android.content.SharedPreferences.Editor editor = getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit();
|
||||
|
||||
editor.putString(KEY_NAME, name);
|
||||
|
||||
if (TextUtils.isEmpty(bio) || BIO_HINT_TEXT.equals(bio)) {
|
||||
editor.remove(KEY_BIO);
|
||||
} else {
|
||||
editor.putString(KEY_BIO, bio);
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(birthday)) {
|
||||
editor.remove(KEY_BIRTHDAY);
|
||||
} else {
|
||||
editor.putString(KEY_BIRTHDAY, birthday);
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(gender)) {
|
||||
editor.remove(KEY_GENDER);
|
||||
} else {
|
||||
editor.putString(KEY_GENDER, gender);
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(location)) {
|
||||
editor.remove(KEY_LOCATION);
|
||||
} else {
|
||||
editor.putString(KEY_LOCATION, location);
|
||||
}
|
||||
|
||||
if (avatarUrl != null) {
|
||||
editor.putString(KEY_AVATAR_URL, avatarUrl);
|
||||
}
|
||||
|
||||
// 如果选择了新头像,也保存本地URI
|
||||
if (selectedAvatarUri != null) {
|
||||
Uri persisted = persistAvatarToInternalStorage(selectedAvatarUri);
|
||||
if (persisted != null) {
|
||||
editor.putString(KEY_AVATAR_URI, persisted.toString());
|
||||
editor.remove(KEY_AVATAR_RES);
|
||||
}
|
||||
}
|
||||
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件名
|
||||
*/
|
||||
private String getFileName(Uri uri) {
|
||||
String result = null;
|
||||
if ("content".equals(uri.getScheme())) {
|
||||
try (Cursor cursor = getContentResolver().query(uri, null, null, null, null)) {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
int index = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
||||
if (index >= 0) {
|
||||
result = cursor.getString(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result == null) {
|
||||
result = uri.getPath();
|
||||
if (result != null) {
|
||||
int cut = result.lastIndexOf('/');
|
||||
if (cut != -1) {
|
||||
result = result.substring(cut + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 回调接口
|
||||
private interface OnAvatarUploadSuccess {
|
||||
void onSuccess(String avatarUrl);
|
||||
}
|
||||
|
||||
private interface OnAvatarUploadError {
|
||||
void onError(String error);
|
||||
}
|
||||
|
||||
private Uri persistAvatarToInternalStorage(Uri sourceUri) {
|
||||
// TODO: 接入后端接口 - 上传头像到服务器
|
||||
// 接口路径: POST /api/upload/image
|
||||
// 请求参数:
|
||||
// - file: 图片文件(multipart/form-data)
|
||||
// - model: 模块类型(如"user")
|
||||
// - pid: 分类ID(如7表示前台用户)
|
||||
// 返回数据格式: ApiResponse<{url: string}>
|
||||
// 上传成功后,保存返回的URL到本地,并更新界面显示
|
||||
// 注意:这里目前只是保存到本地,实际应该先上传到服务器,然后保存服务器返回的URL
|
||||
if (sourceUri == null) return null;
|
||||
InputStream in = null;
|
||||
OutputStream out = null;
|
||||
|
|
|
|||
|
|
@ -45,7 +45,9 @@ public class FansListActivity extends AppCompatActivity {
|
|||
|
||||
adapter = new FriendsAdapter(item -> {
|
||||
if (item == null) return;
|
||||
Toast.makeText(this, "打开粉丝:" + item.getName(), Toast.LENGTH_SHORT).show();
|
||||
// 跳转到用户主页,传递签名信息
|
||||
UserProfileReadOnlyActivity.start(this, item.getId(), item.getName(),
|
||||
"", item.getSubtitle(), item.getAvatarUrl());
|
||||
});
|
||||
|
||||
binding.fansRecyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||||
|
|
@ -80,13 +82,22 @@ public class FansListActivity extends AppCompatActivity {
|
|||
String id = String.valueOf(user.get("userId"));
|
||||
String name = (String) user.get("nickname");
|
||||
String phone = (String) user.get("phone");
|
||||
String signature = (String) user.get("signature");
|
||||
String avatar = (String) user.get("avatar");
|
||||
Boolean isOnline = (Boolean) user.get("isOnline");
|
||||
Boolean isMutualFollow = (Boolean) user.get("isMutualFollow");
|
||||
|
||||
String status = isMutualFollow != null && isMutualFollow ?
|
||||
"互相关注" : (isOnline != null && isOnline ? "在线" : "离线");
|
||||
items.add(new FriendItem(id, name != null ? name : phone, status,
|
||||
isOnline != null && isOnline));
|
||||
// 优先显示签名,没有签名则显示互关/在线状态
|
||||
String subtitle;
|
||||
if (signature != null && !signature.isEmpty()) {
|
||||
subtitle = signature;
|
||||
} else if (isMutualFollow != null && isMutualFollow) {
|
||||
subtitle = "互相关注";
|
||||
} else {
|
||||
subtitle = isOnline != null && isOnline ? "在线" : "离线";
|
||||
}
|
||||
items.add(new FriendItem(id, name != null ? name : phone, subtitle,
|
||||
isOnline != null && isOnline, avatar));
|
||||
}
|
||||
adapter.submitList(items);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -3,25 +3,32 @@ 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 androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.example.livestreaming.databinding.ActivityFindGameBinding;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
|
||||
import java.util.List;
|
||||
/**
|
||||
* 找人玩游戏 - 从后端API加载消息
|
||||
*/
|
||||
public class FindGameActivity extends BaseCategoryActivity {
|
||||
|
||||
public class FindGameActivity extends AppCompatActivity {
|
||||
|
||||
private static final String CATEGORY = "找人玩游戏";
|
||||
private static final int CATEGORY_ID = -1; // 通过名称自动查找
|
||||
private static final String CATEGORY_NAME = "找人玩游戏";
|
||||
|
||||
private ActivityFindGameBinding binding;
|
||||
private PostAdapter postAdapter;
|
||||
|
||||
public static void start(Context context) {
|
||||
Intent intent = new Intent(context, FindGameActivity.class);
|
||||
context.startActivity(intent);
|
||||
context.startActivity(new Intent(context, FindGameActivity.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getCategoryId() {
|
||||
return CATEGORY_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCategoryName() {
|
||||
return CATEGORY_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -31,27 +38,13 @@ public class FindGameActivity extends AppCompatActivity {
|
|||
setContentView(binding.getRoot());
|
||||
|
||||
setupUI();
|
||||
setupRecyclerView();
|
||||
loadPosts();
|
||||
// 初始化消息列表(从API加载)
|
||||
initMessageList(binding.recyclerPosts, binding.emptyView, null, binding.fabPublish);
|
||||
}
|
||||
|
||||
private void setupUI() {
|
||||
binding.backButton.setOnClickListener(v -> finish());
|
||||
|
||||
binding.fabPublish.setOnClickListener(v -> {
|
||||
PublishPostHelper.showPublishDialog(this, CATEGORY, new PublishPostHelper.PublishCallback() {
|
||||
@Override
|
||||
public void onSuccess(Post post) {
|
||||
postAdapter.addPost(post);
|
||||
binding.recyclerPosts.scrollToPosition(0);
|
||||
updateEmptyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String error) {}
|
||||
});
|
||||
});
|
||||
|
||||
BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation;
|
||||
bottomNavigation.setSelectedItemId(R.id.nav_friends);
|
||||
UnreadMessageManager.updateBadge(bottomNavigation);
|
||||
|
|
@ -81,28 +74,6 @@ public class FindGameActivity extends AppCompatActivity {
|
|||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void setupRecyclerView() {
|
||||
postAdapter = new PostAdapter();
|
||||
binding.recyclerPosts.setLayoutManager(new LinearLayoutManager(this));
|
||||
binding.recyclerPosts.setAdapter(postAdapter);
|
||||
}
|
||||
|
||||
private void loadPosts() {
|
||||
List<Post> posts = PostManager.getPostsByCategory(this, CATEGORY);
|
||||
postAdapter.setPosts(posts);
|
||||
updateEmptyView();
|
||||
}
|
||||
|
||||
private void updateEmptyView() {
|
||||
if (postAdapter.getItemCount() == 0) {
|
||||
binding.emptyView.setVisibility(View.VISIBLE);
|
||||
binding.recyclerPosts.setVisibility(View.GONE);
|
||||
} else {
|
||||
binding.emptyView.setVisibility(View.GONE);
|
||||
binding.recyclerPosts.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
|
|
@ -111,7 +82,6 @@ public class FindGameActivity extends AppCompatActivity {
|
|||
BottomNavigationView bottomNav = binding.bottomNavInclude.bottomNavigation;
|
||||
bottomNav.setSelectedItemId(R.id.nav_friends);
|
||||
UnreadMessageManager.updateBadge(bottomNav);
|
||||
loadPosts();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,21 +7,50 @@ import android.animation.ValueAnimator;
|
|||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.View;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
import android.view.animation.OvershootInterpolator;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.constraintlayout.widget.ConstraintSet;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
|
||||
import com.example.livestreaming.databinding.ActivityFishPondBinding;
|
||||
import com.example.livestreaming.net.ApiResponse;
|
||||
import com.example.livestreaming.net.ApiService;
|
||||
import com.example.livestreaming.net.CommunityResponse;
|
||||
import com.example.livestreaming.net.ApiClient;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
public class FishPondActivity extends AppCompatActivity {
|
||||
|
||||
private static final String TAG = "FishPondActivity";
|
||||
private static final int MAX_VISIBLE_CATEGORIES = 3; // 默认显示的板块数量
|
||||
|
||||
private ActivityFishPondBinding binding;
|
||||
private ApiService apiService;
|
||||
private CategoryAdapter categoryAdapter;
|
||||
|
||||
// 保存所有板块数据,用于弹窗展示
|
||||
private List<CategoryAdapter.CategoryItem> allCategories = new ArrayList<>();
|
||||
|
||||
// 板块收起/展开状态
|
||||
private boolean isCategoryExpanded = true;
|
||||
private View categoryContainer;
|
||||
private View toggleCategoryBtn;
|
||||
private android.widget.TextView toggleCategoryText;
|
||||
private android.widget.ImageView toggleCategoryIcon;
|
||||
|
||||
private static class OrbitUserData {
|
||||
final String id;
|
||||
|
|
@ -29,6 +58,7 @@ public class FishPondActivity extends AppCompatActivity {
|
|||
final String location;
|
||||
final String bio;
|
||||
final int avatarRes;
|
||||
final String avatarUrl;
|
||||
|
||||
OrbitUserData(String id, String name, String location, String bio, int avatarRes) {
|
||||
this.id = id;
|
||||
|
|
@ -36,6 +66,16 @@ public class FishPondActivity extends AppCompatActivity {
|
|||
this.location = location;
|
||||
this.bio = bio;
|
||||
this.avatarRes = avatarRes;
|
||||
this.avatarUrl = null;
|
||||
}
|
||||
|
||||
OrbitUserData(String id, String name, String location, String bio, String avatarUrl) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.location = location;
|
||||
this.bio = bio;
|
||||
this.avatarRes = R.drawable.wish_tree_checker_backup;
|
||||
this.avatarUrl = avatarUrl;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -70,10 +110,14 @@ public class FishPondActivity extends AppCompatActivity {
|
|||
binding = ActivityFishPondBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
apiService = ApiClient.getService(this);
|
||||
|
||||
setupOrbitUserInteractions();
|
||||
setupCenterRefresh();
|
||||
setupQuickActions();
|
||||
setupCategoryToggle();
|
||||
loadCurrentUserInfo();
|
||||
loadMatchableUserCount(); // 加载可匹配用户总数
|
||||
|
||||
binding.orbitContainer.post(this::updateOrbitLayout);
|
||||
refreshUsers();
|
||||
|
|
@ -130,48 +174,306 @@ public class FishPondActivity extends AppCompatActivity {
|
|||
private void setupQuickActions() {
|
||||
if (binding == null) return;
|
||||
|
||||
// 语音匹配按钮
|
||||
binding.actionLeft.setOnClickListener(v -> {
|
||||
VoiceMatchActivity.start(this);
|
||||
// 设置RecyclerView和适配器
|
||||
setupCategoryRecyclerView();
|
||||
|
||||
// 从API加载板块
|
||||
loadCategoriesFromApi();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置板块RecyclerView
|
||||
*/
|
||||
private void setupCategoryRecyclerView() {
|
||||
categoryAdapter = new CategoryAdapter(this);
|
||||
categoryAdapter.setOnCategoryClickListener(category -> {
|
||||
onCategoryClick(category);
|
||||
});
|
||||
|
||||
// 心动信号按钮
|
||||
binding.actionRight.setOnClickListener(v -> {
|
||||
HeartbeatSignalActivity.start(this);
|
||||
});
|
||||
// 使用GridLayoutManager,4列(3个板块 + 1个展开按钮)
|
||||
GridLayoutManager layoutManager = new GridLayoutManager(this, 4);
|
||||
binding.categoryRecyclerView.setLayoutManager(layoutManager);
|
||||
binding.categoryRecyclerView.setAdapter(categoryAdapter);
|
||||
binding.categoryRecyclerView.setNestedScrollingEnabled(false);
|
||||
}
|
||||
|
||||
// 获取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;
|
||||
/**
|
||||
* 从API加载板块列表
|
||||
*/
|
||||
private void loadCategoriesFromApi() {
|
||||
if (binding == null) return;
|
||||
|
||||
// 显示加载中
|
||||
binding.categoryLoading.setVisibility(View.VISIBLE);
|
||||
binding.categoryRecyclerView.setVisibility(View.GONE);
|
||||
|
||||
apiService.getCommunityCategories().enqueue(new Callback<ApiResponse<List<CommunityResponse.Category>>>() {
|
||||
@Override
|
||||
public void onResponse(Call<ApiResponse<List<CommunityResponse.Category>>> call,
|
||||
Response<ApiResponse<List<CommunityResponse.Category>>> response) {
|
||||
if (binding == null) return;
|
||||
binding.categoryLoading.setVisibility(View.GONE);
|
||||
|
||||
if (response.isSuccessful() && response.body() != null
|
||||
&& response.body().isOk() && response.body().getData() != null) {
|
||||
List<CommunityResponse.Category> categories = response.body().getData();
|
||||
Log.d(TAG, "加载到 " + categories.size() + " 个板块");
|
||||
|
||||
// 转换为CategoryItem列表
|
||||
allCategories.clear();
|
||||
for (CommunityResponse.Category cat : categories) {
|
||||
// 只显示状态正常的板块
|
||||
if (cat.status == 1 || cat.status == 0) {
|
||||
allCategories.add(CategoryAdapter.CategoryItem.fromCategory(cat));
|
||||
Log.d(TAG, "板块: " + cat.getName() + ", 图标: " + cat.getIcon() + ", 跳转: " + cat.getJumpPage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!allCategories.isEmpty()) {
|
||||
binding.categoryRecyclerView.setVisibility(View.VISIBLE);
|
||||
// 只显示前3个板块
|
||||
showLimitedCategories();
|
||||
} else {
|
||||
// 无数据,使用默认板块
|
||||
Log.w(TAG, "API返回空板块列表,使用默认板块");
|
||||
loadDefaultCategories();
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "API响应异常,使用默认板块");
|
||||
loadDefaultCategories();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<ApiResponse<List<CommunityResponse.Category>>> call, Throwable t) {
|
||||
if (binding == null) return;
|
||||
binding.categoryLoading.setVisibility(View.GONE);
|
||||
Log.e(TAG, "加载板块失败: " + t.getMessage());
|
||||
loadDefaultCategories();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示有限数量的板块(前3个 + 展开更多按钮)
|
||||
*/
|
||||
private void showLimitedCategories() {
|
||||
List<CategoryAdapter.CategoryItem> visibleItems = new ArrayList<>();
|
||||
|
||||
// 添加前3个板块
|
||||
for (int i = 0; i < Math.min(MAX_VISIBLE_CATEGORIES, allCategories.size()); i++) {
|
||||
visibleItems.add(allCategories.get(i));
|
||||
}
|
||||
|
||||
// 如果有更多板块,添加"展开更多"按钮
|
||||
if (allCategories.size() > MAX_VISIBLE_CATEGORIES) {
|
||||
CategoryAdapter.CategoryItem moreItem = new CategoryAdapter.CategoryItem(
|
||||
-999, "展开更多", "el-icon-menu", "ShowAllCategories"
|
||||
);
|
||||
visibleItems.add(moreItem);
|
||||
}
|
||||
|
||||
categoryAdapter.setCategories(visibleItems);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载默认板块(API失败时的备用)
|
||||
*/
|
||||
private void loadDefaultCategories() {
|
||||
if (binding == null) return;
|
||||
|
||||
allCategories.clear();
|
||||
allCategories.add(new CategoryAdapter.CategoryItem(1, "在线处对象", "el-icon-user", "OnlineDatingActivity"));
|
||||
allCategories.add(new CategoryAdapter.CategoryItem(2, "找人玩游戏", "el-icon-coordinate", "FindGameActivity"));
|
||||
allCategories.add(new CategoryAdapter.CategoryItem(3, "一起KTV", "el-icon-headset", "KTVTogetherActivity"));
|
||||
allCategories.add(new CategoryAdapter.CategoryItem(4, "你画我猜", "el-icon-edit", "DrawGuessActivity"));
|
||||
allCategories.add(new CategoryAdapter.CategoryItem(5, "和平精英", "el-icon-aim", "PeaceEliteActivity"));
|
||||
allCategories.add(new CategoryAdapter.CategoryItem(6, "桌子游戏", "el-icon-grid", "TableGamesActivity"));
|
||||
|
||||
binding.categoryRecyclerView.setVisibility(View.VISIBLE);
|
||||
showLimitedCategories();
|
||||
}
|
||||
|
||||
/**
|
||||
* 板块点击处理 - 根据jumpPage跳转到对应Activity
|
||||
*/
|
||||
private void onCategoryClick(CategoryAdapter.CategoryItem category) {
|
||||
String jumpPage = category.getJumpPage();
|
||||
String name = category.getName();
|
||||
int categoryId = category.getId();
|
||||
|
||||
Log.d(TAG, "点击板块: " + name + ", jumpPage: " + jumpPage + ", id: " + categoryId);
|
||||
|
||||
// 检查是否是"展开更多"按钮
|
||||
if ("ShowAllCategories".equals(jumpPage) || categoryId == -999) {
|
||||
showAllCategoriesDialog();
|
||||
return;
|
||||
}
|
||||
|
||||
// 跳转到板块详情页,传递板块名称
|
||||
navigateToCategoryPage(categoryId, name, jumpPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示所有板块的弹窗
|
||||
*/
|
||||
private void showAllCategoriesDialog() {
|
||||
if (allCategories.isEmpty()) return;
|
||||
|
||||
android.app.AlertDialog.Builder builder = new android.app.AlertDialog.Builder(this);
|
||||
|
||||
// 创建弹窗布局
|
||||
View dialogView = getLayoutInflater().inflate(R.layout.dialog_all_categories, null);
|
||||
androidx.recyclerview.widget.RecyclerView dialogRecyclerView = dialogView.findViewById(R.id.dialogCategoryRecyclerView);
|
||||
View closeBtn = dialogView.findViewById(R.id.dialogCloseBtn);
|
||||
|
||||
// 设置RecyclerView
|
||||
CategoryAdapter dialogAdapter = new CategoryAdapter(this);
|
||||
dialogRecyclerView.setLayoutManager(new GridLayoutManager(this, 3));
|
||||
dialogRecyclerView.setAdapter(dialogAdapter);
|
||||
|
||||
// 设置所有板块数据(不包含"展开更多")
|
||||
dialogAdapter.setCategories(allCategories);
|
||||
|
||||
builder.setView(dialogView);
|
||||
android.app.AlertDialog dialog = builder.create();
|
||||
|
||||
// 设置透明背景
|
||||
if (dialog.getWindow() != null) {
|
||||
dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
|
||||
}
|
||||
|
||||
// 板块点击事件
|
||||
dialogAdapter.setOnCategoryClickListener(category -> {
|
||||
dialog.dismiss();
|
||||
navigateToCategoryPage(category.getId(), category.getName(), category.getJumpPage());
|
||||
});
|
||||
|
||||
// 关闭按钮
|
||||
if (closeBtn != null) {
|
||||
closeBtn.setOnClickListener(v -> dialog.dismiss());
|
||||
}
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到板块详情页
|
||||
*/
|
||||
private void navigateToCategoryPage(int categoryId, String categoryName, String jumpPage) {
|
||||
// 根据jumpPage或板块名称跳转
|
||||
if (jumpPage != null && !jumpPage.isEmpty()) {
|
||||
navigateByJumpPage(jumpPage, categoryId, categoryName);
|
||||
} else {
|
||||
// 没有jumpPage,根据名称匹配
|
||||
navigateByName(categoryName, categoryId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据jumpPage跳转 - 统一使用DynamicCommunityActivity,传递板块名称
|
||||
*/
|
||||
private void navigateByJumpPage(String jumpPage, int categoryId, String categoryName) {
|
||||
// 所有板块统一使用DynamicCommunityActivity,确保标题显示正确的板块名称
|
||||
navigateToGenericCategory(categoryId, categoryName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据板块名称跳转 - 统一使用DynamicCommunityActivity
|
||||
*/
|
||||
private void navigateByName(String name, int categoryId) {
|
||||
if (name == null) {
|
||||
navigateToGenericCategory(categoryId, "未知板块");
|
||||
return;
|
||||
}
|
||||
// 统一使用通用跳转,确保标题显示正确的板块名称
|
||||
navigateToGenericCategory(categoryId, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用板块跳转(使用DynamicCommunityActivity)
|
||||
*/
|
||||
private void navigateToGenericCategory(int categoryId, String categoryName) {
|
||||
try {
|
||||
Intent intent = new Intent(this, DynamicCommunityActivity.class);
|
||||
intent.putExtra("category_id", categoryId);
|
||||
intent.putExtra("category_name", categoryName);
|
||||
startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "跳转失败: " + e.getMessage());
|
||||
Toast.makeText(this, "板块 \"" + categoryName + "\" 暂未开放", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置板块隐藏/展开功能
|
||||
*/
|
||||
private void setupCategoryToggle() {
|
||||
// 使用getResources().getIdentifier动态获取ID
|
||||
int containerId = getResources().getIdentifier("categoryContainer", "id", getPackageName());
|
||||
int btnId = getResources().getIdentifier("toggleCategoryBtn", "id", getPackageName());
|
||||
int textId = getResources().getIdentifier("toggleCategoryText", "id", getPackageName());
|
||||
int iconId = getResources().getIdentifier("toggleCategoryIcon", "id", getPackageName());
|
||||
|
||||
categoryContainer = findViewById(containerId);
|
||||
toggleCategoryBtn = findViewById(btnId);
|
||||
toggleCategoryText = findViewById(textId);
|
||||
toggleCategoryIcon = findViewById(iconId);
|
||||
|
||||
// 如果布局中没有这些视图,使用RecyclerView作为categoryContainer
|
||||
if (categoryContainer == null && binding != null && binding.categoryRecyclerView != null) {
|
||||
categoryContainer = binding.categoryRecyclerView;
|
||||
}
|
||||
|
||||
if (toggleCategoryBtn != null) {
|
||||
toggleCategoryBtn.setOnClickListener(v -> {
|
||||
v.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
|
||||
toggleCategoryVisibility();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换板块显示/隐藏
|
||||
*/
|
||||
private void toggleCategoryVisibility() {
|
||||
if (categoryContainer == null) return;
|
||||
|
||||
isCategoryExpanded = !isCategoryExpanded;
|
||||
|
||||
if (isCategoryExpanded) {
|
||||
// 展开动画
|
||||
categoryContainer.setVisibility(View.VISIBLE);
|
||||
categoryContainer.setAlpha(0f);
|
||||
categoryContainer.animate()
|
||||
.alpha(1f)
|
||||
.setDuration(200)
|
||||
.start();
|
||||
if (toggleCategoryText != null) {
|
||||
toggleCategoryText.setText("收起");
|
||||
}
|
||||
if (toggleCategoryIcon != null) {
|
||||
toggleCategoryIcon.animate()
|
||||
.rotation(0f)
|
||||
.setDuration(200)
|
||||
.start();
|
||||
}
|
||||
} else {
|
||||
// 收起动画
|
||||
categoryContainer.animate()
|
||||
.alpha(0f)
|
||||
.setDuration(200)
|
||||
.withEndAction(() -> categoryContainer.setVisibility(View.GONE))
|
||||
.start();
|
||||
if (toggleCategoryText != null) {
|
||||
toggleCategoryText.setText("展开");
|
||||
}
|
||||
if (toggleCategoryIcon != null) {
|
||||
toggleCategoryIcon.animate()
|
||||
.rotation(180f)
|
||||
.setDuration(200)
|
||||
.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -181,14 +483,17 @@ public class FishPondActivity extends AppCompatActivity {
|
|||
int size = binding.orbitContainer.getWidth();
|
||||
if (size <= 0) return;
|
||||
|
||||
int avatarSize = binding.orbitUserTopAvatar.getWidth();
|
||||
if (avatarSize <= 0 && binding.orbitUserTopAvatar.getLayoutParams() != null) {
|
||||
avatarSize = binding.orbitUserTopAvatar.getLayoutParams().width;
|
||||
}
|
||||
if (avatarSize <= 0) return;
|
||||
|
||||
int outerRadius = size / 2;
|
||||
orbitRadiusPx = outerRadius - (avatarSize / 2);
|
||||
float density = getResources().getDisplayMetrics().density;
|
||||
|
||||
// 圆环有 36dp 的 margin,所以圆环半径 = (容器宽度 - 72dp) / 2
|
||||
int ringMargin = (int) (36 * density);
|
||||
int ringRadius = (size / 2) - ringMargin;
|
||||
|
||||
// 轨道半径 = 圆环半径
|
||||
// 这样 LinearLayout 的中心会落在圆环上
|
||||
// 由于头像在 LinearLayout 顶部,头像中心会稍微在圆环内侧
|
||||
// 但视觉效果是可接受的
|
||||
orbitRadiusPx = ringRadius;
|
||||
|
||||
applyOrbitAngles();
|
||||
}
|
||||
|
|
@ -322,66 +627,265 @@ public class FishPondActivity extends AppCompatActivity {
|
|||
binding.rightInfo.setText(genderText + " | " + locationText + " | 在线");
|
||||
}
|
||||
|
||||
private void refreshUsers() {
|
||||
// TODO: 接入后端接口 - 获取附近用户(缘池功能)
|
||||
// 接口路径: GET /api/users/nearby
|
||||
// 请求参数:
|
||||
// - latitude: 当前用户纬度(必填)
|
||||
// - longitude: 当前用户经度(必填)
|
||||
// - radius (可选): 搜索半径(单位:米,默认5000)
|
||||
// - limit (可选): 返回数量,默认6
|
||||
// 返回数据格式: ApiResponse<List<User>>
|
||||
// User对象应包含: id, name, avatarUrl, location, bio, distance(距离,单位:米), isLive等字段
|
||||
// 返回距离最近的N个用户,用于显示在轨道上
|
||||
/**
|
||||
* 加载可匹配用户总数
|
||||
*/
|
||||
private void loadMatchableUserCount() {
|
||||
if (binding == null) return;
|
||||
|
||||
int[] avatars = new int[] {
|
||||
R.drawable.wish_tree_checker_backup,
|
||||
R.drawable.wish_tree_prev_no_bg,
|
||||
R.drawable.wish_tree_trim_backup,
|
||||
R.drawable.wish_tree_black,
|
||||
R.drawable.wish_tree
|
||||
};
|
||||
apiService.getMatchableUserCount().enqueue(new Callback<ApiResponse<Integer>>() {
|
||||
@Override
|
||||
public void onResponse(Call<ApiResponse<Integer>> call,
|
||||
Response<ApiResponse<Integer>> response) {
|
||||
if (binding == null) return;
|
||||
|
||||
if (response.isSuccessful() && response.body() != null
|
||||
&& response.body().isOk() && response.body().getData() != null) {
|
||||
int count = response.body().getData();
|
||||
binding.leftCount.setText(count + " 人");
|
||||
Log.d(TAG, "可匹配用户总数: " + count);
|
||||
} else {
|
||||
Log.w(TAG, "获取用户总数失败,使用默认值");
|
||||
binding.leftCount.setText("-- 人");
|
||||
}
|
||||
}
|
||||
|
||||
String[] names = new String[] { "小树", "阿宁", "Lina", "暖暖", "小七", "小北", "Mika", "安安", "小鹿" };
|
||||
String[] cities = new String[] { "杭州", "南宁", "深圳", "成都", "武汉", "西安", "上海", "北京", "重庆" };
|
||||
|
||||
userTop = bindUser("u_top", binding.orbitUserTopAvatar, binding.orbitUserTopName, binding.orbitUserTopLocation, avatars, names, cities);
|
||||
userRight = bindUser("u_right", binding.orbitUserRightAvatar, binding.orbitUserRightName, binding.orbitUserRightLocation, avatars, names, cities);
|
||||
userBottomRight = bindUser("u_bottom_right", binding.orbitUserBottomRightAvatar, binding.orbitUserBottomRightName, binding.orbitUserBottomRightLocation, avatars, names, cities);
|
||||
userBottomCenter = bindUser("u_bottom_center", binding.orbitUserBottomCenterAvatar, binding.orbitUserBottomCenterName, binding.orbitUserBottomCenterLocation, avatars, names, cities);
|
||||
userBottomLeft = bindUser("u_bottom_left", binding.orbitUserBottomLeftAvatar, binding.orbitUserBottomLeftName, binding.orbitUserBottomLeftLocation, avatars, names, cities);
|
||||
userLeft = bindUser("u_left", binding.orbitUserLeftAvatar, binding.orbitUserLeftName, binding.orbitUserLeftLocation, avatars, names, cities);
|
||||
@Override
|
||||
public void onFailure(Call<ApiResponse<Integer>> call, Throwable t) {
|
||||
if (binding == null) return;
|
||||
Log.e(TAG, "获取用户总数网络错误: " + t.getMessage());
|
||||
binding.leftCount.setText("-- 人");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private OrbitUserData bindUser(String id,
|
||||
android.widget.ImageView avatarView,
|
||||
android.widget.TextView nameView,
|
||||
android.widget.TextView locationView,
|
||||
int[] avatars,
|
||||
String[] names,
|
||||
String[] cities) {
|
||||
if (avatarView == null || nameView == null || locationView == null) {
|
||||
return new OrbitUserData(id, "", "", "", R.drawable.wish_tree_checker_backup);
|
||||
private void refreshUsers() {
|
||||
if (binding == null) return;
|
||||
|
||||
// 刷新用户总数
|
||||
loadMatchableUserCount();
|
||||
|
||||
// 尝试从API获取附近用户
|
||||
loadNearbyUsersFromApi();
|
||||
}
|
||||
|
||||
private void loadNearbyUsersFromApi() {
|
||||
// 获取当前位置(这里使用默认位置,实际应该获取GPS位置)
|
||||
SharedPreferences prefs = getSharedPreferences("profile_prefs", MODE_PRIVATE);
|
||||
double latitude = prefs.getFloat("latitude", 22.82f); // 默认南宁纬度
|
||||
double longitude = prefs.getFloat("longitude", 108.32f); // 默认南宁经度
|
||||
int radius = 5000; // 5公里范围
|
||||
int limit = 6; // 获取6个用户
|
||||
|
||||
apiService.getNearbyUsers(latitude, longitude, radius, limit)
|
||||
.enqueue(new Callback<ApiResponse<CommunityResponse.NearbyUserList>>() {
|
||||
@Override
|
||||
public void onResponse(Call<ApiResponse<CommunityResponse.NearbyUserList>> call,
|
||||
Response<ApiResponse<CommunityResponse.NearbyUserList>> response) {
|
||||
if (response.isSuccessful() && response.body() != null
|
||||
&& response.body().isOk() && response.body().getData() != null) {
|
||||
List<CommunityResponse.NearbyUser> users = response.body().getData().getUsers();
|
||||
if (users != null && !users.isEmpty()) {
|
||||
bindUsersFromApi(users);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// API失败或无数据,使用模拟数据
|
||||
Log.w(TAG, "API返回空数据,使用模拟数据");
|
||||
loadMockUsers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<ApiResponse<CommunityResponse.NearbyUserList>> call, Throwable t) {
|
||||
Log.e(TAG, "获取附近用户失败: " + t.getMessage());
|
||||
// 网络失败,使用模拟数据
|
||||
loadMockUsers();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void bindUsersFromApi(List<CommunityResponse.NearbyUser> users) {
|
||||
if (binding == null || users == null) return;
|
||||
|
||||
// 绑定最多6个用户到轨道位置
|
||||
if (users.size() > 0) {
|
||||
CommunityResponse.NearbyUser u = users.get(0);
|
||||
userTop = bindUserFromApi("u_top", binding.orbitUserTopAvatar, binding.orbitUserTopName, binding.orbitUserTopLocation, u);
|
||||
}
|
||||
if (users.size() > 1) {
|
||||
CommunityResponse.NearbyUser u = users.get(1);
|
||||
userRight = bindUserFromApi("u_right", binding.orbitUserRightAvatar, binding.orbitUserRightName, binding.orbitUserRightLocation, u);
|
||||
}
|
||||
if (users.size() > 2) {
|
||||
CommunityResponse.NearbyUser u = users.get(2);
|
||||
userBottomRight = bindUserFromApi("u_bottom_right", binding.orbitUserBottomRightAvatar, binding.orbitUserBottomRightName, binding.orbitUserBottomRightLocation, u);
|
||||
}
|
||||
if (users.size() > 3) {
|
||||
CommunityResponse.NearbyUser u = users.get(3);
|
||||
userBottomCenter = bindUserFromApi("u_bottom_center", binding.orbitUserBottomCenterAvatar, binding.orbitUserBottomCenterName, binding.orbitUserBottomCenterLocation, u);
|
||||
}
|
||||
if (users.size() > 4) {
|
||||
CommunityResponse.NearbyUser u = users.get(4);
|
||||
userBottomLeft = bindUserFromApi("u_bottom_left", binding.orbitUserBottomLeftAvatar, binding.orbitUserBottomLeftName, binding.orbitUserBottomLeftLocation, u);
|
||||
}
|
||||
if (users.size() > 5) {
|
||||
CommunityResponse.NearbyUser u = users.get(5);
|
||||
userLeft = bindUserFromApi("u_left", binding.orbitUserLeftAvatar, binding.orbitUserLeftName, binding.orbitUserLeftLocation, u);
|
||||
}
|
||||
|
||||
int a = avatars[(int) (Math.random() * avatars.length)];
|
||||
String n = names[(int) (Math.random() * names.length)];
|
||||
String c = cities[(int) (Math.random() * cities.length)];
|
||||
// 如果用户数量不足6个,剩余位置显示默认头像
|
||||
if (users.size() < 6) {
|
||||
fillRemainingWithDefaultAvatar(users.size());
|
||||
}
|
||||
}
|
||||
|
||||
String bio = "真诚交友 · " + c + " · 在线";
|
||||
// 默认头像资源(三种颜色的圆形头像)
|
||||
private static final int[] DEFAULT_AVATARS = new int[] {
|
||||
R.drawable.default_avatar_purple,
|
||||
R.drawable.default_avatar_pink,
|
||||
R.drawable.default_avatar_blue
|
||||
};
|
||||
|
||||
Glide.with(avatarView)
|
||||
.load(a)
|
||||
.circleCrop()
|
||||
.into(avatarView);
|
||||
nameView.setText(n);
|
||||
locationView.setText(c);
|
||||
private OrbitUserData bindUserFromApi(String id,
|
||||
android.widget.ImageView avatarView,
|
||||
android.widget.TextView nameView,
|
||||
android.widget.TextView locationView,
|
||||
CommunityResponse.NearbyUser user) {
|
||||
if (avatarView == null || nameView == null || locationView == null || user == null) {
|
||||
return new OrbitUserData(id, "", "", "", DEFAULT_AVATARS[0]);
|
||||
}
|
||||
|
||||
String name = user.getNickname() != null ? user.getNickname() : "用户";
|
||||
String location = user.getLocation() != null ? user.getLocation() : "附近";
|
||||
String avatarUrl = user.getAvatar();
|
||||
String bio = "真诚交友 · " + location + " · " + (user.isOnline() ? "在线" : "离线");
|
||||
|
||||
// 加载头像:有URL则加载网络图片,否则随机使用默认头像
|
||||
if (avatarUrl != null && !avatarUrl.isEmpty()) {
|
||||
// 拼接完整的头像URL
|
||||
String fullAvatarUrl = avatarUrl;
|
||||
if (!avatarUrl.startsWith("http://") && !avatarUrl.startsWith("https://")) {
|
||||
// 获取API基础地址并拼接
|
||||
String baseUrl = ApiClient.getCurrentBaseUrl(this);
|
||||
if (baseUrl.endsWith("/")) {
|
||||
baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
|
||||
}
|
||||
fullAvatarUrl = baseUrl + "/" + avatarUrl;
|
||||
}
|
||||
Log.d(TAG, "加载头像: " + fullAvatarUrl);
|
||||
|
||||
// 随机选择一个默认头像作为占位图和错误图
|
||||
int defaultAvatar = DEFAULT_AVATARS[(int) (Math.random() * DEFAULT_AVATARS.length)];
|
||||
Glide.with(avatarView)
|
||||
.load(fullAvatarUrl)
|
||||
.placeholder(defaultAvatar)
|
||||
.error(defaultAvatar)
|
||||
.circleCrop()
|
||||
.into(avatarView);
|
||||
} else {
|
||||
// 没有头像URL,随机使用默认头像
|
||||
int defaultAvatar = DEFAULT_AVATARS[(int) (Math.random() * DEFAULT_AVATARS.length)];
|
||||
Glide.with(avatarView)
|
||||
.load(defaultAvatar)
|
||||
.circleCrop()
|
||||
.into(avatarView);
|
||||
}
|
||||
|
||||
nameView.setText(name);
|
||||
locationView.setText(location);
|
||||
|
||||
avatarView.setAlpha(0.6f);
|
||||
avatarView.animate().alpha(1f).setDuration(250L).start();
|
||||
|
||||
return new OrbitUserData(id, n, c, bio, a);
|
||||
return new OrbitUserData(String.valueOf(user.getUserId()), name, location, bio, avatarUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户数量不足时,剩余位置显示默认头像
|
||||
*/
|
||||
private void fillRemainingWithDefaultAvatar(int startIndex) {
|
||||
if (binding == null) return;
|
||||
|
||||
if (startIndex <= 0) {
|
||||
userTop = setDefaultUserDisplay("u_top", binding.orbitUserTopAvatar, binding.orbitUserTopName, binding.orbitUserTopLocation);
|
||||
}
|
||||
if (startIndex <= 1) {
|
||||
userRight = setDefaultUserDisplay("u_right", binding.orbitUserRightAvatar, binding.orbitUserRightName, binding.orbitUserRightLocation);
|
||||
}
|
||||
if (startIndex <= 2) {
|
||||
userBottomRight = setDefaultUserDisplay("u_bottom_right", binding.orbitUserBottomRightAvatar, binding.orbitUserBottomRightName, binding.orbitUserBottomRightLocation);
|
||||
}
|
||||
if (startIndex <= 3) {
|
||||
userBottomCenter = setDefaultUserDisplay("u_bottom_center", binding.orbitUserBottomCenterAvatar, binding.orbitUserBottomCenterName, binding.orbitUserBottomCenterLocation);
|
||||
}
|
||||
if (startIndex <= 4) {
|
||||
userBottomLeft = setDefaultUserDisplay("u_bottom_left", binding.orbitUserBottomLeftAvatar, binding.orbitUserBottomLeftName, binding.orbitUserBottomLeftLocation);
|
||||
}
|
||||
if (startIndex <= 5) {
|
||||
userLeft = setDefaultUserDisplay("u_left", binding.orbitUserLeftAvatar, binding.orbitUserLeftName, binding.orbitUserLeftLocation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置默认用户显示(用户数量不足时)
|
||||
*/
|
||||
private OrbitUserData setDefaultUserDisplay(String id,
|
||||
android.widget.ImageView avatarView,
|
||||
android.widget.TextView nameView,
|
||||
android.widget.TextView locationView) {
|
||||
if (avatarView == null || nameView == null || locationView == null) {
|
||||
return new OrbitUserData(id, "", "", "", DEFAULT_AVATARS[0]);
|
||||
}
|
||||
|
||||
int defaultAvatar = DEFAULT_AVATARS[(int) (Math.random() * DEFAULT_AVATARS.length)];
|
||||
Glide.with(avatarView)
|
||||
.load(defaultAvatar)
|
||||
.circleCrop()
|
||||
.into(avatarView);
|
||||
nameView.setText("暂无");
|
||||
locationView.setText("");
|
||||
|
||||
avatarView.setAlpha(0.6f);
|
||||
avatarView.animate().alpha(1f).setDuration(250L).start();
|
||||
|
||||
return new OrbitUserData(id, "暂无", "", "", defaultAvatar);
|
||||
}
|
||||
|
||||
private void loadMockUsers() {
|
||||
if (binding == null) return;
|
||||
|
||||
// API失败时,显示提示而不是虚假数据
|
||||
Log.w(TAG, "无法从服务器获取用户数据");
|
||||
Toast.makeText(this, "无法获取用户数据,请检查网络连接", Toast.LENGTH_SHORT).show();
|
||||
|
||||
// 清空显示
|
||||
clearUserDisplay(binding.orbitUserTopAvatar, binding.orbitUserTopName, binding.orbitUserTopLocation);
|
||||
clearUserDisplay(binding.orbitUserRightAvatar, binding.orbitUserRightName, binding.orbitUserRightLocation);
|
||||
clearUserDisplay(binding.orbitUserBottomRightAvatar, binding.orbitUserBottomRightName, binding.orbitUserBottomRightLocation);
|
||||
clearUserDisplay(binding.orbitUserBottomCenterAvatar, binding.orbitUserBottomCenterName, binding.orbitUserBottomCenterLocation);
|
||||
clearUserDisplay(binding.orbitUserBottomLeftAvatar, binding.orbitUserBottomLeftName, binding.orbitUserBottomLeftLocation);
|
||||
clearUserDisplay(binding.orbitUserLeftAvatar, binding.orbitUserLeftName, binding.orbitUserLeftLocation);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空用户显示(网络失败时使用)
|
||||
*/
|
||||
private void clearUserDisplay(android.widget.ImageView avatarView,
|
||||
android.widget.TextView nameView,
|
||||
android.widget.TextView locationView) {
|
||||
if (avatarView != null) {
|
||||
int defaultAvatar = DEFAULT_AVATARS[(int) (Math.random() * DEFAULT_AVATARS.length)];
|
||||
Glide.with(avatarView)
|
||||
.load(defaultAvatar)
|
||||
.circleCrop()
|
||||
.into(avatarView);
|
||||
}
|
||||
if (nameView != null) {
|
||||
nameView.setText("加载中...");
|
||||
}
|
||||
if (locationView != null) {
|
||||
locationView.setText("");
|
||||
}
|
||||
}
|
||||
|
||||
private void setupOrbitUserInteractions() {
|
||||
|
|
@ -398,14 +902,28 @@ public class FishPondActivity extends AppCompatActivity {
|
|||
private void onOrbitUserClick(View view, OrbitUserData user) {
|
||||
if (view == null || user == null) return;
|
||||
playAvatarClickEffect(view);
|
||||
view.postDelayed(() -> UserProfileReadOnlyActivity.start(
|
||||
this,
|
||||
user.id,
|
||||
user.name,
|
||||
user.location,
|
||||
user.bio,
|
||||
user.avatarRes
|
||||
), 120L);
|
||||
view.postDelayed(() -> {
|
||||
// 如果有avatarUrl,使用URL;否则使用资源ID
|
||||
if (user.avatarUrl != null && !user.avatarUrl.isEmpty()) {
|
||||
UserProfileReadOnlyActivity.start(
|
||||
this,
|
||||
user.id,
|
||||
user.name,
|
||||
user.location,
|
||||
user.bio,
|
||||
user.avatarUrl
|
||||
);
|
||||
} else {
|
||||
UserProfileReadOnlyActivity.start(
|
||||
this,
|
||||
user.id,
|
||||
user.name,
|
||||
user.location,
|
||||
user.bio,
|
||||
user.avatarRes
|
||||
);
|
||||
}
|
||||
}, 120L);
|
||||
}
|
||||
|
||||
private void playAvatarClickEffect(View view) {
|
||||
|
|
|
|||
|
|
@ -45,7 +45,9 @@ public class FollowingListActivity extends AppCompatActivity {
|
|||
|
||||
adapter = new FriendsAdapter(item -> {
|
||||
if (item == null) return;
|
||||
Toast.makeText(this, "打开关注:" + item.getName(), Toast.LENGTH_SHORT).show();
|
||||
// 跳转到用户主页,传递签名信息
|
||||
UserProfileReadOnlyActivity.start(this, item.getId(), item.getName(),
|
||||
"", item.getSubtitle(), item.getAvatarUrl());
|
||||
});
|
||||
|
||||
binding.followingRecyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||||
|
|
@ -80,11 +82,16 @@ public class FollowingListActivity extends AppCompatActivity {
|
|||
String id = String.valueOf(user.get("userId"));
|
||||
String name = (String) user.get("nickname");
|
||||
String phone = (String) user.get("phone");
|
||||
String signature = (String) user.get("signature");
|
||||
String avatar = (String) user.get("avatar");
|
||||
Boolean isOnline = (Boolean) user.get("isOnline");
|
||||
|
||||
String status = isOnline != null && isOnline ? "在线" : "离线";
|
||||
items.add(new FriendItem(id, name != null ? name : phone, status,
|
||||
isOnline != null && isOnline));
|
||||
// 优先显示签名,没有签名则显示在线状态
|
||||
String subtitle = signature != null && !signature.isEmpty()
|
||||
? signature
|
||||
: (isOnline != null && isOnline ? "在线" : "离线");
|
||||
items.add(new FriendItem(id, name != null ? name : phone, subtitle,
|
||||
isOnline != null && isOnline, avatar));
|
||||
}
|
||||
adapter.submitList(items);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,18 +1,46 @@
|
|||
package com.example.livestreaming;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.animation.AccelerateInterpolator;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.example.livestreaming.databinding.ActivityHeartbeatSignalBinding;
|
||||
import com.example.livestreaming.net.ApiClient;
|
||||
import com.example.livestreaming.net.ApiResponse;
|
||||
import com.example.livestreaming.net.ApiService;
|
||||
import com.example.livestreaming.net.CommunityResponse;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
/**
|
||||
* 心动信号页面 - 探探风格卡片滑动匹配
|
||||
*/
|
||||
public class HeartbeatSignalActivity extends AppCompatActivity {
|
||||
|
||||
private ActivityHeartbeatSignalBinding binding;
|
||||
private ApiService apiService;
|
||||
|
||||
private List<CommunityResponse.MatchUser> users = new ArrayList<>();
|
||||
private int currentIndex = 0;
|
||||
private View currentCard;
|
||||
private boolean isAnimating = false;
|
||||
|
||||
public static void start(Context context) {
|
||||
Intent intent = new Intent(context, HeartbeatSignalActivity.class);
|
||||
|
|
@ -25,18 +53,47 @@ public class HeartbeatSignalActivity extends AppCompatActivity {
|
|||
binding = ActivityHeartbeatSignalBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
apiService = ApiClient.getService(this);
|
||||
|
||||
setupUI();
|
||||
loadRecommendUsers();
|
||||
}
|
||||
|
||||
private void setupUI() {
|
||||
binding.backButton.setOnClickListener(v -> finish());
|
||||
|
||||
// 跳过按钮
|
||||
binding.btnSkip.setOnClickListener(v -> {
|
||||
if (!isAnimating && currentCard != null) {
|
||||
swipeCard(false);
|
||||
}
|
||||
});
|
||||
|
||||
// 喜欢按钮
|
||||
binding.btnLike.setOnClickListener(v -> {
|
||||
if (!isAnimating && currentCard != null) {
|
||||
swipeCard(true);
|
||||
}
|
||||
});
|
||||
|
||||
// 超级喜欢按钮
|
||||
binding.btnSuperLike.setOnClickListener(v -> {
|
||||
if (!isAnimating && currentCard != null) {
|
||||
superLike();
|
||||
}
|
||||
});
|
||||
|
||||
// 刷新按钮
|
||||
binding.btnRefresh.setOnClickListener(v -> loadRecommendUsers());
|
||||
|
||||
setupBottomNav();
|
||||
}
|
||||
|
||||
private void setupBottomNav() {
|
||||
BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation;
|
||||
bottomNavigation.setSelectedItemId(R.id.nav_friends);
|
||||
|
||||
// 更新未读消息徽章
|
||||
UnreadMessageManager.updateBadge(bottomNavigation);
|
||||
|
||||
|
||||
bottomNavigation.setOnItemSelectedListener(item -> {
|
||||
int id = item.getItemId();
|
||||
if (id == R.id.nav_home) {
|
||||
|
|
@ -44,6 +101,11 @@ public class HeartbeatSignalActivity extends AppCompatActivity {
|
|||
finish();
|
||||
return true;
|
||||
}
|
||||
if (id == R.id.nav_friends) {
|
||||
startActivity(new Intent(this, FishPondActivity.class));
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
if (id == R.id.nav_wish_tree) {
|
||||
WishTreeActivity.start(this);
|
||||
finish();
|
||||
|
|
@ -62,14 +124,349 @@ public class HeartbeatSignalActivity extends AppCompatActivity {
|
|||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void loadRecommendUsers() {
|
||||
showLoading(true);
|
||||
|
||||
apiService.getRecommendUsers(10).enqueue(new Callback<ApiResponse<List<CommunityResponse.MatchUser>>>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse<List<CommunityResponse.MatchUser>>> call,
|
||||
@NonNull Response<ApiResponse<List<CommunityResponse.MatchUser>>> response) {
|
||||
showLoading(false);
|
||||
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getData() != null) {
|
||||
users = response.body().getData();
|
||||
currentIndex = 0;
|
||||
showCards();
|
||||
} else {
|
||||
// 使用模拟数据
|
||||
loadMockUsers();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse<List<CommunityResponse.MatchUser>>> call, @NonNull Throwable t) {
|
||||
showLoading(false);
|
||||
// 使用模拟数据
|
||||
loadMockUsers();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载模拟数据
|
||||
*/
|
||||
private void loadMockUsers() {
|
||||
users = new ArrayList<>();
|
||||
|
||||
String[] names = {"小美", "阿宁", "Lina", "暖暖", "小七", "小北", "Mika", "安安", "小鹿", "糖糖"};
|
||||
String[] locations = {"南宁 · 3km", "杭州 · 5km", "深圳 · 2km", "成都 · 8km", "武汉 · 4km"};
|
||||
String[] bios = {
|
||||
"喜欢旅行和美食,希望遇到有趣的灵魂~",
|
||||
"爱笑的女孩运气不会太差",
|
||||
"生活不止眼前的苟且,还有诗和远方",
|
||||
"简单生活,快乐每一天",
|
||||
"寻找那个对的人"
|
||||
};
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
CommunityResponse.MatchUser user = new CommunityResponse.MatchUser();
|
||||
user.id = i + 1;
|
||||
user.nickname = names[i % names.length];
|
||||
user.age = 20 + (int) (Math.random() * 8);
|
||||
user.location = locations[i % locations.length];
|
||||
user.bio = bios[i % bios.length];
|
||||
user.gender = "女";
|
||||
users.add(user);
|
||||
}
|
||||
|
||||
currentIndex = 0;
|
||||
showCards();
|
||||
}
|
||||
|
||||
private void showCards() {
|
||||
binding.cardContainer.removeAllViews();
|
||||
|
||||
if (users.isEmpty()) {
|
||||
showEmpty(true);
|
||||
return;
|
||||
}
|
||||
|
||||
showEmpty(false);
|
||||
|
||||
// 显示最多3张卡片(层叠效果)
|
||||
int cardsToShow = Math.min(3, users.size() - currentIndex);
|
||||
for (int i = cardsToShow - 1; i >= 0; i--) {
|
||||
int index = currentIndex + i;
|
||||
if (index < users.size()) {
|
||||
View card = createCard(users.get(index), i);
|
||||
binding.cardContainer.addView(card);
|
||||
if (i == 0) {
|
||||
currentCard = card;
|
||||
setupCardTouchListener(card);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private View createCard(CommunityResponse.MatchUser user, int stackIndex) {
|
||||
View card = getLayoutInflater().inflate(R.layout.item_swipe_card, binding.cardContainer, false);
|
||||
|
||||
// 设置用户信息
|
||||
android.widget.TextView userName = card.findViewById(R.id.userName);
|
||||
android.widget.TextView userAge = card.findViewById(R.id.userAge);
|
||||
android.widget.TextView userLocation = card.findViewById(R.id.userLocation);
|
||||
android.widget.TextView userBio = card.findViewById(R.id.userBio);
|
||||
android.widget.ImageView userPhoto = card.findViewById(R.id.userPhoto);
|
||||
|
||||
userName.setText(user.nickname);
|
||||
userAge.setText(user.age + "岁");
|
||||
userLocation.setText(user.location);
|
||||
userBio.setText(user.bio);
|
||||
|
||||
// 加载头像
|
||||
String photoUrl = null;
|
||||
if (user.photos != null && !user.photos.isEmpty()) {
|
||||
photoUrl = user.photos.get(0);
|
||||
} else {
|
||||
photoUrl = user.avatar;
|
||||
}
|
||||
|
||||
if (photoUrl != null && !photoUrl.isEmpty()) {
|
||||
com.bumptech.glide.Glide.with(this)
|
||||
.load(photoUrl)
|
||||
.placeholder(R.drawable.wish_tree_checker_backup)
|
||||
.error(R.drawable.wish_tree_checker_backup)
|
||||
.centerCrop()
|
||||
.into(userPhoto);
|
||||
}
|
||||
|
||||
// 层叠效果
|
||||
float scale = 1f - (stackIndex * 0.05f);
|
||||
float translationY = stackIndex * 20f;
|
||||
card.setScaleX(scale);
|
||||
card.setScaleY(scale);
|
||||
card.setTranslationY(translationY);
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
private void setupCardTouchListener(View card) {
|
||||
card.setOnTouchListener(new View.OnTouchListener() {
|
||||
private float startX, startY;
|
||||
private float dX, dY;
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View v, android.view.MotionEvent event) {
|
||||
if (isAnimating) return false;
|
||||
|
||||
switch (event.getAction()) {
|
||||
case android.view.MotionEvent.ACTION_DOWN:
|
||||
startX = event.getRawX();
|
||||
startY = event.getRawY();
|
||||
dX = v.getX() - startX;
|
||||
dY = v.getY() - startY;
|
||||
return true;
|
||||
|
||||
case android.view.MotionEvent.ACTION_MOVE:
|
||||
float newX = event.getRawX() + dX;
|
||||
float newY = event.getRawY() + dY;
|
||||
v.setX(newX);
|
||||
v.setY(newY);
|
||||
|
||||
// 旋转效果
|
||||
float rotation = (newX - startX) / 20f;
|
||||
v.setRotation(rotation);
|
||||
|
||||
// 显示喜欢/跳过标签
|
||||
float deltaX = event.getRawX() - startX;
|
||||
View likeStamp = v.findViewById(R.id.likeStamp);
|
||||
View nopeStamp = v.findViewById(R.id.nopeStamp);
|
||||
|
||||
if (deltaX > 50) {
|
||||
float alpha = Math.min(1f, (deltaX - 50) / 150f);
|
||||
likeStamp.setAlpha(alpha);
|
||||
nopeStamp.setAlpha(0f);
|
||||
} else if (deltaX < -50) {
|
||||
float alpha = Math.min(1f, (-deltaX - 50) / 150f);
|
||||
nopeStamp.setAlpha(alpha);
|
||||
likeStamp.setAlpha(0f);
|
||||
} else {
|
||||
likeStamp.setAlpha(0f);
|
||||
nopeStamp.setAlpha(0f);
|
||||
}
|
||||
return true;
|
||||
|
||||
case android.view.MotionEvent.ACTION_UP:
|
||||
float deltaXFinal = event.getRawX() - startX;
|
||||
if (deltaXFinal > 150) {
|
||||
// 右滑 - 喜欢
|
||||
animateCardOut(v, true);
|
||||
} else if (deltaXFinal < -150) {
|
||||
// 左滑 - 跳过
|
||||
animateCardOut(v, false);
|
||||
} else {
|
||||
// 回弹
|
||||
v.animate()
|
||||
.x(0)
|
||||
.y(0)
|
||||
.rotation(0)
|
||||
.setDuration(200)
|
||||
.start();
|
||||
v.findViewById(R.id.likeStamp).setAlpha(0f);
|
||||
v.findViewById(R.id.nopeStamp).setAlpha(0f);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void swipeCard(boolean isLike) {
|
||||
if (currentCard == null || isAnimating) return;
|
||||
animateCardOut(currentCard, isLike);
|
||||
}
|
||||
|
||||
private void animateCardOut(View card, boolean isLike) {
|
||||
isAnimating = true;
|
||||
|
||||
float targetX = isLike ? 1000f : -1000f;
|
||||
float targetRotation = isLike ? 30f : -30f;
|
||||
|
||||
card.animate()
|
||||
.x(targetX)
|
||||
.rotation(targetRotation)
|
||||
.alpha(0f)
|
||||
.setDuration(300)
|
||||
.setInterpolator(new AccelerateInterpolator())
|
||||
.setListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
isAnimating = false;
|
||||
|
||||
// 发送喜欢/跳过请求
|
||||
if (currentIndex < users.size()) {
|
||||
CommunityResponse.MatchUser user = users.get(currentIndex);
|
||||
if (isLike) {
|
||||
sendLike(user.id);
|
||||
} else {
|
||||
sendSkip(user.id);
|
||||
}
|
||||
}
|
||||
|
||||
currentIndex++;
|
||||
showCards();
|
||||
}
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
private void superLike() {
|
||||
if (currentCard == null || isAnimating) return;
|
||||
|
||||
isAnimating = true;
|
||||
|
||||
// 超级喜欢动画 - 向上飞出
|
||||
currentCard.animate()
|
||||
.y(-1000f)
|
||||
.scaleX(1.2f)
|
||||
.scaleY(1.2f)
|
||||
.alpha(0f)
|
||||
.setDuration(400)
|
||||
.setListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
isAnimating = false;
|
||||
|
||||
if (currentIndex < users.size()) {
|
||||
CommunityResponse.MatchUser user = users.get(currentIndex);
|
||||
sendSuperLike(user.id);
|
||||
}
|
||||
|
||||
currentIndex++;
|
||||
showCards();
|
||||
}
|
||||
})
|
||||
.start();
|
||||
|
||||
Toast.makeText(this, "超级喜欢!", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
private void sendLike(int userId) {
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("userId", userId);
|
||||
|
||||
apiService.likeUser(body).enqueue(new Callback<ApiResponse<CommunityResponse.MatchResult>>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse<CommunityResponse.MatchResult>> call,
|
||||
@NonNull Response<ApiResponse<CommunityResponse.MatchResult>> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getData() != null) {
|
||||
CommunityResponse.MatchResult result = response.body().getData();
|
||||
if (result.isMatch) {
|
||||
showMatchDialog(result.matchedUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse<CommunityResponse.MatchResult>> call, @NonNull Throwable t) {
|
||||
// 忽略错误
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void sendSkip(int userId) {
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("userId", userId);
|
||||
|
||||
apiService.skipUser(body).enqueue(new Callback<ApiResponse<String>>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse<String>> call, @NonNull Response<ApiResponse<String>> response) {
|
||||
// 忽略结果
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse<String>> call, @NonNull Throwable t) {
|
||||
// 忽略错误
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void sendSuperLike(int userId) {
|
||||
// 超级喜欢也是喜欢,只是权重更高
|
||||
sendLike(userId);
|
||||
}
|
||||
|
||||
private void showMatchDialog(CommunityResponse.MatchUser user) {
|
||||
new androidx.appcompat.app.AlertDialog.Builder(this)
|
||||
.setTitle("💕 配对成功!")
|
||||
.setMessage("你和 " + (user != null ? user.nickname : "TA") + " 互相喜欢!\n快去打个招呼吧~")
|
||||
.setPositiveButton("发消息", (dialog, which) -> {
|
||||
// TODO: 跳转到聊天页面
|
||||
Toast.makeText(this, "即将跳转到聊天", Toast.LENGTH_SHORT).show();
|
||||
})
|
||||
.setNegativeButton("继续浏览", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void showLoading(boolean show) {
|
||||
binding.loadingView.setVisibility(show ? View.VISIBLE : View.GONE);
|
||||
binding.cardContainer.setVisibility(show ? View.GONE : View.VISIBLE);
|
||||
binding.emptyView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private void showEmpty(boolean show) {
|
||||
binding.emptyView.setVisibility(show ? View.VISIBLE : View.GONE);
|
||||
binding.cardContainer.setVisibility(show ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
if (binding != null) {
|
||||
BottomNavigationView bottomNav = binding.bottomNavInclude.bottomNavigation;
|
||||
bottomNav.setSelectedItemId(R.id.nav_friends);
|
||||
// 更新未读消息徽章
|
||||
UnreadMessageManager.updateBadge(bottomNav);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,25 +3,32 @@ 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 androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.example.livestreaming.databinding.ActivityKtvTogetherBinding;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
|
||||
import java.util.List;
|
||||
/**
|
||||
* 一起KTV - 从后端API加载消息
|
||||
*/
|
||||
public class KTVTogetherActivity extends BaseCategoryActivity {
|
||||
|
||||
public class KTVTogetherActivity extends AppCompatActivity {
|
||||
|
||||
private static final String CATEGORY = "一起KTV";
|
||||
private static final int CATEGORY_ID = -1; // 通过名称自动查找
|
||||
private static final String CATEGORY_NAME = "一起KTV";
|
||||
|
||||
private ActivityKtvTogetherBinding binding;
|
||||
private PostAdapter postAdapter;
|
||||
|
||||
public static void start(Context context) {
|
||||
Intent intent = new Intent(context, KTVTogetherActivity.class);
|
||||
context.startActivity(intent);
|
||||
context.startActivity(new Intent(context, KTVTogetherActivity.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getCategoryId() {
|
||||
return CATEGORY_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCategoryName() {
|
||||
return CATEGORY_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -31,27 +38,12 @@ public class KTVTogetherActivity extends AppCompatActivity {
|
|||
setContentView(binding.getRoot());
|
||||
|
||||
setupUI();
|
||||
setupRecyclerView();
|
||||
loadPosts();
|
||||
initMessageList(binding.recyclerPosts, binding.emptyView, null, binding.fabPublish);
|
||||
}
|
||||
|
||||
private void setupUI() {
|
||||
binding.backButton.setOnClickListener(v -> finish());
|
||||
|
||||
binding.fabPublish.setOnClickListener(v -> {
|
||||
PublishPostHelper.showPublishDialog(this, CATEGORY, new PublishPostHelper.PublishCallback() {
|
||||
@Override
|
||||
public void onSuccess(Post post) {
|
||||
postAdapter.addPost(post);
|
||||
binding.recyclerPosts.scrollToPosition(0);
|
||||
updateEmptyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String error) {}
|
||||
});
|
||||
});
|
||||
|
||||
BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation;
|
||||
bottomNavigation.setSelectedItemId(R.id.nav_friends);
|
||||
UnreadMessageManager.updateBadge(bottomNavigation);
|
||||
|
|
@ -81,28 +73,6 @@ public class KTVTogetherActivity extends AppCompatActivity {
|
|||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void setupRecyclerView() {
|
||||
postAdapter = new PostAdapter();
|
||||
binding.recyclerPosts.setLayoutManager(new LinearLayoutManager(this));
|
||||
binding.recyclerPosts.setAdapter(postAdapter);
|
||||
}
|
||||
|
||||
private void loadPosts() {
|
||||
List<Post> posts = PostManager.getPostsByCategory(this, CATEGORY);
|
||||
postAdapter.setPosts(posts);
|
||||
updateEmptyView();
|
||||
}
|
||||
|
||||
private void updateEmptyView() {
|
||||
if (postAdapter.getItemCount() == 0) {
|
||||
binding.emptyView.setVisibility(View.VISIBLE);
|
||||
binding.recyclerPosts.setVisibility(View.GONE);
|
||||
} else {
|
||||
binding.emptyView.setVisibility(View.GONE);
|
||||
binding.recyclerPosts.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
|
|
@ -111,7 +81,6 @@ public class KTVTogetherActivity extends AppCompatActivity {
|
|||
BottomNavigationView bottomNav = binding.bottomNavInclude.bottomNavigation;
|
||||
bottomNav.setSelectedItemId(R.id.nav_friends);
|
||||
UnreadMessageManager.updateBadge(bottomNav);
|
||||
loadPosts();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -216,8 +216,10 @@ public class MyFriendsActivity extends AppCompatActivity {
|
|||
String id = String.valueOf(item.opt("id"));
|
||||
String name = item.optString("name", "未知用户");
|
||||
String avatarUrl = item.optString("avatarUrl", "");
|
||||
String signature = item.optString("signature", "");
|
||||
boolean isOnline = item.optInt("isOnline", 0) == 1;
|
||||
String subtitle = isOnline ? "在线" : "离线";
|
||||
// 优先显示签名,没有签名则显示在线状态
|
||||
String subtitle = !signature.isEmpty() ? signature : (isOnline ? "在线" : "离线");
|
||||
allFriends.add(new FriendItem(id, name, subtitle, isOnline, avatarUrl));
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "解析好友项失败", e);
|
||||
|
|
|
|||
|
|
@ -17,38 +17,49 @@ public class NearbyUsersAdapter extends ListAdapter<NearbyUser, NearbyUsersAdapt
|
|||
public interface OnUserClickListener {
|
||||
void onUserClick(NearbyUser user);
|
||||
}
|
||||
|
||||
public interface OnAddFriendClickListener {
|
||||
void onAddFriendClick(NearbyUser user);
|
||||
}
|
||||
|
||||
private final OnUserClickListener onUserClickListener;
|
||||
private OnAddFriendClickListener onAddFriendClickListener;
|
||||
|
||||
public NearbyUsersAdapter(OnUserClickListener onUserClickListener) {
|
||||
super(DIFF);
|
||||
this.onUserClickListener = onUserClickListener;
|
||||
}
|
||||
|
||||
public void setOnAddFriendClickListener(OnAddFriendClickListener listener) {
|
||||
this.onAddFriendClickListener = listener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
ItemNearbyUserBinding binding = ItemNearbyUserBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new VH(binding, onUserClickListener);
|
||||
return new VH(binding, onUserClickListener, onAddFriendClickListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull VH holder, int position) {
|
||||
holder.bind(getItem(position));
|
||||
holder.bind(getItem(position), onAddFriendClickListener);
|
||||
}
|
||||
|
||||
static class VH extends RecyclerView.ViewHolder {
|
||||
|
||||
private final ItemNearbyUserBinding binding;
|
||||
private final OnUserClickListener onUserClickListener;
|
||||
private OnAddFriendClickListener onAddFriendClickListener;
|
||||
|
||||
VH(ItemNearbyUserBinding binding, OnUserClickListener onUserClickListener) {
|
||||
VH(ItemNearbyUserBinding binding, OnUserClickListener onUserClickListener, OnAddFriendClickListener onAddFriendClickListener) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
this.onUserClickListener = onUserClickListener;
|
||||
this.onAddFriendClickListener = onAddFriendClickListener;
|
||||
}
|
||||
|
||||
void bind(NearbyUser user) {
|
||||
void bind(NearbyUser user, OnAddFriendClickListener addFriendListener) {
|
||||
if (user == null) return;
|
||||
|
||||
// 设置用户名
|
||||
|
|
@ -63,11 +74,6 @@ public class NearbyUsersAdapter extends ListAdapter<NearbyUser, NearbyUsersAdapt
|
|||
binding.distanceText.setText(distanceText);
|
||||
|
||||
// 使用Glide加载头像
|
||||
// TODO: 接入后端接口 - 从后端获取附近用户头像URL
|
||||
// 接口路径: GET /api/user/profile/{userId}
|
||||
// 请求参数: userId (路径参数,从NearbyUser对象中获取)
|
||||
// 返回数据格式: ApiResponse<{avatarUrl: string}>
|
||||
// 目前使用默认占位图
|
||||
Glide.with(binding.avatarImage.getContext())
|
||||
.load((String) null) // 暂时为null,等待后端接口
|
||||
.circleCrop()
|
||||
|
|
@ -82,14 +88,16 @@ public class NearbyUsersAdapter extends ListAdapter<NearbyUser, NearbyUsersAdapt
|
|||
binding.liveBadge.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// 添加按钮点击事件
|
||||
// 添加按钮点击事件 - 发送好友请求
|
||||
binding.addButton.setOnClickListener(v -> {
|
||||
if (onUserClickListener != null) {
|
||||
if (addFriendListener != null) {
|
||||
addFriendListener.onAddFriendClick(user);
|
||||
} else if (onUserClickListener != null) {
|
||||
onUserClickListener.onUserClick(user);
|
||||
}
|
||||
});
|
||||
|
||||
// 整个item点击也可以触发添加
|
||||
// 整个item点击跳转到用户主页
|
||||
binding.getRoot().setOnClickListener(v -> {
|
||||
if (onUserClickListener != null) {
|
||||
onUserClickListener.onUserClick(user);
|
||||
|
|
|
|||
|
|
@ -3,25 +3,32 @@ 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 androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.example.livestreaming.databinding.ActivityOnlineDatingBinding;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
|
||||
import java.util.List;
|
||||
/**
|
||||
* 在线处对象 - 从后端API加载消息
|
||||
*/
|
||||
public class OnlineDatingActivity extends BaseCategoryActivity {
|
||||
|
||||
public class OnlineDatingActivity extends AppCompatActivity {
|
||||
|
||||
private static final String CATEGORY = "在线处对象";
|
||||
private static final int CATEGORY_ID = -1; // 通过名称自动查找
|
||||
private static final String CATEGORY_NAME = "在线处对象";
|
||||
|
||||
private ActivityOnlineDatingBinding binding;
|
||||
private PostAdapter postAdapter;
|
||||
|
||||
public static void start(Context context) {
|
||||
Intent intent = new Intent(context, OnlineDatingActivity.class);
|
||||
context.startActivity(intent);
|
||||
context.startActivity(new Intent(context, OnlineDatingActivity.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getCategoryId() {
|
||||
return CATEGORY_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCategoryName() {
|
||||
return CATEGORY_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -31,35 +38,14 @@ public class OnlineDatingActivity extends AppCompatActivity {
|
|||
setContentView(binding.getRoot());
|
||||
|
||||
setupUI();
|
||||
setupRecyclerView();
|
||||
loadPosts();
|
||||
initMessageList(binding.recyclerPosts, binding.emptyView, null, binding.fabPublish);
|
||||
}
|
||||
|
||||
private void setupUI() {
|
||||
binding.backButton.setOnClickListener(v -> finish());
|
||||
|
||||
// 发布动态按钮
|
||||
binding.fabPublish.setOnClickListener(v -> {
|
||||
PublishPostHelper.showPublishDialog(this, CATEGORY, new PublishPostHelper.PublishCallback() {
|
||||
@Override
|
||||
public void onSuccess(Post post) {
|
||||
// 发布成功,添加到列表顶部
|
||||
postAdapter.addPost(post);
|
||||
binding.recyclerPosts.scrollToPosition(0);
|
||||
updateEmptyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String error) {
|
||||
// 发布失败
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation;
|
||||
bottomNavigation.setSelectedItemId(R.id.nav_friends);
|
||||
|
||||
// 更新未读消息徽章
|
||||
UnreadMessageManager.updateBadge(bottomNavigation);
|
||||
|
||||
bottomNavigation.setOnItemSelectedListener(item -> {
|
||||
|
|
@ -87,29 +73,6 @@ public class OnlineDatingActivity extends AppCompatActivity {
|
|||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void setupRecyclerView() {
|
||||
postAdapter = new PostAdapter();
|
||||
binding.recyclerPosts.setLayoutManager(new LinearLayoutManager(this));
|
||||
binding.recyclerPosts.setAdapter(postAdapter);
|
||||
}
|
||||
|
||||
private void loadPosts() {
|
||||
// 加载该分类的动态
|
||||
List<Post> posts = PostManager.getPostsByCategory(this, CATEGORY);
|
||||
postAdapter.setPosts(posts);
|
||||
updateEmptyView();
|
||||
}
|
||||
|
||||
private void updateEmptyView() {
|
||||
if (postAdapter.getItemCount() == 0) {
|
||||
binding.emptyView.setVisibility(View.VISIBLE);
|
||||
binding.recyclerPosts.setVisibility(View.GONE);
|
||||
} else {
|
||||
binding.emptyView.setVisibility(View.GONE);
|
||||
binding.recyclerPosts.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
|
|
@ -117,10 +80,7 @@ public class OnlineDatingActivity extends AppCompatActivity {
|
|||
if (binding != null) {
|
||||
BottomNavigationView bottomNav = binding.bottomNavInclude.bottomNavigation;
|
||||
bottomNav.setSelectedItemId(R.id.nav_friends);
|
||||
// 更新未读消息徽章
|
||||
UnreadMessageManager.updateBadge(bottomNav);
|
||||
// 刷新动态列表
|
||||
loadPosts();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,25 +3,32 @@ 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 androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.example.livestreaming.databinding.ActivityPeaceEliteBinding;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
|
||||
import java.util.List;
|
||||
/**
|
||||
* 和平精英 - 从后端API加载消息
|
||||
*/
|
||||
public class PeaceEliteActivity extends BaseCategoryActivity {
|
||||
|
||||
public class PeaceEliteActivity extends AppCompatActivity {
|
||||
|
||||
private static final String CATEGORY = "和平精英";
|
||||
private static final int CATEGORY_ID = -1; // 通过名称自动查找
|
||||
private static final String CATEGORY_NAME = "和平精英";
|
||||
|
||||
private ActivityPeaceEliteBinding binding;
|
||||
private PostAdapter postAdapter;
|
||||
|
||||
public static void start(Context context) {
|
||||
Intent intent = new Intent(context, PeaceEliteActivity.class);
|
||||
context.startActivity(intent);
|
||||
context.startActivity(new Intent(context, PeaceEliteActivity.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getCategoryId() {
|
||||
return CATEGORY_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCategoryName() {
|
||||
return CATEGORY_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -31,27 +38,12 @@ public class PeaceEliteActivity extends AppCompatActivity {
|
|||
setContentView(binding.getRoot());
|
||||
|
||||
setupUI();
|
||||
setupRecyclerView();
|
||||
loadPosts();
|
||||
initMessageList(binding.recyclerPosts, binding.emptyView, null, binding.fabPublish);
|
||||
}
|
||||
|
||||
private void setupUI() {
|
||||
binding.backButton.setOnClickListener(v -> finish());
|
||||
|
||||
binding.fabPublish.setOnClickListener(v -> {
|
||||
PublishPostHelper.showPublishDialog(this, CATEGORY, new PublishPostHelper.PublishCallback() {
|
||||
@Override
|
||||
public void onSuccess(Post post) {
|
||||
postAdapter.addPost(post);
|
||||
binding.recyclerPosts.scrollToPosition(0);
|
||||
updateEmptyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String error) {}
|
||||
});
|
||||
});
|
||||
|
||||
BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation;
|
||||
bottomNavigation.setSelectedItemId(R.id.nav_friends);
|
||||
UnreadMessageManager.updateBadge(bottomNavigation);
|
||||
|
|
@ -81,28 +73,6 @@ public class PeaceEliteActivity extends AppCompatActivity {
|
|||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void setupRecyclerView() {
|
||||
postAdapter = new PostAdapter();
|
||||
binding.recyclerPosts.setLayoutManager(new LinearLayoutManager(this));
|
||||
binding.recyclerPosts.setAdapter(postAdapter);
|
||||
}
|
||||
|
||||
private void loadPosts() {
|
||||
List<Post> posts = PostManager.getPostsByCategory(this, CATEGORY);
|
||||
postAdapter.setPosts(posts);
|
||||
updateEmptyView();
|
||||
}
|
||||
|
||||
private void updateEmptyView() {
|
||||
if (postAdapter.getItemCount() == 0) {
|
||||
binding.emptyView.setVisibility(View.VISIBLE);
|
||||
binding.recyclerPosts.setVisibility(View.GONE);
|
||||
} else {
|
||||
binding.emptyView.setVisibility(View.GONE);
|
||||
binding.recyclerPosts.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
|
|
@ -111,7 +81,6 @@ public class PeaceEliteActivity extends AppCompatActivity {
|
|||
BottomNavigationView bottomNav = binding.bottomNavInclude.bottomNavigation;
|
||||
bottomNav.setSelectedItemId(R.id.nav_friends);
|
||||
UnreadMessageManager.updateBadge(bottomNav);
|
||||
loadPosts();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,42 +1,51 @@
|
|||
package com.example.livestreaming;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class Post implements Serializable {
|
||||
private String id;
|
||||
private String userId;
|
||||
private String userName;
|
||||
private String userAvatar;
|
||||
private long id;
|
||||
private String odlId; // 兼容旧的字符串ID
|
||||
private String odlUserId;
|
||||
private String authorName;
|
||||
private String authorAvatar;
|
||||
private String content;
|
||||
private String imageUrl;
|
||||
private List<String> images; // 多图片支持
|
||||
private String category;
|
||||
private long timestamp;
|
||||
private String createTime;
|
||||
private int likeCount;
|
||||
private int commentCount;
|
||||
private boolean isLiked;
|
||||
|
||||
public Post() {}
|
||||
|
||||
public Post(String id, String userId, String userName, String content, String category) {
|
||||
this.id = id;
|
||||
this.userId = userId;
|
||||
this.userName = userName;
|
||||
public Post(String odlId, String odlUserId, String authorName, String content, String category) {
|
||||
this.odlId = odlId;
|
||||
this.odlUserId = odlUserId;
|
||||
this.authorName = authorName;
|
||||
this.content = content;
|
||||
this.category = category;
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public String getId() { return id; }
|
||||
public void setId(String id) { this.id = id; }
|
||||
public long getId() { return id; }
|
||||
public void setId(long id) { this.id = id; }
|
||||
|
||||
public String getUserId() { return userId; }
|
||||
public void setUserId(String userId) { this.userId = userId; }
|
||||
public String getOdlId() { return odlId; }
|
||||
public void setOdlId(String odlId) { this.odlId = odlId; }
|
||||
|
||||
public String getUserName() { return userName; }
|
||||
public void setUserName(String userName) { this.userName = userName; }
|
||||
public String getOdlUserId() { return odlUserId; }
|
||||
public void setOdlUserId(String odlUserId) { this.odlUserId = odlUserId; }
|
||||
|
||||
public String getUserAvatar() { return userAvatar; }
|
||||
public void setUserAvatar(String userAvatar) { this.userAvatar = userAvatar; }
|
||||
public String getAuthorName() { return authorName; }
|
||||
public void setAuthorName(String authorName) { this.authorName = authorName; }
|
||||
|
||||
public String getAuthorAvatar() { return authorAvatar; }
|
||||
public void setAuthorAvatar(String authorAvatar) { this.authorAvatar = authorAvatar; }
|
||||
|
||||
public String getContent() { return content; }
|
||||
public void setContent(String content) { this.content = content; }
|
||||
|
|
@ -44,12 +53,50 @@ public class Post implements Serializable {
|
|||
public String getImageUrl() { return imageUrl; }
|
||||
public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; }
|
||||
|
||||
/**
|
||||
* 获取图片列表
|
||||
*/
|
||||
public List<String> getImages() {
|
||||
if (images != null && !images.isEmpty()) {
|
||||
return images;
|
||||
}
|
||||
// 兼容旧的单图片字段
|
||||
if (imageUrl != null && !imageUrl.isEmpty()) {
|
||||
return Arrays.asList(imageUrl);
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置图片列表(支持逗号分隔的字符串或List)
|
||||
*/
|
||||
public void setImages(String imagesStr) {
|
||||
if (imagesStr != null && !imagesStr.isEmpty()) {
|
||||
// 解析逗号分隔的图片URL
|
||||
String[] arr = imagesStr.split(",");
|
||||
this.images = new ArrayList<>();
|
||||
for (String url : arr) {
|
||||
String trimmed = url.trim();
|
||||
if (!trimmed.isEmpty()) {
|
||||
this.images.add(trimmed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setImagesList(List<String> images) {
|
||||
this.images = images;
|
||||
}
|
||||
|
||||
public String getCategory() { return category; }
|
||||
public void setCategory(String category) { this.category = category; }
|
||||
|
||||
public long getTimestamp() { return timestamp; }
|
||||
public void setTimestamp(long timestamp) { this.timestamp = timestamp; }
|
||||
|
||||
public String getCreateTime() { return createTime; }
|
||||
public void setCreateTime(String createTime) { this.createTime = createTime; }
|
||||
|
||||
public int getLikeCount() { return likeCount; }
|
||||
public void setLikeCount(int likeCount) { this.likeCount = likeCount; }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,18 @@
|
|||
package com.example.livestreaming;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
|
|
@ -17,13 +21,35 @@ import java.util.Locale;
|
|||
|
||||
public class PostAdapter extends RecyclerView.Adapter<PostAdapter.PostViewHolder> {
|
||||
|
||||
private final Context context;
|
||||
private List<Post> posts = new ArrayList<>();
|
||||
private OnItemClickListener listener;
|
||||
|
||||
public interface OnItemClickListener {
|
||||
void onItemClick(Post post);
|
||||
}
|
||||
|
||||
public PostAdapter(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public void setOnItemClickListener(OnItemClickListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void setPosts(List<Post> posts) {
|
||||
this.posts = posts != null ? posts : new ArrayList<>();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void addPosts(List<Post> newPosts) {
|
||||
if (newPosts != null && !newPosts.isEmpty()) {
|
||||
int start = posts.size();
|
||||
posts.addAll(newPosts);
|
||||
notifyItemRangeInserted(start, newPosts.size());
|
||||
}
|
||||
}
|
||||
|
||||
public void addPost(Post post) {
|
||||
if (post != null) {
|
||||
posts.add(0, post);
|
||||
|
|
@ -50,31 +76,107 @@ public class PostAdapter extends RecyclerView.Adapter<PostAdapter.PostViewHolder
|
|||
return posts.size();
|
||||
}
|
||||
|
||||
static class PostViewHolder extends RecyclerView.ViewHolder {
|
||||
class PostViewHolder extends RecyclerView.ViewHolder {
|
||||
private final ImageView ivAvatar;
|
||||
private final TextView tvUserName;
|
||||
private final TextView tvContent;
|
||||
private final TextView tvTime;
|
||||
private final TextView tvLikeCount;
|
||||
private final TextView tvCommentCount;
|
||||
private final LinearLayout imagesContainer;
|
||||
|
||||
public PostViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
ivAvatar = itemView.findViewById(R.id.iv_avatar);
|
||||
tvUserName = itemView.findViewById(R.id.tv_user_name);
|
||||
tvContent = itemView.findViewById(R.id.tv_content);
|
||||
tvTime = itemView.findViewById(R.id.tv_time);
|
||||
tvLikeCount = itemView.findViewById(R.id.tv_like_count);
|
||||
tvCommentCount = itemView.findViewById(R.id.tv_comment_count);
|
||||
imagesContainer = itemView.findViewById(R.id.images_container);
|
||||
|
||||
itemView.setOnClickListener(v -> {
|
||||
int pos = getAdapterPosition();
|
||||
if (pos != RecyclerView.NO_POSITION && listener != null) {
|
||||
listener.onItemClick(posts.get(pos));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void bind(Post post) {
|
||||
if (tvUserName != null) tvUserName.setText(post.getUserName());
|
||||
if (tvContent != null) tvContent.setText(post.getContent());
|
||||
if (tvTime != null) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("MM-dd HH:mm", Locale.getDefault());
|
||||
tvTime.setText(sdf.format(new Date(post.getTimestamp())));
|
||||
// 用户名
|
||||
if (tvUserName != null) {
|
||||
tvUserName.setText(post.getAuthorName() != null ? post.getAuthorName() : "匿名用户");
|
||||
}
|
||||
|
||||
// 内容
|
||||
if (tvContent != null) {
|
||||
tvContent.setText(post.getContent());
|
||||
}
|
||||
|
||||
// 时间
|
||||
if (tvTime != null) {
|
||||
if (post.getCreateTime() != null && !post.getCreateTime().isEmpty()) {
|
||||
tvTime.setText(post.getCreateTime());
|
||||
} else if (post.getTimestamp() > 0) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("MM-dd HH:mm", Locale.getDefault());
|
||||
tvTime.setText(sdf.format(new Date(post.getTimestamp())));
|
||||
}
|
||||
}
|
||||
|
||||
// 点赞数
|
||||
if (tvLikeCount != null) {
|
||||
tvLikeCount.setText(String.valueOf(post.getLikeCount()));
|
||||
}
|
||||
|
||||
// 评论数
|
||||
if (tvCommentCount != null) {
|
||||
tvCommentCount.setText(String.valueOf(post.getCommentCount()));
|
||||
}
|
||||
|
||||
// 头像
|
||||
if (ivAvatar != null) {
|
||||
String avatarUrl = post.getAuthorAvatar();
|
||||
if (avatarUrl != null && !avatarUrl.isEmpty()) {
|
||||
Glide.with(context)
|
||||
.load(avatarUrl)
|
||||
.placeholder(R.drawable.wish_tree_checker_backup)
|
||||
.error(R.drawable.wish_tree_checker_backup)
|
||||
.circleCrop()
|
||||
.into(ivAvatar);
|
||||
} else {
|
||||
ivAvatar.setImageResource(R.drawable.wish_tree_checker_backup);
|
||||
}
|
||||
}
|
||||
|
||||
// 图片列表
|
||||
if (imagesContainer != null) {
|
||||
imagesContainer.removeAllViews();
|
||||
List<String> images = post.getImages();
|
||||
if (images != null && !images.isEmpty()) {
|
||||
imagesContainer.setVisibility(View.VISIBLE);
|
||||
for (int i = 0; i < Math.min(images.size(), 9); i++) {
|
||||
ImageView imageView = new ImageView(context);
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||
200, 200
|
||||
);
|
||||
params.setMargins(0, 0, 8, 0);
|
||||
imageView.setLayoutParams(params);
|
||||
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||
|
||||
Glide.with(context)
|
||||
.load(images.get(i))
|
||||
.placeholder(R.drawable.wish_tree_checker_backup)
|
||||
.error(R.drawable.wish_tree_checker_backup)
|
||||
.centerCrop()
|
||||
.into(imageView);
|
||||
|
||||
imagesContainer.addView(imageView);
|
||||
}
|
||||
} else {
|
||||
imagesContainer.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
if (tvLikeCount != null) tvLikeCount.setText(String.valueOf(post.getLikeCount()));
|
||||
if (tvCommentCount != null) tvCommentCount.setText(String.valueOf(post.getCommentCount()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import android.graphics.drawable.Drawable;
|
|||
import android.os.Bundle;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
|
@ -24,6 +25,9 @@ import com.bumptech.glide.Glide;
|
|||
import com.example.livestreaming.BuildConfig;
|
||||
import com.example.livestreaming.databinding.ActivityProfileBinding;
|
||||
import com.example.livestreaming.ShareUtils;
|
||||
import com.example.livestreaming.net.ApiClient;
|
||||
import com.example.livestreaming.net.ApiResponse;
|
||||
import com.example.livestreaming.net.UserInfoResponse;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog;
|
||||
|
||||
|
|
@ -36,14 +40,13 @@ import java.util.Calendar;
|
|||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
public class ProfileActivity extends AppCompatActivity {
|
||||
|
||||
private static final String TAG = "ProfileActivity";
|
||||
private ActivityProfileBinding binding;
|
||||
|
||||
private static final String PREFS_NAME = "profile_prefs";
|
||||
|
|
@ -86,6 +89,7 @@ public class ProfileActivity extends AppCompatActivity {
|
|||
);
|
||||
|
||||
loadProfileFromPrefs();
|
||||
loadUserInfoFromServer(); // 从服务器加载用户信息
|
||||
loadAndDisplayTags();
|
||||
loadProfileInfo();
|
||||
loadFollowStats(); // 加载关注统计
|
||||
|
|
@ -191,6 +195,102 @@ public class ProfileActivity extends AppCompatActivity {
|
|||
// 等级/称号/徽章:保持固定显示(例如“月亮/星耀/至尊”),不从本地缓存覆盖。
|
||||
}
|
||||
|
||||
/**
|
||||
* 从服务器加载用户信息并同步到本地
|
||||
*/
|
||||
private void loadUserInfoFromServer() {
|
||||
if (!AuthHelper.isLoggedIn(this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ApiClient.getService(this).getUserInfo().enqueue(new Callback<ApiResponse<UserInfoResponse>>() {
|
||||
@Override
|
||||
public void onResponse(Call<ApiResponse<UserInfoResponse>> call, Response<ApiResponse<UserInfoResponse>> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().isOk()) {
|
||||
UserInfoResponse userInfo = response.body().getData();
|
||||
if (userInfo != null) {
|
||||
syncUserInfoToLocal(userInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<ApiResponse<UserInfoResponse>> call, Throwable t) {
|
||||
Log.e(TAG, "加载用户信息失败: " + t.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步服务器用户信息到本地并更新UI
|
||||
*/
|
||||
private void syncUserInfoToLocal(UserInfoResponse userInfo) {
|
||||
android.content.SharedPreferences.Editor editor = getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit();
|
||||
|
||||
// 同步昵称
|
||||
if (!TextUtils.isEmpty(userInfo.getNickname())) {
|
||||
editor.putString(KEY_NAME, userInfo.getNickname());
|
||||
binding.name.setText(userInfo.getNickname());
|
||||
}
|
||||
|
||||
// 同步个性签名
|
||||
if (!TextUtils.isEmpty(userInfo.getMark())) {
|
||||
editor.putString(KEY_BIO, userInfo.getMark());
|
||||
binding.bioText.setText(userInfo.getMark());
|
||||
binding.bioText.setTextColor(0xFF111111);
|
||||
}
|
||||
|
||||
// 同步头像
|
||||
if (!TextUtils.isEmpty(userInfo.getAvatar())) {
|
||||
String avatarUrl = userInfo.getAvatar();
|
||||
String baseUrl = ApiClient.getCurrentBaseUrl(ProfileActivity.this);
|
||||
|
||||
// 处理相对路径
|
||||
if (!avatarUrl.startsWith("http://") && !avatarUrl.startsWith("https://")) {
|
||||
if (avatarUrl.startsWith("crmebimage/")) {
|
||||
avatarUrl = baseUrl.replace("/api/", "/") + avatarUrl;
|
||||
}
|
||||
}
|
||||
|
||||
editor.putString(KEY_AVATAR_URI, avatarUrl);
|
||||
Glide.with(ProfileActivity.this)
|
||||
.load(avatarUrl)
|
||||
.circleCrop()
|
||||
.error(R.drawable.ic_account_circle_24)
|
||||
.placeholder(R.drawable.ic_account_circle_24)
|
||||
.into(binding.avatar);
|
||||
}
|
||||
|
||||
// 同步生日
|
||||
if (!TextUtils.isEmpty(userInfo.getBirthday())) {
|
||||
editor.putString(KEY_BIRTHDAY, userInfo.getBirthday());
|
||||
}
|
||||
|
||||
// 同步性别
|
||||
if (userInfo.getSex() != null) {
|
||||
String gender = "";
|
||||
switch (userInfo.getSex()) {
|
||||
case 1: gender = "男"; break;
|
||||
case 2: gender = "女"; break;
|
||||
case 3: gender = "保密"; break;
|
||||
}
|
||||
if (!TextUtils.isEmpty(gender)) {
|
||||
editor.putString(KEY_GENDER, gender);
|
||||
}
|
||||
}
|
||||
|
||||
// 同步地址
|
||||
if (!TextUtils.isEmpty(userInfo.getAddres())) {
|
||||
editor.putString(KEY_LOCATION, userInfo.getAddres());
|
||||
}
|
||||
|
||||
editor.apply();
|
||||
|
||||
// 刷新标签显示
|
||||
loadAndDisplayTags();
|
||||
loadProfileInfo();
|
||||
}
|
||||
|
||||
private void setupEditableAreas() {
|
||||
// TODO: 接入后端接口 - 更新用户资料
|
||||
// 接口路径: PUT /api/users/{userId}/profile
|
||||
|
|
@ -698,13 +798,21 @@ public class ProfileActivity extends AppCompatActivity {
|
|||
// 更新关注数
|
||||
Object followingCount = stats.get("followingCount");
|
||||
if (followingCount != null) {
|
||||
binding.following.setText(String.valueOf(followingCount) + "\n关注");
|
||||
int count = 0;
|
||||
if (followingCount instanceof Number) {
|
||||
count = ((Number) followingCount).intValue();
|
||||
}
|
||||
binding.following.setText(count + "\n关注");
|
||||
}
|
||||
|
||||
// 更新粉丝数
|
||||
Object followersCount = stats.get("followersCount");
|
||||
if (followersCount != null) {
|
||||
binding.followers.setText(String.valueOf(followersCount) + "\n粉丝");
|
||||
int count = 0;
|
||||
if (followersCount instanceof Number) {
|
||||
count = ((Number) followersCount).intValue();
|
||||
}
|
||||
binding.followers.setText(count + "\n粉丝");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,20 @@ import android.app.AlertDialog;
|
|||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.example.livestreaming.net.ApiClient;
|
||||
import com.example.livestreaming.net.ApiResponse;
|
||||
import com.example.livestreaming.net.ApiService;
|
||||
import com.example.livestreaming.net.CommunityResponse;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
public class PublishPostHelper {
|
||||
|
||||
public interface PublishCallback {
|
||||
|
|
@ -12,9 +26,22 @@ public class PublishPostHelper {
|
|||
void onError(String error);
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示发布对话框(旧版本,本地保存)
|
||||
*/
|
||||
public static void showPublishDialog(Activity activity, String category, PublishCallback callback) {
|
||||
showPublishDialog(activity, category, -1, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示发布对话框(新版本,支持API发布)
|
||||
* @param categoryId 板块ID,如果>0则通过API发布,否则本地保存
|
||||
*/
|
||||
public static void showPublishDialog(Activity activity, String category, int categoryId, PublishCallback callback) {
|
||||
EditText input = new EditText(activity);
|
||||
input.setHint("输入内容...");
|
||||
input.setMinLines(3);
|
||||
input.setPadding(32, 32, 32, 32);
|
||||
|
||||
new AlertDialog.Builder(activity)
|
||||
.setTitle("发布动态")
|
||||
|
|
@ -25,19 +52,86 @@ public class PublishPostHelper {
|
|||
Toast.makeText(activity, "内容不能为空", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
Post post = new Post();
|
||||
post.setId(String.valueOf(System.currentTimeMillis()));
|
||||
post.setContent(content);
|
||||
post.setCategory(category);
|
||||
post.setUserName("用户");
|
||||
post.setTimestamp(System.currentTimeMillis());
|
||||
|
||||
PostManager.savePost(activity, post);
|
||||
if (callback != null) {
|
||||
callback.onSuccess(post);
|
||||
if (categoryId > 0) {
|
||||
// 通过API发布
|
||||
publishToApi(activity, categoryId, category, content, callback);
|
||||
} else {
|
||||
// 本地保存(兼容旧逻辑)
|
||||
Post post = new Post();
|
||||
post.setId(System.currentTimeMillis());
|
||||
post.setContent(content);
|
||||
post.setCategory(category);
|
||||
post.setAuthorName("用户");
|
||||
post.setTimestamp(System.currentTimeMillis());
|
||||
|
||||
PostManager.savePost(activity, post);
|
||||
if (callback != null) {
|
||||
callback.onSuccess(post);
|
||||
}
|
||||
}
|
||||
})
|
||||
.setNegativeButton("取消", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过API发布消息
|
||||
*/
|
||||
private static void publishToApi(Activity activity, int categoryId, String category,
|
||||
String content, PublishCallback callback) {
|
||||
ApiService apiService = ApiClient.getService(activity);
|
||||
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("categoryId", categoryId);
|
||||
body.put("content", content);
|
||||
|
||||
apiService.publishCommunityMessage(body).enqueue(new Callback<ApiResponse<CommunityResponse.Message>>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse<CommunityResponse.Message>> call,
|
||||
@NonNull Response<ApiResponse<CommunityResponse.Message>> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().isOk()) {
|
||||
Toast.makeText(activity, "发布成功", Toast.LENGTH_SHORT).show();
|
||||
|
||||
// 转换为Post对象
|
||||
CommunityResponse.Message msg = response.body().getData();
|
||||
Post post = new Post();
|
||||
if (msg != null) {
|
||||
post.setId(msg.id);
|
||||
post.setAuthorName(msg.nickname);
|
||||
post.setAuthorAvatar(msg.avatar);
|
||||
post.setContent(msg.content);
|
||||
post.setCategory(category);
|
||||
post.setCreateTime(msg.createTime);
|
||||
} else {
|
||||
post.setId(System.currentTimeMillis());
|
||||
post.setContent(content);
|
||||
post.setCategory(category);
|
||||
}
|
||||
|
||||
if (callback != null) {
|
||||
callback.onSuccess(post);
|
||||
}
|
||||
} else {
|
||||
String errorMsg = "发布失败";
|
||||
if (response.body() != null && response.body().getMessage() != null) {
|
||||
errorMsg = response.body().getMessage();
|
||||
}
|
||||
Toast.makeText(activity, errorMsg, Toast.LENGTH_SHORT).show();
|
||||
if (callback != null) {
|
||||
callback.onError(errorMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse<CommunityResponse.Message>> call,
|
||||
@NonNull Throwable t) {
|
||||
Toast.makeText(activity, "网络错误,请重试", Toast.LENGTH_SHORT).show();
|
||||
if (callback != null) {
|
||||
callback.onError("网络错误");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,170 @@
|
|||
package com.example.livestreaming;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
import androidx.recyclerview.widget.ListAdapter;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
|
||||
/**
|
||||
* 推荐用户列表适配器(关注页面使用)
|
||||
*/
|
||||
public class RecommendUserAdapter extends ListAdapter<RecommendUserAdapter.RecommendUser, RecommendUserAdapter.ViewHolder> {
|
||||
|
||||
public interface OnUserActionListener {
|
||||
void onFollowClick(RecommendUser user, int position);
|
||||
void onRemoveClick(RecommendUser user, int position);
|
||||
void onUserClick(RecommendUser user);
|
||||
}
|
||||
|
||||
private OnUserActionListener listener;
|
||||
|
||||
public RecommendUserAdapter() {
|
||||
super(DIFF_CALLBACK);
|
||||
}
|
||||
|
||||
public void setOnUserActionListener(OnUserActionListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
private static final DiffUtil.ItemCallback<RecommendUser> DIFF_CALLBACK = new DiffUtil.ItemCallback<RecommendUser>() {
|
||||
@Override
|
||||
public boolean areItemsTheSame(@NonNull RecommendUser oldItem, @NonNull RecommendUser newItem) {
|
||||
return oldItem.getId() == newItem.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(@NonNull RecommendUser oldItem, @NonNull RecommendUser newItem) {
|
||||
return oldItem.equals(newItem);
|
||||
}
|
||||
};
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_recommend_user, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
RecommendUser user = getItem(position);
|
||||
holder.bind(user, position);
|
||||
}
|
||||
|
||||
class ViewHolder extends RecyclerView.ViewHolder {
|
||||
private final ImageView avatar;
|
||||
private final TextView name;
|
||||
private final TextView desc;
|
||||
private final ImageView verifiedIcon;
|
||||
private final TextView btnFollow;
|
||||
private final ImageView btnRemove;
|
||||
|
||||
ViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
avatar = itemView.findViewById(R.id.userAvatar);
|
||||
name = itemView.findViewById(R.id.userName);
|
||||
desc = itemView.findViewById(R.id.userDesc);
|
||||
verifiedIcon = itemView.findViewById(R.id.verifiedIcon);
|
||||
btnFollow = itemView.findViewById(R.id.btnFollow);
|
||||
btnRemove = itemView.findViewById(R.id.btnRemove);
|
||||
}
|
||||
|
||||
void bind(RecommendUser user, int position) {
|
||||
name.setText(user.getName());
|
||||
desc.setText(user.getDescription());
|
||||
|
||||
// 加载头像
|
||||
if (user.getAvatarUrl() != null && !user.getAvatarUrl().isEmpty()) {
|
||||
Glide.with(itemView.getContext())
|
||||
.load(user.getAvatarUrl())
|
||||
.placeholder(R.drawable.ic_account_circle_24)
|
||||
.error(R.drawable.ic_account_circle_24)
|
||||
.circleCrop()
|
||||
.into(avatar);
|
||||
} else {
|
||||
avatar.setImageResource(R.drawable.ic_account_circle_24);
|
||||
}
|
||||
|
||||
// 认证标识
|
||||
verifiedIcon.setVisibility(user.isVerified() ? View.VISIBLE : View.GONE);
|
||||
|
||||
// 关注状态
|
||||
if (user.isFollowed()) {
|
||||
btnFollow.setText("已关注");
|
||||
btnFollow.setTextColor(itemView.getContext().getResources().getColor(android.R.color.darker_gray, null));
|
||||
btnFollow.setBackgroundResource(R.drawable.bg_channel_tag);
|
||||
} else {
|
||||
btnFollow.setText("关注");
|
||||
btnFollow.setTextColor(itemView.getContext().getResources().getColor(R.color.red_primary, null));
|
||||
btnFollow.setBackgroundResource(R.drawable.bg_follow_button);
|
||||
}
|
||||
|
||||
// 点击事件
|
||||
itemView.setOnClickListener(v -> {
|
||||
if (listener != null) listener.onUserClick(user);
|
||||
});
|
||||
|
||||
btnFollow.setOnClickListener(v -> {
|
||||
if (listener != null) listener.onFollowClick(user, position);
|
||||
});
|
||||
|
||||
btnRemove.setOnClickListener(v -> {
|
||||
if (listener != null) listener.onRemoveClick(user, position);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 推荐用户数据类
|
||||
*/
|
||||
public static class RecommendUser {
|
||||
private int id;
|
||||
private String name;
|
||||
private String avatarUrl;
|
||||
private String description;
|
||||
private boolean verified;
|
||||
private boolean followed;
|
||||
|
||||
public RecommendUser(int id, String name, String avatarUrl, String description, boolean verified) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.avatarUrl = avatarUrl;
|
||||
this.description = description;
|
||||
this.verified = verified;
|
||||
this.followed = false;
|
||||
}
|
||||
|
||||
public int getId() { return id; }
|
||||
public String getName() { return name; }
|
||||
public String getAvatarUrl() { return avatarUrl; }
|
||||
public String getDescription() { return description; }
|
||||
public boolean isVerified() { return verified; }
|
||||
public boolean isFollowed() { return followed; }
|
||||
public void setFollowed(boolean followed) { this.followed = followed; }
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
RecommendUser that = (RecommendUser) o;
|
||||
return id == that.id && verified == that.verified && followed == that.followed
|
||||
&& java.util.Objects.equals(name, that.name)
|
||||
&& java.util.Objects.equals(avatarUrl, that.avatarUrl)
|
||||
&& java.util.Objects.equals(description, that.description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return java.util.Objects.hash(id, name, avatarUrl, description, verified, followed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
package com.example.livestreaming;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.example.livestreaming.net.CommunityResponse;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 心动信号卡片适配器 - 探探风格滑动卡片
|
||||
*/
|
||||
public class SwipeCardAdapter extends RecyclerView.Adapter<SwipeCardAdapter.ViewHolder> {
|
||||
|
||||
private final Context context;
|
||||
private final List<CommunityResponse.MatchUser> users = new ArrayList<>();
|
||||
|
||||
public SwipeCardAdapter(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public void setUsers(List<CommunityResponse.MatchUser> newUsers) {
|
||||
users.clear();
|
||||
if (newUsers != null) {
|
||||
users.addAll(newUsers);
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void addUsers(List<CommunityResponse.MatchUser> newUsers) {
|
||||
if (newUsers != null && !newUsers.isEmpty()) {
|
||||
int start = users.size();
|
||||
users.addAll(newUsers);
|
||||
notifyItemRangeInserted(start, newUsers.size());
|
||||
}
|
||||
}
|
||||
|
||||
public CommunityResponse.MatchUser getUser(int position) {
|
||||
if (position >= 0 && position < users.size()) {
|
||||
return users.get(position);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void removeUser(int position) {
|
||||
if (position >= 0 && position < users.size()) {
|
||||
users.remove(position);
|
||||
notifyItemRemoved(position);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(context).inflate(R.layout.item_swipe_card, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
CommunityResponse.MatchUser user = users.get(position);
|
||||
holder.bind(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return users.size();
|
||||
}
|
||||
|
||||
class ViewHolder extends RecyclerView.ViewHolder {
|
||||
private final ImageView userPhoto;
|
||||
private final TextView userName;
|
||||
private final TextView userAge;
|
||||
private final TextView userLocation;
|
||||
private final TextView userBio;
|
||||
private final ImageView likeStamp;
|
||||
private final ImageView nopeStamp;
|
||||
|
||||
ViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
userPhoto = itemView.findViewById(R.id.userPhoto);
|
||||
userName = itemView.findViewById(R.id.userName);
|
||||
userAge = itemView.findViewById(R.id.userAge);
|
||||
userLocation = itemView.findViewById(R.id.userLocation);
|
||||
userBio = itemView.findViewById(R.id.userBio);
|
||||
likeStamp = itemView.findViewById(R.id.likeStamp);
|
||||
nopeStamp = itemView.findViewById(R.id.nopeStamp);
|
||||
}
|
||||
|
||||
void bind(CommunityResponse.MatchUser user) {
|
||||
userName.setText(user.nickname);
|
||||
userAge.setText(user.age + "岁");
|
||||
userLocation.setText(user.location);
|
||||
userBio.setText(user.bio);
|
||||
|
||||
// 加载头像
|
||||
String photoUrl = null;
|
||||
if (user.photos != null && !user.photos.isEmpty()) {
|
||||
photoUrl = user.photos.get(0);
|
||||
} else {
|
||||
photoUrl = user.avatar;
|
||||
}
|
||||
|
||||
if (photoUrl != null && !photoUrl.isEmpty()) {
|
||||
Glide.with(context)
|
||||
.load(photoUrl)
|
||||
.placeholder(R.drawable.wish_tree_checker_backup)
|
||||
.error(R.drawable.wish_tree_checker_backup)
|
||||
.centerCrop()
|
||||
.into(userPhoto);
|
||||
} else {
|
||||
userPhoto.setImageResource(R.drawable.wish_tree_checker_backup);
|
||||
}
|
||||
|
||||
// 重置标签透明度
|
||||
likeStamp.setAlpha(0f);
|
||||
nopeStamp.setAlpha(0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示喜欢标签
|
||||
*/
|
||||
void showLikeStamp(float alpha) {
|
||||
likeStamp.setAlpha(Math.min(1f, alpha));
|
||||
nopeStamp.setAlpha(0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示跳过标签
|
||||
*/
|
||||
void showNopeStamp(float alpha) {
|
||||
nopeStamp.setAlpha(Math.min(1f, alpha));
|
||||
likeStamp.setAlpha(0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏所有标签
|
||||
*/
|
||||
void hideStamps() {
|
||||
likeStamp.setAlpha(0f);
|
||||
nopeStamp.setAlpha(0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,25 +3,32 @@ 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 androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.example.livestreaming.databinding.ActivityTableGamesBinding;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
|
||||
import java.util.List;
|
||||
/**
|
||||
* 桌游 - 从后端API加载消息
|
||||
*/
|
||||
public class TableGamesActivity extends BaseCategoryActivity {
|
||||
|
||||
public class TableGamesActivity extends AppCompatActivity {
|
||||
|
||||
private static final String CATEGORY = "桌子游戏";
|
||||
private static final int CATEGORY_ID = -1; // 通过名称自动查找
|
||||
private static final String CATEGORY_NAME = "桌游";
|
||||
|
||||
private ActivityTableGamesBinding binding;
|
||||
private PostAdapter postAdapter;
|
||||
|
||||
public static void start(Context context) {
|
||||
Intent intent = new Intent(context, TableGamesActivity.class);
|
||||
context.startActivity(intent);
|
||||
context.startActivity(new Intent(context, TableGamesActivity.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getCategoryId() {
|
||||
return CATEGORY_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCategoryName() {
|
||||
return CATEGORY_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -31,27 +38,12 @@ public class TableGamesActivity extends AppCompatActivity {
|
|||
setContentView(binding.getRoot());
|
||||
|
||||
setupUI();
|
||||
setupRecyclerView();
|
||||
loadPosts();
|
||||
initMessageList(binding.recyclerPosts, binding.emptyView, null, binding.fabPublish);
|
||||
}
|
||||
|
||||
private void setupUI() {
|
||||
binding.backButton.setOnClickListener(v -> finish());
|
||||
|
||||
binding.fabPublish.setOnClickListener(v -> {
|
||||
PublishPostHelper.showPublishDialog(this, CATEGORY, new PublishPostHelper.PublishCallback() {
|
||||
@Override
|
||||
public void onSuccess(Post post) {
|
||||
postAdapter.addPost(post);
|
||||
binding.recyclerPosts.scrollToPosition(0);
|
||||
updateEmptyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String error) {}
|
||||
});
|
||||
});
|
||||
|
||||
BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation;
|
||||
bottomNavigation.setSelectedItemId(R.id.nav_friends);
|
||||
UnreadMessageManager.updateBadge(bottomNavigation);
|
||||
|
|
@ -81,28 +73,6 @@ public class TableGamesActivity extends AppCompatActivity {
|
|||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void setupRecyclerView() {
|
||||
postAdapter = new PostAdapter();
|
||||
binding.recyclerPosts.setLayoutManager(new LinearLayoutManager(this));
|
||||
binding.recyclerPosts.setAdapter(postAdapter);
|
||||
}
|
||||
|
||||
private void loadPosts() {
|
||||
List<Post> posts = PostManager.getPostsByCategory(this, CATEGORY);
|
||||
postAdapter.setPosts(posts);
|
||||
updateEmptyView();
|
||||
}
|
||||
|
||||
private void updateEmptyView() {
|
||||
if (postAdapter.getItemCount() == 0) {
|
||||
binding.emptyView.setVisibility(View.VISIBLE);
|
||||
binding.recyclerPosts.setVisibility(View.GONE);
|
||||
} else {
|
||||
binding.emptyView.setVisibility(View.GONE);
|
||||
binding.recyclerPosts.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
|
|
@ -111,7 +81,6 @@ public class TableGamesActivity extends AppCompatActivity {
|
|||
BottomNavigationView bottomNav = binding.bottomNavInclude.bottomNavigation;
|
||||
bottomNav.setSelectedItemId(R.id.nav_friends);
|
||||
UnreadMessageManager.updateBadge(bottomNav);
|
||||
loadPosts();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import com.example.livestreaming.databinding.ActivityUserProfileReadOnlyBinding;
|
|||
import com.example.livestreaming.net.ApiResponse;
|
||||
import com.example.livestreaming.net.ApiService;
|
||||
import com.example.livestreaming.net.ApiClient;
|
||||
import com.example.livestreaming.net.AuthStore;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -35,9 +36,13 @@ public class UserProfileReadOnlyActivity extends AppCompatActivity {
|
|||
private static final String EXTRA_LOCATION = "extra_location";
|
||||
private static final String EXTRA_BIO = "extra_bio";
|
||||
private static final String EXTRA_AVATAR_RES = "extra_avatar_res";
|
||||
private static final String EXTRA_AVATAR_URL = "extra_avatar_url";
|
||||
|
||||
private String currentUserId;
|
||||
private String currentUserName;
|
||||
private boolean isFollowing = false;
|
||||
private boolean isFriend = false;
|
||||
private boolean hasPendingRequest = false; // 是否已发送好友请求
|
||||
|
||||
public static void start(Context context, String userId, String name, String location, String bio, int avatarRes) {
|
||||
Intent intent = new Intent(context, UserProfileReadOnlyActivity.class);
|
||||
|
|
@ -49,6 +54,16 @@ public class UserProfileReadOnlyActivity extends AppCompatActivity {
|
|||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
public static void start(Context context, String userId, String name, String location, String bio, String avatarUrl) {
|
||||
Intent intent = new Intent(context, UserProfileReadOnlyActivity.class);
|
||||
intent.putExtra(EXTRA_USER_ID, userId);
|
||||
intent.putExtra(EXTRA_NAME, name);
|
||||
intent.putExtra(EXTRA_LOCATION, location);
|
||||
intent.putExtra(EXTRA_BIO, bio);
|
||||
intent.putExtra(EXTRA_AVATAR_URL, avatarUrl);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
|
@ -58,20 +73,39 @@ public class UserProfileReadOnlyActivity extends AppCompatActivity {
|
|||
binding.backButton.setOnClickListener(v -> finish());
|
||||
|
||||
currentUserId = getIntent().getStringExtra(EXTRA_USER_ID);
|
||||
String name = getIntent().getStringExtra(EXTRA_NAME);
|
||||
currentUserName = getIntent().getStringExtra(EXTRA_NAME);
|
||||
String name = currentUserName;
|
||||
String location = getIntent().getStringExtra(EXTRA_LOCATION);
|
||||
String bio = getIntent().getStringExtra(EXTRA_BIO);
|
||||
int avatarRes = getIntent().getIntExtra(EXTRA_AVATAR_RES, R.drawable.wish_tree_checker_backup);
|
||||
String avatarUrl = getIntent().getStringExtra(EXTRA_AVATAR_URL);
|
||||
|
||||
binding.name.setText(name != null ? name : "");
|
||||
binding.location.setText(location != null ? location : "");
|
||||
binding.bio.setText(bio != null ? bio : "");
|
||||
binding.avatar.setImageResource(avatarRes);
|
||||
|
||||
// 优先使用avatarUrl,如果没有则使用avatarRes
|
||||
if (avatarUrl != null && !avatarUrl.isEmpty()) {
|
||||
com.bumptech.glide.Glide.with(this)
|
||||
.load(avatarUrl)
|
||||
.placeholder(R.drawable.wish_tree_checker_backup)
|
||||
.error(R.drawable.wish_tree_checker_backup)
|
||||
.circleCrop()
|
||||
.into(binding.avatar);
|
||||
} else {
|
||||
binding.avatar.setImageResource(avatarRes);
|
||||
}
|
||||
|
||||
// 设置头像点击放大功能
|
||||
final String finalAvatarUrl = avatarUrl;
|
||||
final int finalAvatarRes = avatarRes;
|
||||
binding.avatar.setOnClickListener(v -> {
|
||||
AvatarViewerDialog dialog = AvatarViewerDialog.create(this);
|
||||
dialog.setAvatarResId(avatarRes);
|
||||
if (finalAvatarUrl != null && !finalAvatarUrl.isEmpty()) {
|
||||
dialog.setAvatarUrl(finalAvatarUrl);
|
||||
} else {
|
||||
dialog.setAvatarResId(finalAvatarRes);
|
||||
}
|
||||
dialog.show();
|
||||
});
|
||||
|
||||
|
|
@ -80,9 +114,15 @@ public class UserProfileReadOnlyActivity extends AppCompatActivity {
|
|||
|
||||
// 检查关注状态
|
||||
checkFollowStatus();
|
||||
|
||||
// 检查好友状态
|
||||
checkFriendStatus();
|
||||
|
||||
// 关注/取消关注按钮点击事件
|
||||
binding.addFriendButton.setOnClickListener(v -> {
|
||||
// 关注按钮点击事件
|
||||
binding.followButton.setOnClickListener(v -> {
|
||||
if (!AuthHelper.requireLogin(this, "关注需要登录")) {
|
||||
return;
|
||||
}
|
||||
if (TextUtils.isEmpty(currentUserId)) {
|
||||
Toast.makeText(this, "用户信息缺失", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
|
|
@ -94,6 +134,26 @@ public class UserProfileReadOnlyActivity extends AppCompatActivity {
|
|||
followUser();
|
||||
}
|
||||
});
|
||||
|
||||
// 加好友按钮点击事件
|
||||
binding.addFriendButton.setOnClickListener(v -> {
|
||||
if (!AuthHelper.requireLogin(this, "加好友需要登录")) {
|
||||
return;
|
||||
}
|
||||
if (TextUtils.isEmpty(currentUserId)) {
|
||||
Toast.makeText(this, "用户信息缺失", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (isFriend) {
|
||||
// 已是好友,跳转到私聊
|
||||
openChat();
|
||||
} else if (hasPendingRequest) {
|
||||
Toast.makeText(this, "已发送好友请求,请等待对方处理", Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
sendFriendRequest();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void checkFollowStatus() {
|
||||
|
|
@ -216,13 +276,177 @@ public class UserProfileReadOnlyActivity extends AppCompatActivity {
|
|||
private void updateFollowButton() {
|
||||
if (binding == null) return;
|
||||
if (isFollowing) {
|
||||
binding.addFriendButton.setText("已关注");
|
||||
binding.followButton.setText("已关注");
|
||||
binding.followButton.setSelected(true);
|
||||
binding.followButton.setTextColor(getResources().getColor(android.R.color.darker_gray, null));
|
||||
} else {
|
||||
binding.followButton.setText("关注");
|
||||
binding.followButton.setSelected(false);
|
||||
binding.followButton.setTextColor(getResources().getColor(R.color.red_primary, null));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查好友状态
|
||||
*/
|
||||
private void checkFriendStatus() {
|
||||
if (TextUtils.isEmpty(currentUserId)) return;
|
||||
|
||||
String token = AuthStore.getToken(this);
|
||||
if (token == null) return;
|
||||
|
||||
try {
|
||||
int userId = Integer.parseInt(currentUserId);
|
||||
ApiService apiService = ApiClient.getService(this);
|
||||
Call<ApiResponse<Map<String, Object>>> call = apiService.checkFriendStatus(userId);
|
||||
|
||||
call.enqueue(new Callback<ApiResponse<Map<String, Object>>>() {
|
||||
@Override
|
||||
public void onResponse(Call<ApiResponse<Map<String, Object>>> call,
|
||||
Response<ApiResponse<Map<String, Object>>> response) {
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
ApiResponse<Map<String, Object>> apiResponse = response.body();
|
||||
if (apiResponse.getCode() == 200 && apiResponse.getData() != null) {
|
||||
Map<String, Object> data = apiResponse.getData();
|
||||
Boolean friend = (Boolean) data.get("isFriend");
|
||||
Boolean pending = (Boolean) data.get("hasPendingRequest");
|
||||
isFriend = friend != null && friend;
|
||||
hasPendingRequest = pending != null && pending;
|
||||
updateAddFriendButton();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<ApiResponse<Map<String, Object>>> call, Throwable t) {
|
||||
// 忽略错误,使用默认状态
|
||||
}
|
||||
});
|
||||
} catch (NumberFormatException e) {
|
||||
// 忽略错误
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送好友请求
|
||||
*/
|
||||
private void sendFriendRequest() {
|
||||
if (TextUtils.isEmpty(currentUserId)) return;
|
||||
|
||||
try {
|
||||
int userId = Integer.parseInt(currentUserId);
|
||||
Map<String, Object> requestBody = new HashMap<>();
|
||||
requestBody.put("targetUserId", userId);
|
||||
requestBody.put("message", "我想加你为好友");
|
||||
|
||||
ApiService apiService = ApiClient.getService(this);
|
||||
Call<ApiResponse<Boolean>> call = apiService.sendFriendRequest(requestBody);
|
||||
|
||||
call.enqueue(new Callback<ApiResponse<Boolean>>() {
|
||||
@Override
|
||||
public void onResponse(Call<ApiResponse<Boolean>> call,
|
||||
Response<ApiResponse<Boolean>> response) {
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
ApiResponse<Boolean> apiResponse = response.body();
|
||||
if (apiResponse.getCode() == 200) {
|
||||
hasPendingRequest = true;
|
||||
updateAddFriendButton();
|
||||
Toast.makeText(UserProfileReadOnlyActivity.this, "好友请求已发送", Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Toast.makeText(UserProfileReadOnlyActivity.this,
|
||||
apiResponse.getMessage() != null ? apiResponse.getMessage() : "发送失败",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(UserProfileReadOnlyActivity.this, "网络请求失败", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<ApiResponse<Boolean>> call, Throwable t) {
|
||||
Toast.makeText(UserProfileReadOnlyActivity.this, "网络错误: " + t.getMessage(),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
} catch (NumberFormatException e) {
|
||||
Toast.makeText(this, "用户ID格式错误", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新加好友按钮状态
|
||||
*/
|
||||
private void updateAddFriendButton() {
|
||||
if (binding == null) return;
|
||||
if (isFriend) {
|
||||
binding.addFriendButton.setText("发消息");
|
||||
binding.addFriendButton.setBackgroundResource(R.drawable.bg_purple_999);
|
||||
} else if (hasPendingRequest) {
|
||||
binding.addFriendButton.setText("已申请");
|
||||
binding.addFriendButton.setAlpha(0.7f);
|
||||
} else {
|
||||
binding.addFriendButton.setText("关注");
|
||||
binding.addFriendButton.setText("加好友");
|
||||
binding.addFriendButton.setAlpha(1f);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开私聊
|
||||
*/
|
||||
private void openChat() {
|
||||
if (TextUtils.isEmpty(currentUserId)) return;
|
||||
|
||||
String friendName = currentUserName != null ? currentUserName : "好友";
|
||||
|
||||
// 先获取或创建与好友的会话,再打开私聊页面
|
||||
String token = com.example.livestreaming.net.AuthStore.getToken(this);
|
||||
if (token == null) {
|
||||
Toast.makeText(this, "请先登录", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
String url = ApiConfig.getBaseUrl() + "/api/front/conversations/with/" + currentUserId;
|
||||
|
||||
okhttp3.OkHttpClient httpClient = new okhttp3.OkHttpClient();
|
||||
okhttp3.Request request = new okhttp3.Request.Builder()
|
||||
.url(url)
|
||||
.addHeader("Authori-zation", token)
|
||||
.post(okhttp3.RequestBody.create("", okhttp3.MediaType.parse("application/json")))
|
||||
.build();
|
||||
|
||||
httpClient.newCall(request).enqueue(new okhttp3.Callback() {
|
||||
@Override
|
||||
public void onFailure(okhttp3.Call call, java.io.IOException e) {
|
||||
runOnUiThread(() -> {
|
||||
Toast.makeText(UserProfileReadOnlyActivity.this, "打开会话失败", Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(okhttp3.Call call, okhttp3.Response response) throws java.io.IOException {
|
||||
String body = response.body() != null ? response.body().string() : "";
|
||||
runOnUiThread(() -> {
|
||||
try {
|
||||
org.json.JSONObject json = new org.json.JSONObject(body);
|
||||
if (json.optInt("code", -1) == 200) {
|
||||
org.json.JSONObject data = json.optJSONObject("data");
|
||||
String conversationId = data != null ? data.optString("conversationId", "") : "";
|
||||
if (!conversationId.isEmpty()) {
|
||||
ConversationActivity.start(UserProfileReadOnlyActivity.this, conversationId, friendName);
|
||||
} else {
|
||||
Toast.makeText(UserProfileReadOnlyActivity.this, "获取会话ID失败", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
} else {
|
||||
String msg = json.optString("message", "打开会话失败");
|
||||
Toast.makeText(UserProfileReadOnlyActivity.this, msg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Toast.makeText(UserProfileReadOnlyActivity.this, "打开会话失败", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupTabsAndWorks() {
|
||||
if (binding == null) return;
|
||||
|
|
|
|||
|
|
@ -1,18 +1,33 @@
|
|||
package com.example.livestreaming;
|
||||
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.View;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.example.livestreaming.databinding.ActivityVoiceMatchBinding;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
|
||||
/**
|
||||
* 语音匹配页面 - 通过语音快速匹配志同道合的朋友
|
||||
*/
|
||||
public class VoiceMatchActivity extends AppCompatActivity {
|
||||
|
||||
private ActivityVoiceMatchBinding binding;
|
||||
private Handler handler = new Handler(Looper.getMainLooper());
|
||||
private AnimatorSet pulseAnimator;
|
||||
private boolean isMatching = false;
|
||||
private Runnable matchingRunnable;
|
||||
|
||||
public static void start(Context context) {
|
||||
Intent intent = new Intent(context, VoiceMatchActivity.class);
|
||||
|
|
@ -26,17 +41,163 @@ public class VoiceMatchActivity extends AppCompatActivity {
|
|||
setContentView(binding.getRoot());
|
||||
|
||||
setupUI();
|
||||
loadUserAvatar();
|
||||
}
|
||||
|
||||
private void setupUI() {
|
||||
binding.backButton.setOnClickListener(v -> finish());
|
||||
|
||||
// 开始匹配按钮
|
||||
binding.btnStartMatch.setOnClickListener(v -> startMatching());
|
||||
|
||||
// 取消匹配按钮
|
||||
binding.btnCancelMatch.setOnClickListener(v -> cancelMatching());
|
||||
|
||||
setupBottomNav();
|
||||
}
|
||||
|
||||
private void loadUserAvatar() {
|
||||
SharedPreferences prefs = getSharedPreferences("profile_prefs", MODE_PRIVATE);
|
||||
String avatarUrl = prefs.getString("profile_avatar", "");
|
||||
|
||||
if (!avatarUrl.isEmpty()) {
|
||||
Glide.with(this)
|
||||
.load(avatarUrl)
|
||||
.placeholder(R.drawable.wish_tree_checker_backup)
|
||||
.error(R.drawable.wish_tree_checker_backup)
|
||||
.circleCrop()
|
||||
.into(binding.userAvatar);
|
||||
}
|
||||
}
|
||||
|
||||
private void startMatching() {
|
||||
if (isMatching) return;
|
||||
isMatching = true;
|
||||
|
||||
// 更新UI
|
||||
binding.btnStartMatch.setVisibility(View.GONE);
|
||||
binding.btnCancelMatch.setVisibility(View.VISIBLE);
|
||||
binding.statusText.setText("正在匹配中...");
|
||||
binding.matchingHint.setVisibility(View.VISIBLE);
|
||||
|
||||
// 开始脉冲动画
|
||||
startPulseAnimation();
|
||||
|
||||
// 模拟匹配过程
|
||||
matchingRunnable = () -> {
|
||||
if (isMatching) {
|
||||
// 模拟匹配成功
|
||||
onMatchSuccess();
|
||||
}
|
||||
};
|
||||
handler.postDelayed(matchingRunnable, 5000 + (long) (Math.random() * 5000));
|
||||
}
|
||||
|
||||
private void cancelMatching() {
|
||||
if (!isMatching) return;
|
||||
isMatching = false;
|
||||
|
||||
// 取消匹配任务
|
||||
if (matchingRunnable != null) {
|
||||
handler.removeCallbacks(matchingRunnable);
|
||||
}
|
||||
|
||||
// 停止动画
|
||||
stopPulseAnimation();
|
||||
|
||||
// 更新UI
|
||||
binding.btnStartMatch.setVisibility(View.VISIBLE);
|
||||
binding.btnCancelMatch.setVisibility(View.GONE);
|
||||
binding.statusText.setText("点击开始匹配");
|
||||
binding.matchingHint.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private void onMatchSuccess() {
|
||||
isMatching = false;
|
||||
stopPulseAnimation();
|
||||
|
||||
// 更新UI
|
||||
binding.statusText.setText("匹配成功!");
|
||||
binding.matchingHint.setText("正在连接...");
|
||||
|
||||
// 显示匹配成功对话框
|
||||
handler.postDelayed(() -> {
|
||||
new androidx.appcompat.app.AlertDialog.Builder(this)
|
||||
.setTitle("🎉 匹配成功")
|
||||
.setMessage("已为你匹配到一位小伙伴!\n是否开始语音通话?")
|
||||
.setPositiveButton("开始通话", (dialog, which) -> {
|
||||
// TODO: 开始语音通话
|
||||
Toast.makeText(this, "语音通话功能开发中", Toast.LENGTH_SHORT).show();
|
||||
resetUI();
|
||||
})
|
||||
.setNegativeButton("继续匹配", (dialog, which) -> {
|
||||
resetUI();
|
||||
startMatching();
|
||||
})
|
||||
.setOnCancelListener(dialog -> resetUI())
|
||||
.show();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
private void resetUI() {
|
||||
binding.btnStartMatch.setVisibility(View.VISIBLE);
|
||||
binding.btnCancelMatch.setVisibility(View.GONE);
|
||||
binding.statusText.setText("点击开始匹配");
|
||||
binding.matchingHint.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private void startPulseAnimation() {
|
||||
// 外圈脉冲动画
|
||||
ObjectAnimator scaleX1 = ObjectAnimator.ofFloat(binding.pulseRing1, "scaleX", 1f, 1.3f);
|
||||
ObjectAnimator scaleY1 = ObjectAnimator.ofFloat(binding.pulseRing1, "scaleY", 1f, 1.3f);
|
||||
ObjectAnimator alpha1 = ObjectAnimator.ofFloat(binding.pulseRing1, "alpha", 1f, 0f);
|
||||
|
||||
ObjectAnimator scaleX2 = ObjectAnimator.ofFloat(binding.pulseRing2, "scaleX", 1f, 1.5f);
|
||||
ObjectAnimator scaleY2 = ObjectAnimator.ofFloat(binding.pulseRing2, "scaleY", 1f, 1.5f);
|
||||
ObjectAnimator alpha2 = ObjectAnimator.ofFloat(binding.pulseRing2, "alpha", 1f, 0f);
|
||||
|
||||
pulseAnimator = new AnimatorSet();
|
||||
pulseAnimator.playTogether(scaleX1, scaleY1, alpha1, scaleX2, scaleY2, alpha2);
|
||||
pulseAnimator.setDuration(1500);
|
||||
pulseAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
|
||||
|
||||
pulseAnimator.addListener(new android.animation.AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(android.animation.Animator animation) {
|
||||
if (isMatching) {
|
||||
// 重置并重新开始
|
||||
binding.pulseRing1.setScaleX(1f);
|
||||
binding.pulseRing1.setScaleY(1f);
|
||||
binding.pulseRing1.setAlpha(1f);
|
||||
binding.pulseRing2.setScaleX(1f);
|
||||
binding.pulseRing2.setScaleY(1f);
|
||||
binding.pulseRing2.setAlpha(1f);
|
||||
pulseAnimator.start();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
pulseAnimator.start();
|
||||
}
|
||||
|
||||
private void stopPulseAnimation() {
|
||||
if (pulseAnimator != null) {
|
||||
pulseAnimator.cancel();
|
||||
}
|
||||
// 重置状态
|
||||
binding.pulseRing1.setScaleX(1f);
|
||||
binding.pulseRing1.setScaleY(1f);
|
||||
binding.pulseRing1.setAlpha(1f);
|
||||
binding.pulseRing2.setScaleX(1f);
|
||||
binding.pulseRing2.setScaleY(1f);
|
||||
binding.pulseRing2.setAlpha(1f);
|
||||
}
|
||||
|
||||
private void setupBottomNav() {
|
||||
BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation;
|
||||
bottomNavigation.setSelectedItemId(R.id.nav_friends);
|
||||
|
||||
// 更新未读消息徽章
|
||||
UnreadMessageManager.updateBadge(bottomNavigation);
|
||||
|
||||
|
||||
bottomNavigation.setOnItemSelectedListener(item -> {
|
||||
int id = item.getItemId();
|
||||
if (id == R.id.nav_home) {
|
||||
|
|
@ -44,6 +205,11 @@ public class VoiceMatchActivity extends AppCompatActivity {
|
|||
finish();
|
||||
return true;
|
||||
}
|
||||
if (id == R.id.nav_friends) {
|
||||
startActivity(new Intent(this, FishPondActivity.class));
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
if (id == R.id.nav_wish_tree) {
|
||||
WishTreeActivity.start(this);
|
||||
finish();
|
||||
|
|
@ -62,16 +228,33 @@ public class VoiceMatchActivity extends AppCompatActivity {
|
|||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
if (binding != null) {
|
||||
BottomNavigationView bottomNav = binding.bottomNavInclude.bottomNavigation;
|
||||
bottomNav.setSelectedItemId(R.id.nav_friends);
|
||||
// 更新未读消息徽章
|
||||
UnreadMessageManager.updateBadge(bottomNav);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
// 离开页面时取消匹配
|
||||
if (isMatching) {
|
||||
cancelMatching();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (matchingRunnable != null) {
|
||||
handler.removeCallbacks(matchingRunnable);
|
||||
}
|
||||
stopPulseAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -70,6 +70,12 @@ public class WishTreeActivity extends AppCompatActivity {
|
|||
setContentView(binding.getRoot());
|
||||
|
||||
apiService = ApiClient.getService(this);
|
||||
|
||||
// 打印当前API地址和登录状态
|
||||
String baseUrl = com.example.livestreaming.net.ApiClient.getCurrentBaseUrl(this);
|
||||
String token = AuthStore.getToken(this);
|
||||
android.util.Log.d("WishTree", "API地址: " + baseUrl);
|
||||
android.util.Log.d("WishTree", "Token: " + (token != null && !token.isEmpty() ? "已登录" : "未登录"));
|
||||
|
||||
startBannerCountdown();
|
||||
setupBottomNav();
|
||||
|
|
@ -144,20 +150,41 @@ public class WishTreeActivity extends AppCompatActivity {
|
|||
* 从服务端加载我的心愿
|
||||
*/
|
||||
private void loadMyWishes() {
|
||||
android.util.Log.d("WishTree", "开始加载我的心愿列表");
|
||||
apiService.getMyWishes(1, 10).enqueue(new Callback<ApiResponse<WishtreeResponse.WishPage>>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse<WishtreeResponse.WishPage>> call,
|
||||
@NonNull Response<ApiResponse<WishtreeResponse.WishPage>> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getData() != null) {
|
||||
myWishes = response.body().getData().list;
|
||||
if (myWishes == null) myWishes = new ArrayList<>();
|
||||
updateWishCards();
|
||||
updateWishCount();
|
||||
android.util.Log.d("WishTree", "加载心愿响应: code=" + response.code());
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
android.util.Log.d("WishTree", "响应body: code=" + response.body().getCode());
|
||||
if (response.body().getData() != null) {
|
||||
myWishes = response.body().getData().list;
|
||||
if (myWishes == null) myWishes = new ArrayList<>();
|
||||
android.util.Log.d("WishTree", "加载到 " + myWishes.size() + " 条心愿");
|
||||
updateWishCards();
|
||||
updateWishCount();
|
||||
} else {
|
||||
android.util.Log.w("WishTree", "响应数据为空");
|
||||
myWishes = new ArrayList<>();
|
||||
updateWishCards();
|
||||
updateWishCount();
|
||||
}
|
||||
} else {
|
||||
android.util.Log.e("WishTree", "加载心愿失败: " + response.code());
|
||||
try {
|
||||
if (response.errorBody() != null) {
|
||||
android.util.Log.e("WishTree", "错误响应: " + response.errorBody().string());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
android.util.Log.e("WishTree", "读取错误响应失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse<WishtreeResponse.WishPage>> call, @NonNull Throwable t) {
|
||||
android.util.Log.e("WishTree", "加载心愿网络错误", t);
|
||||
Toast.makeText(WishTreeActivity.this, "加载心愿失败", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
|
|
@ -271,31 +298,64 @@ public class WishTreeActivity extends AppCompatActivity {
|
|||
* 发布心愿到服务端
|
||||
*/
|
||||
private void publishWish(String content, Dialog dialog) {
|
||||
// 检查登录状态
|
||||
String token = AuthStore.getToken(this);
|
||||
if (token == null || token.isEmpty()) {
|
||||
Toast.makeText(this, "请先登录", Toast.LENGTH_SHORT).show();
|
||||
startActivity(new Intent(this, LoginActivity.class));
|
||||
return;
|
||||
}
|
||||
|
||||
Integer festivalId = currentFestival != null ? currentFestival.id : null;
|
||||
WishtreeRequest.PublishWish request = new WishtreeRequest.PublishWish(festivalId, content, null);
|
||||
|
||||
android.util.Log.d("WishTree", "发布心愿请求: festivalId=" + festivalId + ", content=" + content);
|
||||
|
||||
apiService.publishWish(request).enqueue(new Callback<ApiResponse<WishtreeResponse.Wish>>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse<WishtreeResponse.Wish>> call,
|
||||
@NonNull Response<ApiResponse<WishtreeResponse.Wish>> response) {
|
||||
android.util.Log.d("WishTree", "发布心愿响应: code=" + response.code());
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
if (response.body().getCode() == 200) {
|
||||
android.util.Log.d("WishTree", "响应body: code=" + response.body().getCode() + ", msg=" + response.body().getMessage());
|
||||
if (response.body().isOk()) {
|
||||
dialog.dismiss();
|
||||
showSuccessDialog();
|
||||
loadMyWishes(); // 重新加载心愿列表
|
||||
} else {
|
||||
String msg = response.body().getMessage();
|
||||
Toast.makeText(WishTreeActivity.this,
|
||||
response.body().getMessage() != null ? response.body().getMessage() : "发布失败",
|
||||
msg != null && !msg.isEmpty() ? msg : "发布失败",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(WishTreeActivity.this, "发布失败", Toast.LENGTH_SHORT).show();
|
||||
// 尝试读取错误响应
|
||||
String errorMsg = "发布失败";
|
||||
try {
|
||||
if (response.errorBody() != null) {
|
||||
String errorBody = response.errorBody().string();
|
||||
android.util.Log.e("WishTree", "错误响应: " + errorBody);
|
||||
errorMsg = "发布失败: " + response.code();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
android.util.Log.e("WishTree", "读取错误响应失败", e);
|
||||
}
|
||||
Toast.makeText(WishTreeActivity.this, errorMsg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse<WishtreeResponse.Wish>> call, @NonNull Throwable t) {
|
||||
Toast.makeText(WishTreeActivity.this, "网络错误", Toast.LENGTH_SHORT).show();
|
||||
android.util.Log.e("WishTree", "发布心愿网络错误", t);
|
||||
String errorMsg = "网络错误";
|
||||
if (t instanceof java.net.UnknownHostException) {
|
||||
errorMsg = "无法连接服务器,请检查网络";
|
||||
} else if (t instanceof java.net.SocketTimeoutException) {
|
||||
errorMsg = "连接超时,请重试";
|
||||
} else if (t.getMessage() != null) {
|
||||
errorMsg = "网络错误: " + t.getMessage();
|
||||
}
|
||||
Toast.makeText(WishTreeActivity.this, errorMsg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -387,7 +447,7 @@ public class WishTreeActivity extends AppCompatActivity {
|
|||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse<String>> call,
|
||||
@NonNull Response<ApiResponse<String>> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getCode() == 200) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().isOk()) {
|
||||
dialog.dismiss();
|
||||
if (isComplete) {
|
||||
showCompleteSuccessDialog();
|
||||
|
|
|
|||
|
|
@ -152,6 +152,12 @@ public interface ApiService {
|
|||
@Path("requestId") long requestId,
|
||||
@Body Map<String, Object> body);
|
||||
|
||||
/**
|
||||
* 检查好友状态(是否是好友、是否有待处理的好友请求)
|
||||
*/
|
||||
@GET("api/front/friends/check/{userId}")
|
||||
Call<ApiResponse<Map<String, Object>>> checkFriendStatus(@Path("userId") int userId);
|
||||
|
||||
// ==================== 文件上传 ====================
|
||||
|
||||
@Multipart
|
||||
|
|
@ -510,4 +516,84 @@ public interface ApiService {
|
|||
*/
|
||||
@GET("api/front/streamer/applications")
|
||||
Call<ApiResponse<List<Map<String, Object>>>> getStreamerApplications();
|
||||
|
||||
// ==================== 社区/缘池接口 ====================
|
||||
|
||||
/**
|
||||
* 获取社区板块列表
|
||||
*/
|
||||
@GET("api/front/community/categories")
|
||||
Call<ApiResponse<List<CommunityResponse.Category>>> getCommunityCategories();
|
||||
|
||||
/**
|
||||
* 获取社区消息列表
|
||||
*/
|
||||
@GET("api/front/community/messages")
|
||||
Call<ApiResponse<CommunityResponse.MessagePage>> getCommunityMessages(
|
||||
@Query("categoryId") int categoryId,
|
||||
@Query("page") int page,
|
||||
@Query("limit") int limit);
|
||||
|
||||
/**
|
||||
* 发布社区消息
|
||||
*/
|
||||
@POST("api/front/community/messages")
|
||||
Call<ApiResponse<CommunityResponse.Message>> publishCommunityMessage(@Body Map<String, Object> body);
|
||||
|
||||
/**
|
||||
* 点赞社区消息
|
||||
*/
|
||||
@POST("api/front/community/messages/{id}/like")
|
||||
Call<ApiResponse<String>> likeCommunityMessage(@Path("id") long id);
|
||||
|
||||
/**
|
||||
* 取消点赞社区消息
|
||||
*/
|
||||
@DELETE("api/front/community/messages/{id}/like")
|
||||
Call<ApiResponse<String>> unlikeCommunityMessage(@Path("id") long id);
|
||||
|
||||
/**
|
||||
* 获取附近用户(缘池功能)
|
||||
*/
|
||||
@GET("api/front/community/nearby-users")
|
||||
Call<ApiResponse<CommunityResponse.NearbyUserList>> getNearbyUsers(
|
||||
@Query("latitude") double latitude,
|
||||
@Query("longitude") double longitude,
|
||||
@Query("radius") int radius,
|
||||
@Query("limit") int limit);
|
||||
|
||||
/**
|
||||
* 获取可匹配用户总数
|
||||
*/
|
||||
@GET("api/front/community/user-count")
|
||||
Call<ApiResponse<Integer>> getMatchableUserCount();
|
||||
|
||||
// ==================== 心动信号/匹配接口 ====================
|
||||
|
||||
/**
|
||||
* 获取推荐匹配用户列表
|
||||
*/
|
||||
@GET("api/front/match/recommend")
|
||||
Call<ApiResponse<List<CommunityResponse.MatchUser>>> getRecommendUsers(
|
||||
@Query("limit") int limit);
|
||||
|
||||
/**
|
||||
* 喜欢用户(右滑)
|
||||
*/
|
||||
@POST("api/front/match/like")
|
||||
Call<ApiResponse<CommunityResponse.MatchResult>> likeUser(@Body Map<String, Object> body);
|
||||
|
||||
/**
|
||||
* 跳过用户(左滑)
|
||||
*/
|
||||
@POST("api/front/match/skip")
|
||||
Call<ApiResponse<String>> skipUser(@Body Map<String, Object> body);
|
||||
|
||||
/**
|
||||
* 获取匹配列表(互相喜欢的用户)
|
||||
*/
|
||||
@GET("api/front/match/list")
|
||||
Call<ApiResponse<List<CommunityResponse.MatchUser>>> getMatchList(
|
||||
@Query("page") int page,
|
||||
@Query("limit") int limit);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,150 @@
|
|||
package com.example.livestreaming.net;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 社区相关响应数据类
|
||||
*/
|
||||
public class CommunityResponse {
|
||||
|
||||
/**
|
||||
* 板块/分类
|
||||
*/
|
||||
public static class Category {
|
||||
public int id;
|
||||
public String name;
|
||||
public String icon;
|
||||
public String type; // quick=快捷入口 card=功能卡片
|
||||
public String jumpPage; // 跳转页面
|
||||
public String description;
|
||||
public int sort;
|
||||
public int status;
|
||||
|
||||
public int getId() { return id; }
|
||||
public String getName() { return name; }
|
||||
public String getIcon() { return icon; }
|
||||
public String getType() { return type; }
|
||||
public String getJumpPage() { return jumpPage; }
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息/帖子
|
||||
*/
|
||||
public static class Message {
|
||||
public long id;
|
||||
public int userId;
|
||||
public String nickname;
|
||||
public String avatar;
|
||||
public int categoryId;
|
||||
public String categoryName;
|
||||
public String content;
|
||||
public String images; // 图片URL(逗号分隔的字符串)
|
||||
public int likeCount;
|
||||
public int commentCount;
|
||||
public int viewCount;
|
||||
public int status;
|
||||
public String createTime;
|
||||
public String updateTime;
|
||||
public boolean isLiked; // 当前用户是否已点赞
|
||||
|
||||
/**
|
||||
* 获取图片列表
|
||||
*/
|
||||
public List<String> getImageList() {
|
||||
List<String> list = new java.util.ArrayList<>();
|
||||
if (images != null && !images.isEmpty()) {
|
||||
String[] arr = images.split(",");
|
||||
for (String url : arr) {
|
||||
String trimmed = url.trim();
|
||||
if (!trimmed.isEmpty()) {
|
||||
list.add(trimmed);
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息分页
|
||||
*/
|
||||
public static class MessagePage {
|
||||
public List<Message> list;
|
||||
public int total;
|
||||
public int page;
|
||||
public int limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* 附近用户
|
||||
*/
|
||||
public static class NearbyUser {
|
||||
public int id;
|
||||
public String nickname;
|
||||
public String avatar;
|
||||
public String gender;
|
||||
public int age;
|
||||
public String location;
|
||||
public String bio;
|
||||
public double distance; // 距离(米)
|
||||
public boolean isOnline;
|
||||
public String lastActiveTime;
|
||||
|
||||
public int getUserId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getNickname() {
|
||||
return nickname;
|
||||
}
|
||||
|
||||
public String getAvatar() {
|
||||
return avatar;
|
||||
}
|
||||
|
||||
public String getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public boolean isOnline() {
|
||||
return isOnline;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 附近用户列表
|
||||
*/
|
||||
public static class NearbyUserList {
|
||||
public List<NearbyUser> list;
|
||||
public int total;
|
||||
|
||||
public List<NearbyUser> getUsers() {
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 匹配用户(心动信号)
|
||||
*/
|
||||
public static class MatchUser {
|
||||
public int id;
|
||||
public String nickname;
|
||||
public String avatar;
|
||||
public String gender;
|
||||
public int age;
|
||||
public String location;
|
||||
public String bio;
|
||||
public List<String> photos; // 用户照片列表
|
||||
public List<String> tags; // 用户标签
|
||||
public double matchScore; // 匹配度
|
||||
}
|
||||
|
||||
/**
|
||||
* 匹配结果
|
||||
*/
|
||||
public static class MatchResult {
|
||||
public boolean isMatch; // 是否互相喜欢
|
||||
public MatchUser matchedUser;
|
||||
public long conversationId; // 如果匹配成功,返回会话ID
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,18 @@ public class UserEditRequest {
|
|||
@SerializedName("avatar")
|
||||
private String avatar;
|
||||
|
||||
@SerializedName("birthday")
|
||||
private String birthday;
|
||||
|
||||
@SerializedName("sex")
|
||||
private Integer sex; // 0=未知, 1=男, 2=女, 3=保密
|
||||
|
||||
@SerializedName("addres")
|
||||
private String addres; // 地址
|
||||
|
||||
@SerializedName("mark")
|
||||
private String mark; // 个人签名/备注
|
||||
|
||||
public UserEditRequest() {}
|
||||
|
||||
public UserEditRequest(String nickname, String avatar) {
|
||||
|
|
@ -19,6 +31,15 @@ public class UserEditRequest {
|
|||
|
||||
public void setNickname(String nickname) { this.nickname = nickname; }
|
||||
public void setAvatar(String avatar) { this.avatar = avatar; }
|
||||
public void setBirthday(String birthday) { this.birthday = birthday; }
|
||||
public void setSex(Integer sex) { this.sex = sex; }
|
||||
public void setAddres(String addres) { this.addres = addres; }
|
||||
public void setMark(String mark) { this.mark = mark; }
|
||||
|
||||
public String getNickname() { return nickname; }
|
||||
public String getAvatar() { return avatar; }
|
||||
public String getBirthday() { return birthday; }
|
||||
public Integer getSex() { return sex; }
|
||||
public String getAddres() { return addres; }
|
||||
public String getMark() { return mark; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,18 @@ public class UserInfoResponse {
|
|||
@SerializedName("collectCount")
|
||||
private Integer collectCount;
|
||||
|
||||
@SerializedName("mark")
|
||||
private String mark; // 个性签名
|
||||
|
||||
@SerializedName("addres")
|
||||
private String addres; // 地址
|
||||
|
||||
@SerializedName("birthday")
|
||||
private String birthday; // 生日
|
||||
|
||||
@SerializedName("sex")
|
||||
private Integer sex; // 性别:0=未知,1=男,2=女,3=保密
|
||||
|
||||
public Integer getUid() { return uid; }
|
||||
public String getNickname() { return nickname; }
|
||||
public String getAvatar() { return avatar; }
|
||||
|
|
@ -69,4 +81,8 @@ public class UserInfoResponse {
|
|||
public String getVipName() { return vipName; }
|
||||
public Boolean getRechargeSwitch() { return rechargeSwitch; }
|
||||
public Integer getCollectCount() { return collectCount; }
|
||||
public String getMark() { return mark; }
|
||||
public String getAddres() { return addres; }
|
||||
public String getBirthday() { return birthday; }
|
||||
public Integer getSex() { return sex; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
|
||||
<solid android:color="@android:color/transparent" />
|
||||
<!-- 使用淡紫色背景,与页面主题协调 -->
|
||||
<solid android:color="#E8D5F0" />
|
||||
</shape>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#F5F5F5" />
|
||||
<corners android:radius="14dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#E0E0E0" />
|
||||
</shape>
|
||||
9
android-app/app/src/main/res/drawable/bg_channel_tag.xml
Normal file
9
android-app/app/src/main/res/drawable/bg_channel_tag.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#E0E0E0" />
|
||||
<corners android:radius="4dp" />
|
||||
<solid android:color="@android:color/white" />
|
||||
</shape>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#F8F8F8" />
|
||||
<corners android:radius="4dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#E8E8E8" />
|
||||
</shape>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#333333" />
|
||||
<corners android:radius="4dp" />
|
||||
<solid android:color="@android:color/white" />
|
||||
</shape>
|
||||
|
|
@ -1,9 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<solid android:color="@android:color/transparent" />
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="#111111" />
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#FF4757" />
|
||||
<corners android:radius="8dp" />
|
||||
</shape>
|
||||
|
||||
|
|
|
|||
19
android-app/app/src/main/res/drawable/bg_follow_button.xml
Normal file
19
android-app/app/src/main/res/drawable/bg_follow_button.xml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- 已关注状态 -->
|
||||
<item android:state_selected="true">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#F5F5F5" />
|
||||
<corners android:radius="22dp" />
|
||||
<stroke android:width="1dp" android:color="#DDDDDD" />
|
||||
</shape>
|
||||
</item>
|
||||
<!-- 未关注状态 -->
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#FFFFFF" />
|
||||
<corners android:radius="22dp" />
|
||||
<stroke android:width="1dp" android:color="#FF4757" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<gradient
|
||||
android:angle="270"
|
||||
android:endColor="#99000000"
|
||||
android:startColor="#00000000" />
|
||||
</shape>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#FF4757" />
|
||||
<corners android:radius="8dp" />
|
||||
</shape>
|
||||
8
android-app/app/src/main/res/drawable/bg_pulse_ring.xml
Normal file
8
android-app/app/src/main/res/drawable/bg_pulse_ring.xml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="#55FFFFFF" />
|
||||
<solid android:color="#00000000" />
|
||||
</shape>
|
||||
6
android-app/app/src/main/res/drawable/bg_tag_pink.xml
Normal file
6
android-app/app/src/main/res/drawable/bg_tag_pink.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#FFECF0" />
|
||||
<corners android:radius="12dp" />
|
||||
</shape>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- 圆形渐变背景 -->
|
||||
<item>
|
||||
<shape android:shape="oval">
|
||||
<gradient
|
||||
android:type="linear"
|
||||
android:angle="135"
|
||||
android:startColor="#63B3ED"
|
||||
android:endColor="#4299E1" />
|
||||
</shape>
|
||||
</item>
|
||||
<!-- 用户图标(小圆点代表头部) -->
|
||||
<item
|
||||
android:top="16dp"
|
||||
android:bottom="28dp"
|
||||
android:left="24dp"
|
||||
android:right="24dp">
|
||||
<shape android:shape="oval">
|
||||
<solid android:color="#FFFFFF" />
|
||||
</shape>
|
||||
</item>
|
||||
<!-- 用户图标(半圆代表身体) -->
|
||||
<item
|
||||
android:top="36dp"
|
||||
android:bottom="12dp"
|
||||
android:left="16dp"
|
||||
android:right="16dp">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#FFFFFF" />
|
||||
<corners android:topLeftRadius="20dp" android:topRightRadius="20dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- 圆形渐变背景 -->
|
||||
<item>
|
||||
<shape android:shape="oval">
|
||||
<gradient
|
||||
android:type="linear"
|
||||
android:angle="135"
|
||||
android:startColor="#F687B3"
|
||||
android:endColor="#ED64A6" />
|
||||
</shape>
|
||||
</item>
|
||||
<!-- 用户图标(小圆点代表头部) -->
|
||||
<item
|
||||
android:top="16dp"
|
||||
android:bottom="28dp"
|
||||
android:left="24dp"
|
||||
android:right="24dp">
|
||||
<shape android:shape="oval">
|
||||
<solid android:color="#FFFFFF" />
|
||||
</shape>
|
||||
</item>
|
||||
<!-- 用户图标(半圆代表身体) -->
|
||||
<item
|
||||
android:top="36dp"
|
||||
android:bottom="12dp"
|
||||
android:left="16dp"
|
||||
android:right="16dp">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#FFFFFF" />
|
||||
<corners android:topLeftRadius="20dp" android:topRightRadius="20dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- 圆形渐变背景 -->
|
||||
<item>
|
||||
<shape android:shape="oval">
|
||||
<gradient
|
||||
android:type="linear"
|
||||
android:angle="135"
|
||||
android:startColor="#B794F6"
|
||||
android:endColor="#9F7AEA" />
|
||||
</shape>
|
||||
</item>
|
||||
<!-- 用户图标 -->
|
||||
<item
|
||||
android:top="18dp"
|
||||
android:bottom="18dp"
|
||||
android:left="18dp"
|
||||
android:right="18dp">
|
||||
<shape android:shape="oval">
|
||||
<solid android:color="#FFFFFF" />
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
10
android-app/app/src/main/res/drawable/ic_add_12.xml
Normal file
10
android-app/app/src/main/res/drawable/ic_add_12.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="12dp"
|
||||
android:height="12dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#999999"
|
||||
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
|
||||
</vector>
|
||||
10
android-app/app/src/main/res/drawable/ic_close_circle_16.xml
Normal file
10
android-app/app/src/main/res/drawable/ic_close_circle_16.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#999999"
|
||||
android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM17,15.59L15.59,17 12,13.41 8.41,17 7,15.59 10.59,12 7,8.41 8.41,7 12,10.59 15.59,7 17,8.41 13.41,12 17,15.59z"/>
|
||||
</vector>
|
||||
10
android-app/app/src/main/res/drawable/ic_expand_more.xml
Normal file
10
android-app/app/src/main/res/drawable/ic_expand_more.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#999999"
|
||||
android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z"/>
|
||||
</vector>
|
||||
10
android-app/app/src/main/res/drawable/ic_grid3x3_24.xml
Normal file
10
android-app/app/src/main/res/drawable/ic_grid3x3_24.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#20B2AA"
|
||||
android:pathData="M4,4h4v4H4V4zM10,4h4v4h-4V4zM16,4h4v4h-4V4zM4,10h4v4H4v-4zM10,10h4v4h-4v-4zM16,10h4v4h-4v-4zM4,16h4v4H4v-4zM10,16h4v4h-4v-4zM16,16h4v4h-4v-4z" />
|
||||
</vector>
|
||||
10
android-app/app/src/main/res/drawable/ic_headset_24.xml
Normal file
10
android-app/app/src/main/res/drawable/ic_headset_24.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#00CED1"
|
||||
android:pathData="M12,1c-4.97,0 -9,4.03 -9,9v7c0,1.66 1.34,3 3,3h3v-8H5v-2c0,-3.87 3.13,-7 7,-7s7,3.13 7,7v2h-4v8h3c1.66,0 3,-1.34 3,-3v-7c0,-4.97 -4.03,-9 -9,-9z" />
|
||||
</vector>
|
||||
10
android-app/app/src/main/res/drawable/ic_info_outline.xml
Normal file
10
android-app/app/src/main/res/drawable/ic_info_outline.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#999999"
|
||||
android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2V7h-2v2z"/>
|
||||
</vector>
|
||||
20
android-app/app/src/main/res/drawable/ic_like_stamp.xml
Normal file
20
android-app/app/src/main/res/drawable/ic_like_stamp.xml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<stroke
|
||||
android:width="4dp"
|
||||
android:color="#4CAF50" />
|
||||
<corners android:radius="8dp" />
|
||||
</shape>
|
||||
</item>
|
||||
<item
|
||||
android:bottom="30dp"
|
||||
android:left="10dp"
|
||||
android:right="10dp"
|
||||
android:top="30dp">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#4CAF50" />
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
11
android-app/app/src/main/res/drawable/ic_live_tv_24.xml
Normal file
11
android-app/app/src/main/res/drawable/ic_live_tv_24.xml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="#999999">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M21,6h-7.59l3.29,-3.29L16,2l-4,4 -4,-4 -0.71,0.71L10.59,6H3c-1.1,0 -2,0.89 -2,2v12c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2V8c0,-1.11 -0.9,-2 -2,-2zM21,20H3V8h18v12zM9,10v8l7,-4z"/>
|
||||
</vector>
|
||||
10
android-app/app/src/main/res/drawable/ic_location_16.xml
Normal file
10
android-app/app/src/main/res/drawable/ic_location_16.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#999999"
|
||||
android:pathData="M12,2C8.13,2 5,5.13 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.87 -3.13,-7 -7,-7zM12,11.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z" />
|
||||
</vector>
|
||||
10
android-app/app/src/main/res/drawable/ic_location_on.xml
Normal file
10
android-app/app/src/main/res/drawable/ic_location_on.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#999999"
|
||||
android:pathData="M12,2C8.13,2 5,5.13 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.87 -3.13,-7 -7,-7zM12,11.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z"/>
|
||||
</vector>
|
||||
10
android-app/app/src/main/res/drawable/ic_lock_12.xml
Normal file
10
android-app/app/src/main/res/drawable/ic_lock_12.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="12dp"
|
||||
android:height="12dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#999999"
|
||||
android:pathData="M18,8h-1V6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2H6c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8H8.9V6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z"/>
|
||||
</vector>
|
||||
10
android-app/app/src/main/res/drawable/ic_message_24.xml
Normal file
10
android-app/app/src/main/res/drawable/ic_message_24.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#9C27B0"
|
||||
android:pathData="M20,2H4C2.9,2 2.01,2.9 2.01,4L2,22l4,-4h14c1.1,0 2,-0.9 2,-2V4C22,2.9 21.1,2 20,2zM6,9h12v2H6V9zM14,14H6v-2h8V14zM18,8H6V6h12V8z" />
|
||||
</vector>
|
||||
20
android-app/app/src/main/res/drawable/ic_nope_stamp.xml
Normal file
20
android-app/app/src/main/res/drawable/ic_nope_stamp.xml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<stroke
|
||||
android:width="4dp"
|
||||
android:color="#F44336" />
|
||||
<corners android:radius="8dp" />
|
||||
</shape>
|
||||
</item>
|
||||
<item
|
||||
android:bottom="30dp"
|
||||
android:left="10dp"
|
||||
android:right="10dp"
|
||||
android:top="30dp">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#F44336" />
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
10
android-app/app/src/main/res/drawable/ic_play_circle_24.xml
Normal file
10
android-app/app/src/main/res/drawable/ic_play_circle_24.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF6347"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM10,16.5v-9l6,4.5 -6,4.5z" />
|
||||
</vector>
|
||||
10
android-app/app/src/main/res/drawable/ic_refresh_24.xml
Normal file
10
android-app/app/src/main/res/drawable/ic_refresh_24.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
|
||||
</vector>
|
||||
10
android-app/app/src/main/res/drawable/ic_smartphone_24.xml
Normal file
10
android-app/app/src/main/res/drawable/ic_smartphone_24.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF8C00"
|
||||
android:pathData="M17,1.01L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19H7V5h10v14z" />
|
||||
</vector>
|
||||
10
android-app/app/src/main/res/drawable/ic_star_24.xml
Normal file
10
android-app/app/src/main/res/drawable/ic_star_24.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FFD700"
|
||||
android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z" />
|
||||
</vector>
|
||||
19
android-app/app/src/main/res/drawable/ic_target_24.xml
Normal file
19
android-app/app/src/main/res/drawable/ic_target_24.xml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<!-- 外圈 -->
|
||||
<path
|
||||
android:fillColor="#FF8C00"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z" />
|
||||
<!-- 中圈 -->
|
||||
<path
|
||||
android:fillColor="#FF8C00"
|
||||
android:pathData="M12,6c-3.31,0 -6,2.69 -6,6s2.69,6 6,6 6,-2.69 6,-6 -2.69,-6 -6,-6zM12,16c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4 4,1.79 4,4 -1.79,4 -4,4z" />
|
||||
<!-- 中心点(圆形用path表示) -->
|
||||
<path
|
||||
android:fillColor="#FF8C00"
|
||||
android:pathData="M12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z" />
|
||||
</vector>
|
||||
10
android-app/app/src/main/res/drawable/ic_thumb_up.xml
Normal file
10
android-app/app/src/main/res/drawable/ic_thumb_up.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF4757"
|
||||
android:pathData="M1,21h4V9H1v12zM23,10c0,-1.1 -0.9,-2 -2,-2h-6.31l0.95,-4.57 0.03,-0.32c0,-0.41 -0.17,-0.79 -0.44,-1.06L14.17,1 7.59,7.59C7.22,7.95 7,8.45 7,9v10c0,1.1 0.9,2 2,2h9c0.83,0 1.54,-0.5 1.84,-1.22l3.02,-7.05c0.09,-0.23 0.14,-0.47 0.14,-0.73v-2z"/>
|
||||
</vector>
|
||||
10
android-app/app/src/main/res/drawable/ic_user_24.xml
Normal file
10
android-app/app/src/main/res/drawable/ic_user_24.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF69B4"
|
||||
android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z" />
|
||||
</vector>
|
||||
10
android-app/app/src/main/res/drawable/ic_verified.xml
Normal file
10
android-app/app/src/main/res/drawable/ic_verified.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF4757"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"/>
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
<?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="#F5F5F5">
|
||||
|
||||
<!-- 顶部栏 -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/topBar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="56dp"
|
||||
android:background="@color/white"
|
||||
android:elevation="2dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/backButton"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:contentDescription="返回"
|
||||
android:src="@drawable/ic_arrow_back_24"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:tint="#333333" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/titleText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="动态社区"
|
||||
android:textColor="#111111"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<!-- 下拉刷新 -->
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefresh"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/bottomNavInclude"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/topBar">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="80dp" />
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/emptyView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toTopOf="@id/bottomNavInclude"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/topBar">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="120dp"
|
||||
android:alpha="0.5"
|
||||
android:src="@drawable/ic_message_24"
|
||||
app:tint="#CCCCCC" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="暂无动态"
|
||||
android:textColor="#999999"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="快来发布第一条动态吧"
|
||||
android:textColor="#CCCCCC"
|
||||
android:textSize="12sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 发布按钮 -->
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fabPublish"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="80dp"
|
||||
android:contentDescription="发布"
|
||||
android:src="@drawable/ic_add_24"
|
||||
app:backgroundTint="@color/purple_500"
|
||||
app:layout_constraintBottom_toTopOf="@id/bottomNavInclude"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:tint="@color/white" />
|
||||
|
||||
<!-- 底部导航 -->
|
||||
<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>
|
||||
|
|
@ -98,6 +98,7 @@
|
|||
android:id="@+id/orbitRing"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_margin="36dp"
|
||||
android:background="@drawable/bg_orbit_ring"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
|
@ -483,83 +484,29 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:padding="10dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/actionLeft"
|
||||
<!-- 动态加载的板块RecyclerView -->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/categoryRecyclerView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="44dp"
|
||||
android:background="@drawable/bg_pill_left"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:paddingStart="14dp"
|
||||
android:paddingEnd="14dp"
|
||||
app:layout_constraintEnd_toStartOf="@id/actionRight"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/actionLeftIcon"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:src="@drawable/ic_voice_24"
|
||||
android:tint="#C218F0"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/actionLeftText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:text="语音 ~ 匹配 ~"
|
||||
android:textColor="#C218F0"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/actionLeftIcon"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/actionRight"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="44dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:background="@drawable/bg_pill_right"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:paddingStart="14dp"
|
||||
android:paddingEnd="14dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:overScrollMode="never"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/actionLeft"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/actionRightIcon"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:src="@drawable/ic_heart_24"
|
||||
android:tint="#FF2D77"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/actionRightText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:text="心动信号 ~"
|
||||
android:textColor="#FF2D77"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/actionRightIcon"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
<!-- 加载中提示 -->
|
||||
<ProgressBar
|
||||
android:id="@+id/categoryLoading"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="40dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<!-- 保留旧的GridLayout作为备用(隐藏) -->
|
||||
<GridLayout
|
||||
android:id="@+id/grid"
|
||||
android:layout_width="0dp"
|
||||
|
|
@ -567,9 +514,10 @@
|
|||
android:layout_marginTop="10dp"
|
||||
android:columnCount="3"
|
||||
android:rowCount="2"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/actionLeft">
|
||||
app:layout_constraintTop_toBottomOf="@id/categoryRecyclerView">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="0dp"
|
||||
|
|
|
|||
|
|
@ -27,14 +27,15 @@
|
|||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/backButton" />
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtitleText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:gravity="center"
|
||||
android:text="发现让你心动的那个TA"
|
||||
android:textColor="#666666"
|
||||
|
|
@ -43,6 +44,122 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/titleText" />
|
||||
|
||||
<!-- 卡片容器 -->
|
||||
<FrameLayout
|
||||
android:id="@+id/cardContainer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/actionButtons"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/subtitleText" />
|
||||
|
||||
<!-- 空状态 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/emptyView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toTopOf="@id/actionButtons"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/subtitleText">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="100dp"
|
||||
android:alpha="0.5"
|
||||
android:src="@drawable/ic_heart_24"
|
||||
app:tint="#FFCCCC" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="暂无更多推荐"
|
||||
android:textColor="#999999"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnRefresh"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="刷新推荐"
|
||||
app:backgroundTint="#FF4081" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 加载中 -->
|
||||
<ProgressBar
|
||||
android:id="@+id/loadingView"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toTopOf="@id/actionButtons"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/subtitleText" />
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/actionButtons"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintBottom_toTopOf="@id/bottomNavInclude"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<!-- 跳过按钮 -->
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/btnSkip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:contentDescription="跳过"
|
||||
android:src="@drawable/ic_close_24"
|
||||
app:backgroundTint="#FFFFFF"
|
||||
app:elevation="4dp"
|
||||
app:fabSize="normal"
|
||||
app:tint="#F44336" />
|
||||
|
||||
<!-- 超级喜欢按钮 -->
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/btnSuperLike"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:contentDescription="超级喜欢"
|
||||
android:src="@drawable/ic_star_24"
|
||||
app:backgroundTint="#FFFFFF"
|
||||
app:elevation="4dp"
|
||||
app:fabSize="mini"
|
||||
app:tint="#2196F3" />
|
||||
|
||||
<!-- 喜欢按钮 -->
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/btnLike"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:contentDescription="喜欢"
|
||||
android:src="@drawable/ic_heart_24"
|
||||
app:backgroundTint="#FFFFFF"
|
||||
app:elevation="4dp"
|
||||
app:fabSize="normal"
|
||||
app:tint="#4CAF50" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<include
|
||||
android:id="@+id/bottomNavInclude"
|
||||
layout="@layout/include_bottom_nav"
|
||||
|
|
|
|||
|
|
@ -147,80 +147,127 @@
|
|||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/categoryTabs"
|
||||
<!-- 分类标签容器 - 包含标签和展开箭头 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/categoryTabsContainer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="6dp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:visibility="gone"
|
||||
app:tabIndicatorColor="#FF4757"
|
||||
app:tabIndicatorFullWidth="false"
|
||||
app:tabMode="scrollable"
|
||||
app:tabSelectedTextColor="#333333"
|
||||
app:tabTextColor="#999999"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/searchContainer">
|
||||
app:layout_constraintTop_toBottomOf="@id/topTabs">
|
||||
|
||||
<com.google.android.material.tabs.TabItem
|
||||
android:layout_width="wrap_content"
|
||||
<!-- 分类标签 - 显示5个标签 -->
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/categoryTabs"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="推荐" />
|
||||
android:layout_weight="1"
|
||||
android:paddingStart="8dp"
|
||||
app:tabIndicatorColor="#FF4757"
|
||||
app:tabIndicatorFullWidth="false"
|
||||
app:tabMode="scrollable"
|
||||
app:tabGravity="start"
|
||||
app:tabSelectedTextColor="#333333"
|
||||
app:tabTextColor="#999999">
|
||||
|
||||
<com.google.android.material.tabs.TabItem
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="游戏" />
|
||||
<com.google.android.material.tabs.TabItem
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="推荐" />
|
||||
|
||||
<com.google.android.material.tabs.TabItem
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="才艺" />
|
||||
<com.google.android.material.tabs.TabItem
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="直播" />
|
||||
|
||||
<com.google.android.material.tabs.TabItem
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="户外" />
|
||||
<com.google.android.material.tabs.TabItem
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="视频" />
|
||||
|
||||
<com.google.android.material.tabs.TabItem
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="音乐" />
|
||||
<com.google.android.material.tabs.TabItem
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="音乐" />
|
||||
|
||||
<com.google.android.material.tabs.TabItem
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="美食" />
|
||||
<com.google.android.material.tabs.TabItem
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="游戏" />
|
||||
</com.google.android.material.tabs.TabLayout>
|
||||
|
||||
<com.google.android.material.tabs.TabItem
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="聊天" />
|
||||
</com.google.android.material.tabs.TabLayout>
|
||||
<!-- 展开箭头按钮 -->
|
||||
<ImageView
|
||||
android:id="@+id/btnExpandCategories"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:padding="8dp"
|
||||
android:src="@drawable/ic_expand_more_24"
|
||||
android:contentDescription="展开更多分类"
|
||||
app:tint="#666666" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefresh"
|
||||
<!-- Tab内容容器 -->
|
||||
<FrameLayout
|
||||
android:id="@+id/tabContentContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/roomsRecyclerView"
|
||||
<!-- 关注Tab内容 -->
|
||||
<include
|
||||
android:id="@+id/followTabContent"
|
||||
layout="@layout/layout_follow_tab"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="88dp" />
|
||||
android:visibility="gone" />
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
<!-- 发现Tab内容 -->
|
||||
<include
|
||||
android:id="@+id/discoverTabContent"
|
||||
layout="@layout/layout_discover_tab"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone" />
|
||||
|
||||
<!-- 附近Tab内容 -->
|
||||
<include
|
||||
android:id="@+id/nearbyTabContent"
|
||||
layout="@layout/layout_nearby_tab"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone" />
|
||||
|
||||
<!-- 原有的下拉刷新和列表(发现页面使用) -->
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefresh"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/roomsRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="88dp" />
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/loading"
|
||||
|
|
@ -238,6 +285,23 @@
|
|||
android:visibility="gone"
|
||||
/>
|
||||
|
||||
<!-- 发布按钮 - 红色白十字,小圆角长方形 -->
|
||||
<TextView
|
||||
android:id="@+id/btnPublish"
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_gravity="bottom|start"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginBottom="96dp"
|
||||
android:background="@drawable/bg_publish_button"
|
||||
android:gravity="center"
|
||||
android:text="+"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone"
|
||||
android:elevation="4dp" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fabAddLive"
|
||||
android:layout_width="56dp"
|
||||
|
|
|
|||
|
|
@ -70,8 +70,9 @@
|
|||
android:layout_marginTop="10dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintEnd_toStartOf="@id/topActionClock"
|
||||
app:layout_constraintStart_toEndOf="@id/avatarRing"
|
||||
app:layout_constraintStart_toStartOf="@id/name"
|
||||
app:layout_constraintTop_toBottomOf="@id/name">
|
||||
|
||||
<TextView
|
||||
|
|
@ -137,12 +138,11 @@
|
|||
android:id="@+id/idRow"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintEnd_toStartOf="@id/topActionClock"
|
||||
app:layout_constraintStart_toEndOf="@id/avatarRing"
|
||||
app:layout_constraintStart_toStartOf="@id/name"
|
||||
app:layout_constraintTop_toBottomOf="@id/chipsRow">
|
||||
|
||||
<ImageView
|
||||
|
|
@ -231,7 +231,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:text="12\n关注"
|
||||
android:text="0\n关注"
|
||||
android:textColor="#111111"
|
||||
android:textStyle="bold" />
|
||||
|
||||
|
|
@ -269,81 +269,89 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/statsRow" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/tagRow"
|
||||
<HorizontalScrollView
|
||||
android:id="@+id/tagScrollView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:orientation="horizontal"
|
||||
android:scrollbars="none"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/bioText">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tagLocation"
|
||||
<LinearLayout
|
||||
android:id="@+id/tagRow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="24dp"
|
||||
android:background="@drawable/bg_gray_12"
|
||||
android:gravity="center"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:text="IP:广西"
|
||||
android:textColor="#666666"
|
||||
android:textSize="11sp" />
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tagGender"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:background="@drawable/bg_gray_12"
|
||||
android:gravity="center"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:text="H"
|
||||
android:textColor="#666666"
|
||||
android:textSize="11sp" />
|
||||
<TextView
|
||||
android:id="@+id/tagLocation"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="24dp"
|
||||
android:background="@drawable/bg_gray_12"
|
||||
android:gravity="center"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:text="IP:广西"
|
||||
android:textColor="#666666"
|
||||
android:textSize="11sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tagAge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:background="@drawable/bg_gray_12"
|
||||
android:gravity="center"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:text="25岁"
|
||||
android:textColor="#666666"
|
||||
android:textSize="11sp" />
|
||||
<TextView
|
||||
android:id="@+id/tagGender"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:background="@drawable/bg_gray_12"
|
||||
android:gravity="center"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:text="男"
|
||||
android:textColor="#666666"
|
||||
android:textSize="11sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tagConstellation"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:background="@drawable/bg_gray_12"
|
||||
android:gravity="center"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:text="金牛座"
|
||||
android:textColor="#666666"
|
||||
android:textSize="11sp" />
|
||||
<TextView
|
||||
android:id="@+id/tagAge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:background="@drawable/bg_gray_12"
|
||||
android:gravity="center"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:text="25岁"
|
||||
android:textColor="#666666"
|
||||
android:textSize="11sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tagPersonality"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:background="@drawable/bg_gray_12"
|
||||
android:gravity="center"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:text="性格测试"
|
||||
android:textColor="#666666"
|
||||
android:textSize="11sp" />
|
||||
<TextView
|
||||
android:id="@+id/tagConstellation"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:background="@drawable/bg_gray_12"
|
||||
android:gravity="center"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:text="金牛座"
|
||||
android:textColor="#666666"
|
||||
android:textSize="11sp" />
|
||||
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
android:id="@+id/tagPersonality"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:background="@drawable/bg_gray_12"
|
||||
android:gravity="center"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:text="性格测试"
|
||||
android:textColor="#666666"
|
||||
android:textSize="11sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/quickActions"
|
||||
|
|
@ -352,169 +360,162 @@
|
|||
android:layout_marginTop="12dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tagRow">
|
||||
app:layout_constraintTop_toBottomOf="@id/tagScrollView">
|
||||
|
||||
<LinearLayout
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
android:scrollbars="none">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/action1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="72dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="44dp"
|
||||
android:layout_height="44dp"
|
||||
android:background="@drawable/bg_gray_12">
|
||||
<LinearLayout
|
||||
android:id="@+id/action1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="64dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/ic_crosshair_24" />
|
||||
<FrameLayout
|
||||
android:layout_width="44dp"
|
||||
android:layout_height="44dp"
|
||||
android:background="@drawable/bg_gray_12">
|
||||
|
||||
</FrameLayout>
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/ic_crosshair_24" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:orientation="vertical">
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="公园勋章"
|
||||
android:textColor="#111111"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold" />
|
||||
android:layout_marginStart="8dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="公园勋章"
|
||||
android:textColor="#111111"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:text="暂无勋章"
|
||||
android:textColor="#999999"
|
||||
android:textSize="11sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/action2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="64dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="44dp"
|
||||
android:layout_height="44dp"
|
||||
android:background="@drawable/bg_gray_12">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/ic_grid_24" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="暂无勋章"
|
||||
android:textColor="#999999"
|
||||
android:textSize="12sp" />
|
||||
android:layout_marginStart="8dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="观看历史"
|
||||
android:textColor="#111111"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:text="看过的作品"
|
||||
android:textColor="#999999"
|
||||
android:textSize="11sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/action3"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="64dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="44dp"
|
||||
android:layout_height="44dp"
|
||||
android:background="@drawable/bg_gray_12">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/ic_heart_24" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="我的挚友"
|
||||
android:textColor="#111111"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/friendsCount"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:text="0人"
|
||||
android:textColor="#999999"
|
||||
android:textSize="11sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/action2"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="72dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="44dp"
|
||||
android:layout_height="44dp"
|
||||
android:background="@drawable/bg_gray_12">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/ic_grid_24" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="观看历史"
|
||||
android:textColor="#111111"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="看过的作品等"
|
||||
android:textColor="#999999"
|
||||
android:textSize="12sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/action3"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="72dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="44dp"
|
||||
android:layout_height="44dp"
|
||||
android:background="@drawable/bg_gray_12">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/ic_heart_24" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="我的挚友"
|
||||
android:textColor="#111111"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="0人"
|
||||
android:textColor="#999999"
|
||||
android:textSize="12sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</HorizontalScrollView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
|
@ -847,18 +848,20 @@
|
|||
android:layout_gravity="bottom" />
|
||||
|
||||
<!-- 发布作品悬浮按钮 -->
|
||||
<ImageButton
|
||||
<TextView
|
||||
android:id="@+id/fabPublishWork"
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="56dp"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_gravity="bottom|start"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginBottom="100dp"
|
||||
android:background="@drawable/bg_fab_publish"
|
||||
android:contentDescription="发布作品"
|
||||
android:src="@drawable/ic_add_24_black"
|
||||
android:scaleType="center"
|
||||
android:elevation="6dp"
|
||||
android:stateListAnimator="@null" />
|
||||
android:gravity="center"
|
||||
android:text="+"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
android:elevation="6dp" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
|
|
|||
|
|
@ -261,20 +261,45 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/bio" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/addFriendButton"
|
||||
<!-- 按钮容器:关注和加好友 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/buttonsContainer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="@drawable/bg_purple_999"
|
||||
android:gravity="center"
|
||||
android:text="加好友"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintHeight_percent="0.055"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/space_addFriend_margin_top" />
|
||||
app:layout_constraintTop_toBottomOf="@id/space_addFriend_margin_top">
|
||||
|
||||
<!-- 关注按钮 -->
|
||||
<TextView
|
||||
android:id="@+id/followButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="44dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:background="@drawable/bg_follow_button"
|
||||
android:gravity="center"
|
||||
android:text="关注"
|
||||
android:textColor="#FF4757"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<!-- 加好友按钮 -->
|
||||
<TextView
|
||||
android:id="@+id/addFriendButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="44dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="6dp"
|
||||
android:background="@drawable/bg_purple_999"
|
||||
android:gravity="center"
|
||||
android:text="加好友"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<Space
|
||||
android:id="@+id/space_statsRow_margin_top"
|
||||
|
|
@ -282,7 +307,7 @@
|
|||
android:layout_height="0dp"
|
||||
app:layout_constraintHeight_percent="0.025"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/addFriendButton" />
|
||||
app:layout_constraintTop_toBottomOf="@id/buttonsContainer" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/statsRow"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#F7F2FF">
|
||||
android:background="@drawable/bg_fishpond_gradient">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/backButton"
|
||||
|
|
@ -14,7 +14,8 @@
|
|||
android:contentDescription="返回"
|
||||
android:src="@drawable/ic_arrow_back_24"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:tint="@color/white" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/titleText"
|
||||
|
|
@ -22,27 +23,146 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="语音匹配"
|
||||
android:textColor="#111111"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/backButton" />
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtitleText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:gravity="center"
|
||||
android:text="通过语音快速匹配志同道合的朋友"
|
||||
android:textColor="#666666"
|
||||
android:textColor="#CCFFFFFF"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/titleText" />
|
||||
|
||||
<!-- 匹配状态区域 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/matchingArea"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toTopOf="@id/actionArea"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/subtitleText">
|
||||
|
||||
<!-- 匹配动画圆圈 -->
|
||||
<FrameLayout
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="200dp">
|
||||
|
||||
<!-- 外圈动画 -->
|
||||
<View
|
||||
android:id="@+id/pulseRing1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/bg_pulse_ring" />
|
||||
|
||||
<View
|
||||
android:id="@+id/pulseRing2"
|
||||
android:layout_width="160dp"
|
||||
android:layout_height="160dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/bg_pulse_ring" />
|
||||
|
||||
<!-- 中心头像 -->
|
||||
<de.hdodenhof.circleimageview.CircleImageView
|
||||
android:id="@+id/userAvatar"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="120dp"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/wish_tree_checker_backup"
|
||||
app:civ_border_color="#FFFFFF"
|
||||
app:civ_border_width="3dp" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/statusText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:text="点击开始匹配"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/matchingHint"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="正在寻找附近的小伙伴..."
|
||||
android:textColor="#AAFFFFFF"
|
||||
android:textSize="14sp"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 操作区域 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/actionArea"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toTopOf="@id/bottomNavInclude"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<!-- 开始匹配按钮 -->
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnStartMatch"
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="56dp"
|
||||
android:text="开始匹配"
|
||||
android:textColor="@color/purple_500"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
app:backgroundTint="@color/white"
|
||||
app:cornerRadius="28dp"
|
||||
app:icon="@drawable/ic_voice_24"
|
||||
app:iconGravity="textStart"
|
||||
app:iconPadding="8dp"
|
||||
app:iconTint="@color/purple_500" />
|
||||
|
||||
<!-- 取消匹配按钮 -->
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnCancelMatch"
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="56dp"
|
||||
android:text="取消匹配"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="16sp"
|
||||
android:visibility="gone"
|
||||
app:backgroundTint="#33FFFFFF"
|
||||
app:cornerRadius="28dp"
|
||||
app:strokeColor="@color/white"
|
||||
app:strokeWidth="1dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/onlineCount"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="当前在线: 128人"
|
||||
android:textColor="#AAFFFFFF"
|
||||
android:textSize="12sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<include
|
||||
android:id="@+id/bottomNavInclude"
|
||||
layout="@layout/include_bottom_nav"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,110 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:background="@android:color/white"
|
||||
android:paddingBottom="24dp">
|
||||
|
||||
<!-- 顶部标题栏 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="12dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="我的频道"
|
||||
android:textSize="16sp"
|
||||
android:textColor="#333333"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="点击进入频道"
|
||||
android:textSize="12sp"
|
||||
android:textColor="#999999"
|
||||
android:layout_marginEnd="16dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/btnEditChannels"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="进入编辑"
|
||||
android:textSize="13sp"
|
||||
android:textColor="#333333"
|
||||
android:paddingHorizontal="12dp"
|
||||
android:paddingVertical="6dp"
|
||||
android:background="@drawable/bg_channel_edit_button" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/btnCloseChannelManager"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:src="@drawable/ic_expand_more_24"
|
||||
android:rotation="180"
|
||||
android:contentDescription="收起"
|
||||
app:tint="#999999" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 我的频道列表 -->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/myChannelsRecycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="12dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:clipToPadding="false" />
|
||||
|
||||
<!-- 分隔线 -->
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:background="#F0F0F0" />
|
||||
|
||||
<!-- 推荐频道标题 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="12dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="推荐频道"
|
||||
android:textSize="16sp"
|
||||
android:textColor="#333333"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="点击添加频道"
|
||||
android:textSize="12sp"
|
||||
android:textColor="#999999" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 推荐频道列表 -->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recommendChannelsRecycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="12dp"
|
||||
android:clipToPadding="false" />
|
||||
|
||||
</LinearLayout>
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="24dp"
|
||||
app:cardBackgroundColor="#FFFFFF"
|
||||
app:cardCornerRadius="20dp"
|
||||
app:cardElevation="8dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<!-- 标题栏 -->
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:text="全部板块"
|
||||
android:textColor="#333333"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/dialogCloseBtn"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:padding="4dp"
|
||||
android:src="@drawable/ic_close_24"
|
||||
android:contentDescription="关闭" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<!-- 板块列表 -->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/dialogCategoryRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxHeight="400dp"
|
||||
android:clipToPadding="false"
|
||||
android:overScrollMode="never" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
48
android-app/app/src/main/res/layout/item_category_card.xml
Normal file
48
android-app/app/src/main/res/layout/item_category_card.xml
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="6dp"
|
||||
app:cardBackgroundColor="#CCF7F2FF"
|
||||
app:cardCornerRadius="16dp"
|
||||
app:cardElevation="0dp"
|
||||
app:strokeColor="#33FFFFFF"
|
||||
app:strokeWidth="1dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="88dp"
|
||||
android:padding="8dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/categoryIcon"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:background="@drawable/bg_diamond_rounded"
|
||||
android:padding="10dp"
|
||||
android:src="@drawable/ic_grid3x3_24"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/categoryName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:maxLines="1"
|
||||
android:text="板块名称"
|
||||
android:textColor="#333333"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/categoryIcon" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
54
android-app/app/src/main/res/layout/item_channel_tag.xml
Normal file
54
android-app/app/src/main/res/layout/item_channel_tag.xml
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="4dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/channelName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="8dp"
|
||||
android:text="频道名"
|
||||
android:textSize="14sp"
|
||||
android:textColor="#333333"
|
||||
android:background="@drawable/bg_channel_tag_normal" />
|
||||
|
||||
<!-- 固定标识(前4个频道显示) -->
|
||||
<ImageView
|
||||
android:id="@+id/fixedIcon"
|
||||
android:layout_width="14dp"
|
||||
android:layout_height="14dp"
|
||||
android:layout_gravity="top|end"
|
||||
android:layout_marginTop="-4dp"
|
||||
android:layout_marginEnd="-4dp"
|
||||
android:src="@drawable/ic_lock_12"
|
||||
android:visibility="gone"
|
||||
app:tint="#999999" />
|
||||
|
||||
<!-- 删除按钮(编辑模式显示) -->
|
||||
<ImageView
|
||||
android:id="@+id/deleteIcon"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_gravity="top|end"
|
||||
android:layout_marginTop="-6dp"
|
||||
android:layout_marginEnd="-6dp"
|
||||
android:src="@drawable/ic_close_circle_16"
|
||||
android:visibility="gone" />
|
||||
|
||||
<!-- 添加按钮(推荐频道显示) -->
|
||||
<ImageView
|
||||
android:id="@+id/addIcon"
|
||||
android:layout_width="14dp"
|
||||
android:layout_height="14dp"
|
||||
android:layout_gravity="top|start"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:src="@drawable/ic_add_12"
|
||||
android:visibility="gone"
|
||||
app:tint="#999999" />
|
||||
|
||||
</FrameLayout>
|
||||
|
|
@ -54,6 +54,15 @@
|
|||
android:layout_marginTop="8dp"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<!-- 图片容器 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/images_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
|
|||
92
android-app/app/src/main/res/layout/item_recommend_user.xml
Normal file
92
android-app/app/src/main/res/layout/item_recommend_user.xml
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 推荐用户列表项 -->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="12dp"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
|
||||
<!-- 头像 -->
|
||||
<ImageView
|
||||
android:id="@+id/userAvatar"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:background="@drawable/bg_avatar_circle"
|
||||
android:clipToOutline="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/ic_account_circle_24" />
|
||||
|
||||
<!-- 用户信息 -->
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="12dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/userName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="用户名"
|
||||
android:textSize="15sp"
|
||||
android:textColor="#333333"
|
||||
android:textStyle="bold"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end" />
|
||||
|
||||
<!-- 认证标识 -->
|
||||
<ImageView
|
||||
android:id="@+id/verifiedIcon"
|
||||
android:layout_width="14dp"
|
||||
android:layout_height="14dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:src="@drawable/ic_verified"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/userDesc"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:text="用户简介"
|
||||
android:textSize="12sp"
|
||||
android:textColor="#999999"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 关注按钮 -->
|
||||
<TextView
|
||||
android:id="@+id/btnFollow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="32dp"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:gravity="center"
|
||||
android:text="关注"
|
||||
android:textSize="13sp"
|
||||
android:textColor="#FF4757"
|
||||
android:background="@drawable/bg_follow_button" />
|
||||
|
||||
<!-- 关闭按钮 -->
|
||||
<ImageView
|
||||
android:id="@+id/btnRemove"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:padding="4dp"
|
||||
android:src="@drawable/ic_close_24"
|
||||
app:tint="#CCCCCC" />
|
||||
|
||||
</LinearLayout>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user