目录
一、测试环境说明

二、项目简介
本图书馆预约系统基于Android Studio开发,采用Java语言编写,主要技术包括:
-
前端使用Android原生组件开发界面,包括Activity、ViewPager、RadioButton等控件
-
数据存储采用SQLite数据库,通过DAO模式管理用户、座位等数据
-
网络通信使用OkHttp库调用第三方API获取励志名言数据
-
使用Gson解析JSON格式的API返回数据
-
传感器技术实现计步功能,通过加速度传感器检测步伐
-
采用Material Design设计规范优化UI/UX体验
-
使用Handler实现延时跳转和轮播图自动切换
三、项目演示
网络资源模板--基于Android studio 图书馆订座App
四、部设计详情(部分)
注册页

注册页面布局与登录页保持风格一致,包含账号、密码和确认密码三个输入字段,以及注册和取消两个操作按钮。
设计上特别强化了表单验证逻辑:实时检查账号是否已存在、密码是否符合复杂度要求、两次输入密码是否一致等,并通过Toast即时反馈验证结果。
用户注册成功后,系统自动填充登录表单并跳转回登录页,优化了用户操作流程。从技术实现看,注册功能同样基于UserDao完成数据持久化,采用标准的Intent机制传递账号密码数据。
页面设计考虑了错误处理场景,如网络异常或数据库操作失败等情况,确保系统的健壮性。清晰的流程指引和即时的操作反馈,使注册过程流畅无阻。
java
package njust.dzh.libraryreservation.App;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import njust.dzh.libraryreservation.R;
import njust.dzh.libraryreservation.Bean.User;
import njust.dzh.libraryreservation.DataBase.UserDao;
public class RegisterActivity extends AppCompatActivity {
private static final int RESULT_OK = 1;
private Button btnRegister;
private Button btnCancel;
private EditText etAccount;
private EditText etPassword;
private EditText etConfirmPassword;
private UserDao userDao;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register);
initView();
}
public void initView() {
// 去除默认标题栏
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) actionBar.hide();
// 绑定控件
etAccount =findViewById(R.id.et_account);
etPassword = findViewById(R.id.et_password);
etConfirmPassword = findViewById(R.id.et_confirm_password);
btnRegister = findViewById(R.id.btn_register);
btnCancel = findViewById(R.id.btn_cancel);
// 设置点击事件
btnRegister.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String acc = etAccount.getText().toString().trim();
String pass = etPassword.getText().toString().trim();
String confirm = etConfirmPassword.getText().toString().trim();
User user = new User(acc, pass);
userDao = new UserDao(getApplicationContext());
userDao.open();
if (userDao.findUser(user)) {
Toast.makeText(RegisterActivity.this, "账号已存在", Toast.LENGTH_SHORT).show();
} else if (TextUtils.isEmpty(pass) || TextUtils.isEmpty(confirm)) {
Toast.makeText(RegisterActivity.this, "密码不能为空", Toast.LENGTH_SHORT).show();
} else if(!pass.equals(confirm)) {
Toast.makeText(RegisterActivity.this, "两次输入的密码不同", Toast.LENGTH_SHORT).show();
} else {
userDao.addUser(user);
Toast.makeText(RegisterActivity.this, "注册成功!", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(RegisterActivity.this, LoginActivity.class);
//将账号和密码传递过去
intent.putExtra("acc", acc);
intent.putExtra("pass", pass);
setResult(RESULT_OK, intent);
finish();
}
userDao.close();
}
});
btnCancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
}
}
首页

首页采用经典的顶部轮播图加底部功能入口的布局模式。顶部ViewPager实现自动轮播的图片展示区,展示图书馆环境照片和学习提示,每张图片配有对应的指示点,显示当前浏览位置。
中间部分放置楼层选择的单选按钮组,下方排列四个主要功能入口按钮,采用Material Design风格的悬浮按钮作为预约操作入口。
整个页面布局层次分明,重点突出。技术实现上,首页通过Handler定时发送消息实现自动轮播效果,同时监听页面切换事件同步更新指示点状态。
功能按钮采用显眼的视觉设计,确保用户能够快速识别和操作。首页作为系统的中枢,设计上充分考虑了导航的便捷性和视觉的舒适性,为用户提供清晰的操作路径。
XML
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".App.MainActivity"
android:background="@drawable/bg">
<androidx.viewpager.widget.ViewPager
android:id="@+id/library_vp"
android:layout_width="match_parent"
android:layout_height="250dp" />
<LinearLayout
android:id="@+id/point_layout"
android:layout_width="match_parent"
android:layout_height="10dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:gravity="center_horizontal"
android:orientation="horizontal" />
<Button
android:id="@+id/reservation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="30sp"
android:background="@drawable/bg_button"
android:layout_margin="20dp"
android:text="@string/reservation_seats"/>
<RadioGroup
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginLeft="20dp">
<RadioButton
android:id="@+id/first_floor"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="一楼"
android:checked="true"/>
<RadioButton
android:id="@+id/second_floor"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="二楼"/>
<RadioButton
android:id="@+id/third_floor"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="三楼"/>
</RadioGroup>
<Button
android:id="@+id/check"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="30sp"
android:background="@drawable/bg_button"
android:layout_margin="20dp"
android:text="@string/check_seats"/>
<Button
android:id="@+id/student"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="30sp"
android:background="@drawable/bg_button"
android:layout_margin="20dp"
android:text="@string/personal_information"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:orientation="horizontal">
<Button
android:id="@+id/step"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:textSize="30sp"
android:layout_marginRight="5dp"
android:background="@drawable/bg_button"
android:text="@string/step"/>
<Button
android:id="@+id/exit"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:textSize="30sp"
android:layout_marginLeft="5dp"
android:background="@drawable/bg_button"
android:text="@string/exit"/>
</LinearLayout>
</LinearLayout>
座位选择

座位选择页面是本系统的核心功能界面,采用直观的矩阵布局模拟实际图书馆座位排列。
每个楼层对应独立的Activity,但保持统一的设计语言:每个座位表示为可选的RadioButton,已预约座位视觉上置灰并禁用,通过颜色区分不同状态。
页面底部设置悬浮的预约确认按钮,点击后弹出二次确认对话框,防止误操作。技术实现上,座位状态数据从本地数据库实时加载,通过遍历View树动态更新界面。
单选按钮分组管理,确保同一时间只能选择一个座位。页面还实现了数据变化后的自动刷新机制,保证用户看到的始终是最新的座位状态。
这种高度仿真的设计大大降低了用户的学习成本,使预约操作直观易懂。
java
package njust.dzh.libraryreservation.Activity;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.List;
import njust.dzh.libraryreservation.App.LoginActivity;
import njust.dzh.libraryreservation.Bean.Seat;
import njust.dzh.libraryreservation.DataBase.SeatDao;
import njust.dzh.libraryreservation.R;
public class SecondFloorActivity extends AppCompatActivity {
private FloatingActionButton fab;
private SeatDao seatDao;
private List<Seat> seatList;
private Seat seat;
private String account;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second_floor);
initView();
}
// 初始化
private void initView() {
initSeats();
initRadioGroups();
fab = findViewById(R.id.fab);
account = getIntent().getStringExtra(LoginActivity.ACCOUNT);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
AlertDialog alertDialog = new AlertDialog.Builder(view.getContext())
.setTitle("提示")
.setIcon(R.drawable.ic_order)
.setMessage("您确定要预订该位置吗?")
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
seatDao.open();
if (seatDao.getSeats(account) != null) {
Toast.makeText(SecondFloorActivity.this, "预订失败,您有预订的座位未退订", Toast.LENGTH_SHORT).show();
} else {
seatDao.addSecondSeats(seat);
Toast.makeText(SecondFloorActivity.this, "预订成功!", Toast.LENGTH_SHORT).show();
}
seatDao.close();
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
Toast.makeText(SecondFloorActivity.this, "预订已取消", Toast.LENGTH_SHORT).show();
}
})
.show();
}
});
}
// 初始化所有单选按钮
private void initSeats() {
seatDao = new SeatDao(this);
seatDao.open();
seatList = seatDao.getSecondSeats();
seatDao.close();
// 获取所有单选按钮实例
RadioButton seat11 = findViewById(R.id.seat11);
RadioButton seat12 = findViewById(R.id.seat12);
RadioButton seat13 = findViewById(R.id.seat13);
RadioButton seat21 = findViewById(R.id.seat21);
RadioButton seat22 = findViewById(R.id.seat22);
RadioButton seat31 = findViewById(R.id.seat31);
RadioButton seat32 = findViewById(R.id.seat32);
RadioButton seat33 = findViewById(R.id.seat33);
RadioButton seat41 = findViewById(R.id.seat41);
RadioButton seat42 = findViewById(R.id.seat42);
RadioButton seat51 = findViewById(R.id.seat51);
RadioButton seat52 = findViewById(R.id.seat52);
RadioButton seat61 = findViewById(R.id.seat61);
RadioButton seat62 = findViewById(R.id.seat62);
RadioButton seat63 = findViewById(R.id.seat63);
RadioButton seat71 = findViewById(R.id.seat71);
RadioButton seat72 = findViewById(R.id.seat72);
RadioButton seat81 = findViewById(R.id.seat81);
RadioButton seat82 = findViewById(R.id.seat82);
RadioButton seat83 = findViewById(R.id.seat83);
RadioButton []radioArray = new RadioButton[] {seat11, seat12, seat13, seat21, seat22, seat31,
seat32, seat33, seat41, seat42, seat51, seat52, seat61, seat62, seat63, seat71, seat72,
seat81, seat82, seat83};
// 遍历所有单选按钮
for (int i = 0; i < radioArray.length; i++) {
int id = radioArray[i].getId();
boolean ordered = false;
// 遍历已选中的单选列表
for (int j = 0; j < seatList.size(); j++) {
if (seatList.get(j).getId() == id) {
radioArray[i].setBackgroundResource(R.drawable.bg_seats_ordered);
radioArray[i].setEnabled(false);
ordered = true;
break;
}
}
// 退订座位后恢复可选
if (!ordered && !radioArray[i].isEnabled()) {
radioArray[i].setEnabled(true);
radioArray[i].setBackgroundResource(R.drawable.bg_seats);
}
}
}
// 初始化所有单选按钮组
private void initRadioGroups() {
RadioGroup radioGroup1 = findViewById(R.id.radio_group1);
RadioGroup radioGroup2 = findViewById(R.id.radio_group2);
RadioGroup radioGroup3 = findViewById(R.id.radio_group3);
RadioGroup radioGroup4 = findViewById(R.id.radio_group4);
RadioGroup radioGroup5 = findViewById(R.id.radio_group5);
RadioGroup radioGroup6 = findViewById(R.id.radio_group6);
RadioGroup radioGroup7 = findViewById(R.id.radio_group7);
RadioGroup radioGroup8 = findViewById(R.id.radio_group8);
// 单选按钮组的集合
RadioGroup [] radioGroups = new RadioGroup[] {radioGroup1, radioGroup2, radioGroup3, radioGroup4,
radioGroup5, radioGroup6, radioGroup7, radioGroup8};
// 每一组的监听器
radioGroup1.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup radioGroup, int i) {
// 清除其他组的选择
for (int k = 0; k < radioGroups.length; k++) {
if (k == 0) continue;
if (radioGroups[k].getCheckedRadioButtonId() != -1) {
radioGroups[k].clearCheck();
}
}
// 获取seat对象
seat = new Seat(i, account);
}
});
radioGroup2.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup radioGroup, int i) {
// 清除其他行的选择
for (int k = 0; k < radioGroups.length; k++) {
if (k == 1) continue;
if (radioGroups[k].getCheckedRadioButtonId() != -1) {
radioGroups[k].clearCheck();
}
}
seat = new Seat(i, account);
}
});
radioGroup3.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup radioGroup, int i) {
// 清除其他行的选择
for (int k = 0; k < radioGroups.length; k++) {
if (k == 2) continue;
if (radioGroups[k].getCheckedRadioButtonId() != -1) {
radioGroups[k].clearCheck();
}
}
seat = new Seat(i, account);
}
});
radioGroup4.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup radioGroup, int i) {
// 清除其他行的选择
for (int k = 0; k < radioGroups.length; k++) {
if (k == 3) continue;
if (radioGroups[k].getCheckedRadioButtonId() != -1) {
radioGroups[k].clearCheck();
}
}
seat = new Seat(i, account);
}
});
radioGroup5.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup radioGroup, int i) {
// 清除其他行的选择
for (int k = 0; k < radioGroups.length; k++) {
if (k == 4) continue;
if (radioGroups[k].getCheckedRadioButtonId() != -1) {
radioGroups[k].clearCheck();
}
}
seat = new Seat(i, account);
}
});
radioGroup6.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup radioGroup, int i) {
// 清除其他行的选择
for (int k = 0; k < radioGroups.length; k++) {
if (k == 5) continue;
if (radioGroups[k].getCheckedRadioButtonId() != -1) {
radioGroups[k].clearCheck();
}
}
seat = new Seat(i, account);
}
});
radioGroup7.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup radioGroup, int i) {
// 清除其他行的选择
for (int k = 0; k < radioGroups.length; k++) {
if (k == 6) continue;
if (radioGroups[k].getCheckedRadioButtonId() != -1) {
radioGroups[k].clearCheck();
}
}
seat = new Seat(i, account);
}
});
radioGroup8.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup radioGroup, int i) {
// 清除其他行的选择
for (int k = 0; k < radioGroups.length; k++) {
if (k == 7) continue;
if (radioGroups[k].getCheckedRadioButtonId() != -1) {
radioGroups[k].clearCheck();
}
}
seat = new Seat(i, account);
}
});
}
}
五、项目源码
👇👇👇👇👇快捷方式👇👇👇👇👇