目录
一、项目演示
基于 Android Studio仿故宫博物院(馆)App--原创
二、开发环境
三、项目简介
该项目隶属于编程乐学团队原创 Android Studio 项目,其中页面素材和布局来自于故宫博物院小程序和故宫展览APP。
该项目中文注释高达95%,如下图所示部分注释展示:
四、项目详情
1.启动页
这段代码是一个简单的Android应用程序启动活动(Activity),具体功能如下:
1. **延迟进入登录页面:**
- 在 `onCreate()` 方法中,使用 `Handler` 和 `Runnable` 实现了一个延迟执行的功能,延迟时间为3秒。
- `runnable` 对象的 `run()` 方法调用了 `tomainActive()` 方法,在延迟结束后启动 `LoginRegisterActivity` 并关闭当前的 `StartActivity`。
2. **计时器功能:**
- 定义了一个内部类 `TimeCount`,继承自 `CountDownTimer`,用于执行一个四秒的倒计时操作,每隔一秒触发一次。
- `onFinish()` 方法中,倒计时结束后移除了 `handler` 中的 `runnable` 对象,确保不会在倒计时结束后再次跳转到登录页面。
3. **Activity 生命周期方法:**
- `onCreate()` 方法中,设置了布局文件 `activity_start.xml` 作为界面显示内容,并启动了延迟执行和计时器。
- `onDestroy()` 方法中未显示重写,但在 `toMainActive()` 方法中的 `finish()` 方法确保了在跳转完成后关闭当前 Activity。
总体来说,该代码实现了在应用启动后延迟3秒进入登录页面,并在倒计时4秒后取消延迟,确保用户在启动页面停留不超过指定时间后自动跳转到登录页面。
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/start"
tools:context=".Activity.StartActivity">
<Button
android:id="@+id/btn_skip"
android:layout_width="70dp"
android:layout_height="30dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="16dp"
android:background="@drawable/start_bg"
android:text="跳过"
android:textColor="#fff"
android:textSize="12sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
2.登陆、注册、忘记密码
主要实现了登陆注册找回密码功能,全部使用到SQLite数据库进行操作
// 定义一个私有方法login,用于处理用户登录逻辑
private void login() {
// 为btnLogin按钮设置点击事件监听器
btnLogin.setOnClickListener(v -> {
// 从etMail(可能是一个EditText控件)中获取文本内容,并转换为字符串,赋值给mail变量
String mail = etMail.getText().toString();
// 从etPassword(可能是一个EditText控件)中获取文本内容,并转换为字符串,赋值给password变量
String password = etPassword.getText().toString();
// 判断mail或password是否为空
if (mail.isEmpty() || password.isEmpty()) {
// 如果其中之一为空,则显示一个Toast提示信息“请确保所有内容不为空!”
Toast.makeText(this, "请确保所有内容不为空!", Toast.LENGTH_SHORT).show();
// 提前返回,不再执行后续代码
return;
}
// 检查RadioButton(rb)是否被选中
boolean isRbChecked = rb.isChecked();
// 判断RadioButton是否未被选中
if (!isRbChecked) {
// 如果未被选中,则显示一个Toast提示信息“请勾选同意注册协议!”
Toast.makeText(this, "请勾选同意注册协议!", Toast.LENGTH_SHORT).show();
// 提前返回,不再执行后续代码
return;
}
// 调用userHelper的validateUser方法验证用户的邮箱和密码是否正确,结果赋值给b变量
boolean b = userHelper.validateUser(mail, password);
// 判断用户验证是否成功
if (b) {
// 如果验证成功,则显示一个Toast提示信息“登录成功!”
Toast.makeText(this, "登录成功!", Toast.LENGTH_SHORT).show();
// 获取SharedPreferences对象,名称为"User",模式为私有模式
SharedPreferences sharedPreferences = getSharedPreferences("User", Context.MODE_PRIVATE);
// 获取SharedPreferences.Editor对象,用于编辑SharedPreferences数据
SharedPreferences.Editor editor = sharedPreferences.edit();
// 将邮箱和密码存储到SharedPreferences中
editor.putString("mail", mail);
editor.putString("password", password);
// 提交修改
editor.apply();
// 结束当前Activity
finish();
} else {
// 如果验证失败,则显示一个Toast提示信息“登录失败,请重试!”
Toast.makeText(this, "登录失败,请重试!", Toast.LENGTH_SHORT).show();
// 清空etPassword和etMail的文本内容
etPassword.setText("");
etMail.setText("");
}
});
}
// 定义一个私有方法register,用于处理注册页面的跳转
private void register() {
// 为tvRegister(可能是一个TextView控件)设置点击事件监听器
tvRegister.setOnClickListener(v -> {
// 创建一个Intent,用于从当前Activity跳转到RegisterActivity
Intent intent = new Intent(LoginActivity.this, RegisterActivity.class);
// 启动RegisterActivity
startActivity(intent);
});
}
// 定义一个私有方法back,用于处理返回操作
private void back() {
// 为imgBack(可能是一个ImageView控件)设置点击事件监听器
imgBack.setOnClickListener(v -> {
// 结束当前Activity
finish();
});
}
// 定义一个私有方法forgot,用于处理忘记密码页面的跳转
private void forgot() {
// 为tvForget(可能是一个TextView控件)设置点击事件监听器
tvForget.setOnClickListener(v -> {
// 创建一个Intent,用于从当前Activity跳转到ForgotPasswordActivity
Intent intent = new Intent(LoginActivity.this, ForgotPasswordActivity.class);
// 启动ForgotPasswordActivity
startActivity(intent);
});
}
`LoginActivity` 是一个 Android 应用中的登录界面,提供用户登录、注册和忘记密码功能。主要功能包括:
- **登录处理**:检查用户输入的邮箱和密码是否为空,验证是否勾选了协议,使用 `UserHelper` 验证用户信息,验证成功后保存信息到 `SharedPreferences`。
- **注册和忘记密码跳转**:点击相应的按钮跳转到注册页面或忘记密码页面。
- **返回操作**:点击返回按钮结束当前页面。
该活动在 `onCreate` 方法中初始化视图组件,并设置了相应的点击事件处理。
private void register() {
// 设置注册按钮的点击事件监听器
btnRegister.setOnClickListener(v -> {
// 获取用户输入的邮箱、用户名和密码
String mail = etMail.getText().toString();
String name = etName.getText().toString();
String password = etPassword.getText().toString();
// 检查邮箱、用户名和密码是否为空
if (mail.isEmpty() || name.isEmpty() || password.isEmpty()) {
// 如果有任何字段为空,显示提示消息
Toast.makeText(this, "请确保所有内容不为空!", Toast.LENGTH_SHORT).show();
return; // 退出方法
}
// 检查密码长度是否在6到16个字符之间
if (password.length() < 6 || password.length() > 16) {
// 如果密码长度不符合要求,显示提示消息
Toast.makeText(this, "密码长度应为6到16个字符!", Toast.LENGTH_SHORT).show();
return; // 退出方法
}
// 检查同意注册协议的 RadioButton 是否被选中
boolean isRbChecked = rb.isChecked();
if (!isRbChecked) {
// 如果没有勾选同意注册协议,显示提示消息
Toast.makeText(this, "请勾选同意注册协议!", Toast.LENGTH_SHORT).show();
return; // 退出方法
}
// 调用 userHelper 的 addUser 方法注册用户,并获取返回结果
boolean b = userHelper.addUser(mail, name, password);
if (b) {
// 如果注册成功,显示成功消息并关闭当前活动
Toast.makeText(this, "注册成功!", Toast.LENGTH_SHORT).show();
finish(); // 关闭当前活动
} else {
// 如果注册失败,显示失败消息,清空输入框内容
Toast.makeText(this, "注册失败,请重试!", Toast.LENGTH_SHORT).show();
etPassword.setText(""); // 清空密码输入框
etMail.setText(""); // 清空邮箱输入框
etName.setText(""); // 清空用户名输入框
}
});
}
`register()` 方法用于处理用户注册逻辑。其功能包括:
1. **设置注册按钮点击事件监听器**:当点击注册按钮时,执行以下操作。
2. **获取用户输入**:从输入框中获取用户的邮箱、用户名和密码。
3. **输入验证**:
- 检查邮箱、用户名和密码是否为空;若为空,显示提示消息。
- 验证密码长度是否在 6 到 16 个字符之间;若不符合要求,显示提示消息。
4. **协议勾选验证**:检查用户是否勾选了同意注册协议的 `RadioButton`;若未勾选,显示提示消息。
5. **调用注册方法**:
- 使用 `UserHelper` 的 `addUser()` 方法注册用户,并根据返回结果显示相应的消息。
- 注册成功则显示成功消息并关闭当前活动。
- 注册失败则显示失败消息,并清空输入框内容。
这个方法确保用户在注册前填写所有必要的信息,并符合基本的验证要求。
3.用户首页、购票约展览、地图导览
这个项目是一个预约系统的功能实现,主要包括以下几个功能点:
1. **预约按钮点击事件**:
- 设置预约按钮的点击事件监听器。
2. **日期选择检查**:
- 获取用户选择的参观日期,并检查是否选择了有效的日期。如果没有选择,弹出提示信息。
3. **时间选择检查**:
- 获取用户选择的参观时间,并检查是否选择了有效的时间。如果没有选择,弹出提示信息。
4. **订单ID生成**:
- 生成一个唯一的订单ID,格式为“D”加上当前时间的年月日时分秒。
5. **订单时间记录**:
- 获取当前时间,并格式化为“yyyy-MM-dd HH:mm:ss”的字符串,记录为订单时间。
6. **票务信息初始化**:
- 初始化各种票务信息,包括票种、票数和总价,用于后续的数据库插入操作。
7. **票务信息处理**:
- 遍历适配器中的数据,检查票数,计算每种票的总价,并将相关信息存入对应的 `StringBuilder` 中。
8. **数据库插入操作**:
- 如果有选择票种,将订单数据(包括用户邮箱、订单ID、订单时间、参观日期、参观时间以及各票种的票数和总价)插入到数据库中,并设置订单状态为“待支付”。
9. **预约成功提示及页面跳转**:
- 弹出预约成功的提示信息,并跳转到订单页面。
10. **票种选择检查**:
- 如果没有选择任何票种,弹出提示信息提醒用户选择票种。
private void reserve() {
// 设置预约按钮的点击事件
btnReserve.setOnClickListener(v -> {
// 获取选择的参观日期
String visitDate = selectedDateText.getText().toString();
// 检查参观日期是否被选择
if (visitDate.equals("点击选择参观日期")) {
Toast.makeText(this, "请选择参观日期!", Toast.LENGTH_SHORT).show();
return; // 退出方法
}
// 获取选中的参观时间单选按钮的 ID
int selectedId = rb.getCheckedRadioButtonId();
// 根据 ID 查找选中的单选按钮
RadioButton selectedRadioButton = findViewById(selectedId);
// 检查是否选择了参观时间
if (selectedRadioButton == null) {
Toast.makeText(this, "请选择参观时间", Toast.LENGTH_SHORT).show();
return; // 退出方法
}
// 获取选中的参观时间
String visitTime = selectedRadioButton.getText().toString();
// 生成订单 ID
String orderId = "D" + new SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()) {{
setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); // 设置时区为上海
}}.format(new Date());
// 获取当前时间作为订单时间
String orderTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) {{
setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); // 设置时区为上海
}}.format(new Date());
// 初始化票务信息
boolean hasTickets = false;
StringBuilder ticketType1 = new StringBuilder();
StringBuilder ticketCount1 = new StringBuilder();
StringBuilder totalPrice1 = new StringBuilder();
StringBuilder ticketType2 = new StringBuilder();
StringBuilder ticketCount2 = new StringBuilder();
StringBuilder totalPrice2 = new StringBuilder();
StringBuilder ticketType3 = new StringBuilder();
StringBuilder ticketCount3 = new StringBuilder();
StringBuilder totalPrice3 = new StringBuilder();
StringBuilder ticketType4 = new StringBuilder();
StringBuilder ticketCount4 = new StringBuilder();
StringBuilder totalPrice4 = new StringBuilder();
int count = 0; // 票种计数器
// 遍历适配器中的数据
for (Map<String, String> item : adapter.getData()) {
String ticketCountStr = item.get("ticketCount"); // 获取票数
int ticketCount = Integer.parseInt(ticketCountStr); // 转换为整数
// 检查票数是否大于零
if (ticketCount > 0) {
hasTickets = true; // 标记有票
String ticketType = item.get("ticketLabel"); // 获取票种
String totalPrice = calculateTotalPrice(ticketType, ticketCount); // 计算总价
count++;
// 根据票种计数器,将票务信息保存到相应的 StringBuilder 中
if (count == 1) {
ticketType1.append(ticketType);
ticketCount1.append(ticketCount);
totalPrice1.append(totalPrice);
} else if (count == 2) {
ticketType2.append(ticketType);
ticketCount2.append(ticketCount);
totalPrice2.append(totalPrice);
} else if (count == 3) {
ticketType3.append(ticketType);
ticketCount3.append(ticketCount);
totalPrice3.append(totalPrice);
} else if (count == 4) {
ticketType4.append(ticketType);
ticketCount4.append(ticketCount);
totalPrice4.append(totalPrice);
}
}
}
// 如果有票
if (hasTickets) {
// 将订单数据插入到数据库
buyHelper.insertOrderData(
mail,
orderId,
orderTime,
visitDate,
visitTime,
ticketType1.toString(),
ticketCount1.toString(),
totalPrice1.toString(),
ticketType2.toString(),
ticketCount2.toString(),
totalPrice2.toString(),
ticketType3.toString(),
ticketCount3.toString(),
totalPrice3.toString(),
ticketType4.toString(),
ticketCount4.toString(),
totalPrice4.toString(),
"待支付" // 设置订单状态为待支付
);
// 预约成功提示并跳转到订单页面
Intent intent = new Intent(BuyActivity.this, OrderActivity.class);
Toast.makeText(this, "预约成功!", Toast.LENGTH_SHORT).show();
startActivity(intent);
} else {
// 没有选择票种提示
Toast.makeText(this, "请选择需要的票种!", Toast.LENGTH_SHORT).show();
}
});
}
4.游故宫、故宫建筑、故宫建筑详情页、参观须知
该页面可以实现跳转购票约展览、故宫建筑、地图导览页。
1. **加载 JSON 数据**:调用 `loadJSONFromAsset()` 方法从资产中加载 JSON 数据,并将其存储在 `json` 变量中。如果 JSON 数据不为空,则继续处理。
2. **解析 JSON 数据**:
- 创建 `Gson` 实例用于解析 JSON 数据。
- 使用 `TypeToken` 定义 JSON 数据的类型(这里是 `List<UnitBean>`)。`TypeToken` 是 Gson 库中用于处理泛型的工具。
- 调用 `Gson` 的 `fromJson` 方法,将 JSON 数据解析成 `unitList`(`List<UnitBean>` 类型)。
3. **配置 RecyclerView**:
- 设置 `RecyclerView` 的布局管理器为 `GridLayoutManager`,并指定列数为 2,意味着 `RecyclerView` 将以网格形式显示数据,每行有 2 列。
- 创建 `UnitAdapter` 实例并将其与 `RecyclerView` 关联。`UnitAdapter` 负责将 `unitList` 中的数据绑定到 `RecyclerView` 的视图上。
collect()
方法处理收藏和取消收藏逻辑,包括对用户进行登录提示。back()
方法处理返回按钮的点击事件。show()
方法负责显示和更新界面内容,包括加载图片和设置文本
private void show() {
// 从资产中加载 JSON 数据
String json = loadJSONFromAsset();
if (json != null) {
// 创建 Gson 实例用于解析 JSON
Gson gson = new Gson();
// 定义用于解析的类型
TypeToken<List<UnitBean>> token = new TypeToken<List<UnitBean>>() {
};
// 解析 JSON 数据并保存到 unitList
unitList = gson.fromJson(json, token.getType());
// 设置 RecyclerView 的布局管理器为 GridLayoutManager,设置列数为 2
rv.setLayoutManager(new GridLayoutManager(this, 2)); // 2 列布局
// 初始化适配器并与 RecyclerView 关联
adapter = new UnitAdapter(this, unitList);
rv.setAdapter(adapter);
}
}
5.看文物、数字文物库、文物详情页
1. **加载数据**:
- 从应用的资产目录中读取 JSON 格式的数据文件。
2. **解析数据**:
- 使用 Gson 库将 JSON 数据解析为 `List<FigureBean>` 对象,以便于在应用中使用。
3. **显示数据**:
- 使用 `RecyclerView` 组件显示解析后的数据。
- `RecyclerView` 的布局管理器设置为 `GridLayoutManager`,以 2 列网格的方式展示数据项。
- 使用自定义适配器 `FigureAdapter` 将数据绑定到 `RecyclerView` 的视图中,以便在界面上展示数据列表。
// 初始化显示数据的方法
private void show() {
// 从资产目录中加载 JSON 数据
String json = loadJSONFromAsset();
// 如果 JSON 数据不为空
if (json != null) {
// 创建 Gson 对象用于解析 JSON 数据
Gson gson = new Gson();
// 定义数据类型
TypeToken<List<FigureBean>> token = new TypeToken<List<FigureBean>>() {
};
// 将 JSON 数据解析为 List<FigureBean> 对象,并赋值给 figureList
figureList = gson.fromJson(json, token.getType()); // 保存原始数据到 figureList
// 设置 RecyclerView 的布局管理器为 GridLayoutManager,显示为 2 列布局
rv.setLayoutManager(new GridLayoutManager(this, 2)); // 2 列布局
// 初始化适配器,并将 figureList 传递给适配器
adapter = new FigureAdapter(this, figureList);
// 设置 RecyclerView 的适配器
rv.setAdapter(adapter);
}
}
1. **设置收藏按钮的点击事件** (`collect` 方法):
- **检查用户登录状态**:判断用户的 `mail` 是否为空,确保用户已登录。
- **处理收藏状态**:
- 如果当前状态是“已收藏”,则执行取消收藏操作:
- 调用 `collectHelper` 的 `deleteData` 方法删除收藏记录。
- 根据删除结果,显示相应的提示信息,并更新按钮的文本和图标。
- 如果当前状态是“收藏”,则执行收藏操作:
- 调用 `collectHelper` 的 `insertData` 方法插入收藏记录。
- 根据插入结果,显示相应的提示信息,并更新按钮的文本和图标。
2. **展示收藏状态和相关信息** (`show` 方法):
- **获取并显示收藏状态**:
- 从 `collectHelper` 获取收藏记录,检查收藏状态是否为“已收藏”。
- 根据收藏状态更新 `tvCollect` 的文本和 `imgCollect` 的图标。
- **加载和显示图片及相关信息**:
- 使用 Glide 加载 `imgPath` 中的图片并显示在 `img` 组件上。
- 设置 `tvName`、`tvNum`、`tvCategory` 和 `tvEra` 的文本内容为相应的数据。
// 定义一个方法 collect(),用于设置收藏按钮的点击事件
private void collect() {
// 设置 llCollect 的点击事件监听器
llCollect.setOnClickListener(v -> {
// 检查 mail 是否为空,表示用户是否登录
if (mail.isEmpty()) {
// 如果 mail 为空,弹出提示框提醒用户先登录
Toast.makeText(this, "请先进行登录!", Toast.LENGTH_SHORT).show();
} else {
// 获取 tvCollect 上的文本内容
String s = tvCollect.getText().toString();
// 如果文本内容是 "已收藏",则执行取消收藏的操作
if (s.equals("已收藏")) {
// 调用 collectHelper 的 deleteData 方法,删除收藏记录
boolean b = collectHelper.deleteData(mail, name);
// 如果删除成功,弹出提示框并更新 UI
if (b) {
Toast.makeText(this, "已取消收藏!", Toast.LENGTH_SHORT).show();
// 更新 tvCollect 的文本为 "收藏"
tvCollect.setText("收藏");
// 更新 imgCollect 的图片资源为收藏的图标
imgCollect.setImageResource(R.drawable.collect);
} else {
// 如果删除失败,弹出提示框
Toast.makeText(this, "取消收藏失败!", Toast.LENGTH_SHORT).show();
}
} else {
// 如果文本内容是 "收藏",则执行收藏的操作
// 调用 collectHelper 的 insertData 方法,插入收藏记录
boolean collect = collectHelper.insertData(mail, "文物", "已收藏", imgPath, name, num, era, category, "", " ");
// 如果插入成功,弹出提示框并更新 UI
if (collect) {
Toast.makeText(this, "收藏成功!", Toast.LENGTH_SHORT).show();
// 更新 tvCollect 的文本为 "已收藏"
tvCollect.setText("已收藏");
// 更新 imgCollect 的图片资源为已收藏的图标
imgCollect.setImageResource(R.drawable.collectd);
} else {
// 如果插入失败,弹出提示框
Toast.makeText(this, "收藏失败!", Toast.LENGTH_SHORT).show();
}
}
}
});
}
// 定义一个方法 show(),用于展示收藏的状态和相关信息
private void show() {
// 从 collectHelper 获取收藏记录
CollectBean collectBean = collectHelper.getCollect(mail, name);
// 如果获取到收藏记录
if (collectBean != null) {
// 判断收藏记录的状态是否为 "已收藏"
if (collectBean.getCollect().equals("已收藏")) {
// 如果是 "已收藏",更新 UI 显示已收藏状态
tvCollect.setText("已收藏");
imgCollect.setImageResource(R.drawable.collectd);
} else {
// 如果不是 "已收藏",更新 UI 显示未收藏状态
tvCollect.setText("收藏");
imgCollect.setImageResource(R.drawable.collect);
}
} else {
// 如果没有收藏记录,设置默认的未收藏状态
tvCollect.setText("收藏");
imgCollect.setImageResource(R.drawable.collect);
}
// 使用 Glide 加载 imgPath 中的图片并设置到 img 组件上
Glide.with(this)
.load(imgPath)
.into(img);
// 设置 tvName、tvNum、tvCategory 和 tvEra 的文本内容
tvName.setText(name);
tvNum.setText(num);
tvCategory.setText(category);
tvEra.setText(era);
}
6.我的页面、我的收藏、我的订单
1. **TabLayout 设置**:
- 添加了两个 Tab(选项卡),分别标记为“建筑”和“文物”。
- 为 TabLayout 设置了选项卡选择监听器,根据用户选择的 Tab 执行不同的方法。
2. **数据加载方法**:
- `JZData()` 方法:加载与“建筑”相关的数据,创建并设置相应的适配器(`CollectAdapter`),然后调用 `updateUI()` 方法更新界面。
- `WWData()` 方法:加载与“文物”相关的数据,创建并设置相应的适配器(`FigureAdapter`),然后调用 `updateUI()` 方法更新界面。
3. **UI 更新方法**:
- `updateUI()` 方法:根据数据列表是否为空来决定如何更新界面。如果数据列表为空,显示提示消息并显示占位视图;如果数据列表不为空,隐藏占位视图并设置 `RecyclerView` 的布局管理器和适配器,显示数据列表。
private void setupTabLayout() {
// 添加 Tab
tablayout.addTab(tablayout.newTab().setText("建筑")); // 添加“建筑”Tab
tablayout.addTab(tablayout.newTab().setText("文物")); // 添加“文物”Tab
// 设置 Tab 选择监听器
tablayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
// 获取被选中的 Tab 的文本
String tabText = tab.getText().toString();
// 判断选中的 Tab 文本,执行相应方法
if (tabText.equals("建筑")) {
JZData(); // 如果是“建筑”Tab,调用 JZData 方法
} else {
WWData(); // 否则,调用 WWData 方法
}
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
// 不需要处理此回调,暂时留空
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
// 不需要处理此回调,暂时留空
}
});
}
private void JZData() {
// 获取“建筑”相关的数据
List<CollectBean> collectBeans = collectHelper.getJZ(mail, "建筑");
// 创建 CollectAdapter 适配器
CollectAdapter collectAdapter = new CollectAdapter(this, collectBeans);
// 更新 UI
updateUI(collectBeans, collectAdapter, "您还没有收藏!");
}
private void WWData() {
// 获取“文物”相关的数据
List<FigureBean> figureBeans = collectHelper.getWW(mail, "文物");
// 创建 FigureAdapter 适配器
FigureAdapter figureAdapter = new FigureAdapter(this, figureBeans);
// 更新 UI
updateUI(figureBeans, figureAdapter, "您还没有收藏!");
}
private <T> void updateUI(List<T> dataList, RecyclerView.Adapter<?> adapter, String emptyMessage) {
// 判断数据列表是否为空
if (dataList.isEmpty()) {
// 显示提示消息
Toast.makeText(this, emptyMessage, Toast.LENGTH_SHORT).show();
// 隐藏 RecyclerView
rv.setVisibility(View.GONE);
// 显示占位视图
tvNoData.setVisibility(View.VISIBLE);
} else {
// 显示 RecyclerView
rv.setVisibility(View.VISIBLE);
// 隐藏占位视图
tvNoData.setVisibility(View.GONE);
// 设置 RecyclerView 的布局管理器为 GridLayoutManager,横排两个显示
rv.setLayoutManager(new GridLayoutManager(this, 2));
// 设置 RecyclerView 的适配器
rv.setAdapter(adapter);
}
}
1. **TabLayout 设置**:
- 添加了三个选项卡:`"全部"`、`"待支付"` 和 `"已支付"`。
2. **选项卡选择监听器**:
- 当用户选择不同的选项卡时,触发 `onTabSelected` 方法,执行相应的操作:
- 选择 `"全部"` 选项卡时,调用 `loadData()` 方法来加载所有订单数据。
- 选择 `"待支付"` 或 `"已支付"` 选项卡时,根据选项卡的文本(状态)从数据库中获取相应状态的订单列表,并将这些订单数据反转,使得最新的订单显示在列表顶部。
3. **订单数据展示**:
- 为 `"待支付"` 和 `"已支付"` 选项卡加载的数据创建 `OrderAdapter` 实例,并将订单数据传递给适配器,然后设置给 `ListView` 控件,展示订单列表。
**总结功能点**:
- 支持在 TabLayout 中切换不同的订单状态选项卡。
- 根据选中的选项卡加载不同的数据,`"全部"` 选项卡加载所有订单数据,其余选项卡根据订单状态筛选数据。
- 在 `ListView` 中展示订单列表,并确保最新的订单显示在顶部。
private void setupTabLayout() {
// 向 TabLayout 添加 "全部" 选项卡
tabLayout.addTab(tabLayout.newTab().setText("全部"));
// 向 TabLayout 添加 "待支付" 选项卡
tabLayout.addTab(tabLayout.newTab().setText("待支付"));
// 向 TabLayout 添加 "已支付" 选项卡
tabLayout.addTab(tabLayout.newTab().setText("已支付"));
// 为 TabLayout 添加选项卡选中监听器
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
// 获取被选中的选项卡的文本
String tabText = tab.getText().toString();
// 根据选项卡的文本决定加载哪种数据
if (tabText.equals("全部")) {
// 如果选中的是 "全部",加载所有订单数据
loadData();
} else {
// 如果选中的是 "待支付" 或 "已支付",根据状态获取订单列表
List<OrderBean> orders = dbHelper.getOrdersByStatusAndMail(tabText, mail);
// 将订单列表反转,使得最新的订单显示在列表顶部
Collections.reverse(orders);
// 创建 OrderAdapter 实例,并将订单数据传递给适配器
OrderAdapter adapter = new OrderAdapter(OrderActivity.this, orders);
// 将适配器设置给 ListView 控件
lv.setAdapter(adapter);
}
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
// 选项卡未被选中时的操作,可以为空
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
// 选项卡被重新选中时的操作,可以为空
}
});
}
7.账户设置、修改密码、修改用户名、退出
这个项目主要实现了用户信息更新功能,包含密码和用户名的修改。具体功能点总结如下:
1. **获取用户信息**:
- 从 `SharedPreferences` 中获取用户的邮件地址,并将其设置到 `EditText` 控件 `etMail` 中。
2. **根据状态设置视图和按钮事件**:
- 从 `Intent` 中获取 `state` 参数,根据其值("1" 或 "2")来确定要执行的操作:
- **状态为 "1"**:调用 `setupPasswordUpdate()` 方法设置密码更新界面的视图和点击事件。
- **状态为 "2"**:调用 `setupUsernameUpdate()` 方法设置用户名更新界面的视图和点击事件。
3. **密码更新功能** (`setupPasswordUpdate()` 方法):
- 设置界面控件的提示文本,提示用户输入原密码和新密码。
- 设置修改按钮的点击事件,执行以下操作:
- 检查新密码长度是否在 6 到 16 个字符之间。
- 验证原密码是否正确。如果正确,则尝试更新密码。
- 更新密码成功后,跳转到主界面,并显示成功消息;失败时,显示失败消息。
4. **用户名更新功能** (`setupUsernameUpdate()` 方法):
- 设置界面控件的提示文本,提示用户输入原用户名和新用户名。
- 设置修改按钮的点击事件,执行以下操作:
- 验证原用户名是否正确。如果正确,则尝试更新用户名。
- 更新用户名成功后,清空输入框并显示成功消息;失败时,显示失败消息。
**总结**:
- 实现了基于用户状态的动态界面设置,支持密码和用户名的更新。
- 提供了用户输入验证和错误处理机制,以确保数据的正确性和用户体验。
// 显示方法,根据不同的状态设置视图和按钮点击事件
private void show() {
// 从SharedPreferences中获取用户的邮件地址
SharedPreferences sharedPreferences = getSharedPreferences("User", Context.MODE_PRIVATE);
mail = sharedPreferences.getString("mail", "");
// 将获取到的邮件地址设置到etMail(一个EditText控件)中
etMail.setText(mail);
// 获取Intent传递过来的“state”参数
String state = getIntent().getStringExtra("state");
// 根据“state”的值设置不同的视图和点击事件
if (state.equals("1")) {
setupPasswordUpdate(); // 如果state为"1",调用设置密码更新的函数
} else if (state.equals("2")) {
setupUsernameUpdate(); // 如果state为"2",调用设置用户名更新的函数
}
}
// 设置密码更新的相关视图和点击事件
private void setupPasswordUpdate() {
// 设置原密码和新密码的提示文本
tvOld.setText("原密码");
etOld.setHint("请输入原密码");
tvNew.setText("新密码");
etNew.setHint("请输入新密码(6-16个字符)");
// 设置修改按钮的点击事件
btnModify.setOnClickListener(v -> {
String oldPassword = etOld.getText().toString();
String newPassword = etNew.getText().toString();
// 检查新密码的长度是否在6到16个字符之间
if (newPassword.length() < 6 || newPassword.length() > 16) {
Toast.makeText(this, "密码长度应为6到16个字符!", Toast.LENGTH_SHORT).show();
return;
}
// 验证原密码是否正确
if (userHelper.isUserValid(mail, oldPassword)) {
// 更新密码
if (userHelper.updateUserPasswordByEmail(mail, newPassword)) {
showToast("更新成功");
navigateToMainActivity("3"); // 密码更新成功后,跳转到主界面
} else {
showToast("更新失败");
}
} else {
showToast("原密码错误!");
}
});
}
// 设置用户名更新的相关视图和点击事件
private void setupUsernameUpdate() {
// 设置原用户名和新用户名的提示文本
tvOld.setText("原用户名");
etOld.setHint("请输入原用户名");
tvNew.setText("新用户名");
etNew.setHint("请输入新用户名");
// 设置修改按钮的点击事件
btnModify.setOnClickListener(v -> {
String oldUsername = etOld.getText().toString();
String newUsername = etNew.getText().toString();
// 验证原用户名是否正确
if (userHelper.isUserValidByEmailAndUsername(mail, oldUsername)) {
// 更新用户名
if (userHelper.updateUsernameByEmail(mail, newUsername)) {
showToast("更新成功");
etOld.setText(""); // 清空输入框
etNew.setText("");
} else {
showToast("更新失败");
}
} else {
showToast("原用户名错误!");
}
});
}
五、项目完整源码
👇👇👇👇👇快捷获取方式👇👇👇👇👇