当前位置: 首页 > news >正文

Android studio进阶开发(七)---做一个完整的登录系统(前后端连接)

我们已经讲过了okhttp和登录系统的使用,我们今天做一个完整的登录系统,后端用spring+mybatis去做

数据库内容

-- 创建学生信息表
CREATE TABLE student_info (id SERIAL PRIMARY KEY,  -- 添加自增主键name VARCHAR(255) NOT NULL,number INT NOT NULL,code INT NOT NULL,student_id INT UNIQUE NOT NULL,  -- 学号唯一Ptteacher VARCHAR(255),Zone INT,grade INT NOT NULL,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP  -- 添加创建时间戳
);-- 插入示例数据
INSERT INTO student_info (name, number, code, student_id, Ptteacher, Zone, grade)
VALUES ('张三', 1001, 2023, 20230001, '王老师', 2, 1),('李四', 1002, 2023, 20230002, '刘老师', 1, 1),('王五', 2001, 2022, 20220001, '张老师', 3, 2),('赵六', 2002, 2022, 20220002, '谢老师', 2, 2),  -- 导师可以为空('钱七', 3001, 2021, 20210001, '陈老师', 1, 3);-- 查询验证数据
SELECT * FROM student_info;

效果图

请添加图片描述

后端

Student.java

package com.example.threes;public class Student { // 改为更有意义的类名private Integer id; // 添加主键private String name;private Integer number;private Integer code;private Integer studentId; // 改为Java命名规范private String ptTeacher; // 改为Java命名规范private Integer zone;private Integer grade;// 所有getter/setterpublic Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getNumber() {return number;}public void setNumber(Integer number) {this.number = number;}public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}public Integer getStudentId() {return studentId;}public void setStudentId(Integer studentId) {this.studentId = studentId;}public String getPtTeacher() {return ptTeacher;}public void setPtTeacher(String ptTeacher) {this.ptTeacher = ptTeacher;}public Integer getZone() {return zone;}public void setZone(Integer zone) {this.zone = zone;}public Integer getGrade() {return grade;}public void setGrade(Integer grade) {this.grade = grade;}
}

StudentController.java

package com.example.threes;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController
@RequestMapping("/students")
public class StudentController {@Autowiredprivate StudentService studentService;@GetMapping("/all")public List<Student> getAllStudents() {return studentService.getAllStudents();}@GetMapping("/search")public List<Student> getStudentById(@RequestParam(value = "student_id", required = true) Integer studentId) {return studentService.getStudentById(studentId);}
}

StudentDao.java

package com.example.threes;
import java.util.List;import java.util.List;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface StudentDao { // 接口改名List<Student> findAll();List<Student> findByStudentId(Integer studentId); // 单一查询方法// 删除其他不相关的方法
}

StudentService.java

package com.example.threes;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;@Service
public class StudentService {@Autowiredprivate StudentDao studentDao;public List<Student> getAllStudents(){return studentDao.findAll();}public List<Student> getStudentById(Integer studentId){return studentDao.findByStudentId(studentId);}
}

Student.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.threes.StudentDao"> <!-- 更新为正确的接口名 --><!-- 结果映射 --><resultMap id="studentResultMap" type="com.example.threes.Student"><id property="id" column="id"/><result property="name" column="name"/><result property="number" column="number"/><result property="code" column="code"/><result property="studentId" column="student_id"/><result property="ptTeacher" column="ptteacher"/><result property="zone" column="zone"/><result property="grade" column="grade"/></resultMap><!-- 查询所有学生 --><select id="findAll" resultMap="studentResultMap">SELECT * FROM student_info</select><!-- 按student_id查询 --><select id="findByStudentId" resultMap="studentResultMap" parameterType="int">SELECT * FROM student_infoWHERE student_id = #{studentId}</select>
</mapper>

效果图

请添加图片描述

客户端

student.java

package com.example.project_a.Information;public class Student {public String name;public int number;public int code;public int student_id;public String Ptteacher;public int Zone;public int grade;//学生的必要信息,可从数据库中修改public String getName() {return name;}public void setName(String name) {this.name = name;}// number 属性的getter和setterpublic int getNumber() {return number;}public void setNumber(int number) {this.number = number;}// code 属性的getter和setterpublic int getCode() {return code;}public void setCode(int code) {this.code = code;}// student_id 属性的getter和setterpublic int getStudent_id() {return student_id;}public void setStudent_id(int student_id) {this.student_id = student_id;}// Ptteacher 属性的getter和setterpublic String getPtteacher() {return Ptteacher;}public void setPtteacher(String Ptteacher) {this.Ptteacher = Ptteacher;}// Zone 属性的getter和setterpublic int getZone() {return Zone;}public void setZone(int Zone) {this.Zone = Zone;}// grade 属性的getter和setterpublic int getGrade() {return grade;}public void setGrade(int grade) {this.grade = grade;}
}

Stu_Login_Activity.java

package com.example.project_a;import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;import android.Manifest;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.method.HideReturnsTransformationMethod;
import android.text.method.PasswordTransformationMethod;
import android.text.method.TransformationMethod;
import android.view.Gravity;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;import com.example.project_a.Information.Student;
import com.example.project_a.tools.PermissionsInfo;import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import pub.devrel.easypermissions.AfterPermissionGranted;
import pub.devrel.easypermissions.EasyPermissions;public class Stu_Login_Activity extends AppCompatActivity {// 控件声明private EditText mEtUsername;private EditText mEtPassword;private Button mBtnLogin;private Button mBtnFound;private ImageView mImgEye;private CheckBox mChbRemember;private TextView tv_result;private CheckBox mChbAutoLogin;// 额外声明private SharedPreferences loginPreferences;// Student对象作为成员变量private Student student;private boolean studentDataLoaded = false;// 常量定义private static String strUsername = "1";private static String strPassword = "1";private static boolean hide = true;private static String CheckOrNot = "checked";private static String UserName = "UserName";private static String PassWord = "PassWord";private boolean isNetworkVerificationInProgress = false; // 新增标志位@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_stu_login);checkingAndroidVersion(); // 检查安卓版本// 初始化Student对象student = new Student();// 控件绑定mEtUsername = findViewById(R.id.ed_stuLogin_username);mEtPassword = findViewById(R.id.et_stuLogin_Password);mBtnLogin = findViewById(R.id.btn_stuLogin_login);mBtnFound = findViewById(R.id.btn_stuLogin_find);mImgEye = findViewById(R.id.img_stuLogin_eye);mChbRemember = findViewById(R.id.cb_stuLogin_remember);mChbAutoLogin = findViewById(R.id.cb_stuLogin_autologin);tv_result = findViewById(R.id.tv_result); // 确保有这个TextViewloginPreferences = getSharedPreferences("Login_stu", MODE_PRIVATE);boolean checked = loginPreferences.getBoolean(CheckOrNot, false);if (checked) {Map<String, String> map = readLogin();if (map != null) {mEtUsername.setText(map.get(UserName).toString());mEtPassword.setText(map.get(PassWord).toString());mChbRemember.setChecked(checked);}}// 功能实现// 登录mBtnLogin.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// 读取是否保存了密码configLoginInfo(mChbRemember.isChecked());// 防止重复点击if (isNetworkVerificationInProgress) {return;}// 正常登录if (onCheck(v)) {Intent intent = new Intent(Stu_Login_Activity.this, Activity_Student_Homepage.class);startActivity(intent);}}});// 跳转到网页mBtnFound.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Uri uri = Uri.parse("http://www.bing.com"); // 账户找回url修改位置Intent intent = new Intent(Intent.ACTION_VIEW, uri);startActivity(intent);}});// 密码显示与否设置mImgEye.setImageResource(R.drawable.invisible);mImgEye.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (v.getId() == R.id.img_stuLogin_eye) {if (hide == true) {mImgEye.setImageResource(R.drawable.visible);HideReturnsTransformationMethod method = HideReturnsTransformationMethod.getInstance();mEtPassword.setTransformationMethod(method);hide = false;} else if (hide == false) {mImgEye.setImageResource(R.drawable.invisible);TransformationMethod method = PasswordTransformationMethod.getInstance();mEtPassword.setTransformationMethod(method);hide = true;}}}});}// 发起GET方式的HTTP请求private void doGet() {isNetworkVerificationInProgress = true; // 设置网络验证开始标志final String username = mEtUsername.getText().toString().trim();if (username.isEmpty()) {runOnUiThread(() -> {Toast.makeText(Stu_Login_Activity.this, "用户名不能为空", Toast.LENGTH_SHORT).show();isNetworkVerificationInProgress = false;});return;}// 1. 构建带参数的URLHttpUrl url = new HttpUrl.Builder().scheme("http").host("192.168.43.9") // 替换为实际IP.port(8080).addPathSegments("students/search") // 层级路径的正确写法.addQueryParameter("student_id", username) // 查询参数 - 使用真实的用户名.build();OkHttpClient client = new OkHttpClient.Builder().connectTimeout(15, TimeUnit.SECONDS)  // 添加超时配置.readTimeout(20, TimeUnit.SECONDS).build();Request request = new Request.Builder().url(url).header("Accept-Language", "zh-CN").build();runOnUiThread(() -> Toast.makeText(Stu_Login_Activity.this, "正在验证账号...", Toast.LENGTH_SHORT).show());client.newCall(request).enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {runOnUiThread(() -> {Toast.makeText(Stu_Login_Activity.this, "网络请求失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();tv_result.setText("请求失败: " + e.getMessage());isNetworkVerificationInProgress = false;});}@Overridepublic void onResponse(Call call, Response response) throws IOException {if (response.isSuccessful()) {String resp = response.body().string();try {JSONArray jsonArray = new JSONArray(resp);// 检查是否有数据if (jsonArray.length() > 0) {// 只取第一组学生数据JSONObject jsonObject = jsonArray.getJSONObject(0);// 填充Student对象数据student.name = jsonObject.optString("name", "未知");student.number = jsonObject.optInt("number", 0);student.code = jsonObject.optInt("code", 0);student.student_id = jsonObject.optInt("studentId", 0);student.Ptteacher = jsonObject.optString("ptTeacher", "未知");student.Zone = jsonObject.optInt("zone", 0);student.grade = jsonObject.optInt("grade", 0);// 标记数据已加载studentDataLoaded = true;// 在UI线程中完成密码验证runOnUiThread(() -> {// 获取用户输入的密码String password = mEtPassword.getText().toString().trim();// 验证服务器返回的code是否与密码匹配if (password.equals(String.valueOf(student.code))) {Toast.makeText(Stu_Login_Activity.this, "登录成功!", Toast.LENGTH_SHORT).show();Intent intent = new Intent(Stu_Login_Activity.this, Activity_Student_Homepage.class);startActivity(intent);} else {Toast.makeText(Stu_Login_Activity.this, "密码错误", Toast.LENGTH_SHORT).show();}isNetworkVerificationInProgress = false; // 重置网络验证标志});} else {runOnUiThread(() -> {Toast.makeText(Stu_Login_Activity.this, "未找到学生信息", Toast.LENGTH_SHORT).show();tv_result.setText("未找到学生信息");isNetworkVerificationInProgress = false;});}} catch (JSONException e) {runOnUiThread(() -> {Toast.makeText(Stu_Login_Activity.this, "JSON解析失败", Toast.LENGTH_SHORT).show();tv_result.setText("JSON解析失败\n原始数据:" + resp);isNetworkVerificationInProgress = false;});}} else {runOnUiThread(() -> {Toast.makeText(Stu_Login_Activity.this, "服务器返回错误: " + response.code(), Toast.LENGTH_SHORT).show();tv_result.setText("服务器返回错误\n状态码:" + response.code());isNetworkVerificationInProgress = false;});}response.close(); // 确保关闭响应资源}});}/*onCheck确认密码事件*/private boolean onCheck(View v) {// 需要获取输入的用户名和密码String username = mEtUsername.getText().toString();String password = mEtPassword.getText().toString();// 弹出内容设置String ok = "登陆成功!";String fail = "密码或用户名有误,登陆失败。";// 1. 检查本地测试账号(特殊硬编码账号)if (username.equals(strUsername) && password.equals(strPassword)) {Toast.makeText(getApplicationContext(), ok, Toast.LENGTH_SHORT).show();return true;}// 2. 检查是否已加载学生数据(网络验证已成功且加载过数据)if (studentDataLoaded && password.equals(String.valueOf(student.code))) {Toast.makeText(getApplicationContext(), ok, Toast.LENGTH_SHORT).show();return true;}// 3. 尝试网络验证doGet();Toast toastCenter = Toast.makeText(Stu_Login_Activity.this, "正在验证账号...", Toast.LENGTH_SHORT);toastCenter.setGravity(Gravity.CENTER_HORIZONTAL, 0, 0);toastCenter.show();return false; // 返回false,等待网络验证回调处理登录结果}/*保存密码*/private void configLoginInfo(boolean checked) {SharedPreferences.Editor editor = loginPreferences.edit();editor.putBoolean(CheckOrNot, mChbRemember.isChecked());if (checked) {editor.putString(UserName, mEtUsername.getText().toString());editor.putString(PassWord, mEtPassword.getText().toString());} else {editor.remove(UserName).remove(PassWord);}editor.commit();}/*登录临时信息读取*/private Map<String, String> readLogin() {Map<String, String> map = new HashMap<>();String username = loginPreferences.getString(UserName, "");String password = loginPreferences.getString(PassWord, "");map.put(UserName, username);map.put(PassWord, password);return map;}// PermissionInfoprivate static final int REQUEST_PERMISSIONS = 9527;public void checkingAndroidVersion() {Toast toastCenter = Toast.makeText(getApplicationContext(), "当前安卓版本为" + Build.VERSION.SDK_INT, Toast.LENGTH_SHORT);toastCenter.show();if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {// Android6.0及以上先获取权限再定位requestPermission();} else {// Android6.0以下直接定位Toast toastCenter1 = Toast.makeText(getApplicationContext(), "ok", Toast.LENGTH_SHORT);toastCenter1.show();}}@AfterPermissionGranted(REQUEST_PERMISSIONS)public void requestPermission() {String[] permissions = {android.Manifest.permission.ACCESS_COARSE_LOCATION,android.Manifest.permission.ACCESS_FINE_LOCATION,android.Manifest.permission.READ_PHONE_STATE,Manifest.permission.WRITE_EXTERNAL_STORAGE};if (EasyPermissions.hasPermissions(this, permissions)) {// true 有权限 开始定位Toast info = Toast.makeText(getApplicationContext(), "已获得权限,可以定位啦!", Toast.LENGTH_SHORT);info.show();} else {// false 无权限EasyPermissions.requestPermissions(this, "需要权限", REQUEST_PERMISSIONS, permissions);}}@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);// 设置权限请求结果EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);}
}

stu_log.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=".Stu_Login_Activity"><ImageViewandroid:layout_width="64dp"android:layout_height="64dp"android:src="@drawable/kids"android:layout_marginTop="200dp"android:layout_gravity="center_horizontal"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="20sp"android:text="学 生 登 录 界 面"android:layout_marginTop="10dp"android:gravity="center_horizontal"android:layout_gravity="center_horizontal"/><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="vertical"android:layout_marginTop="65dp"android:layout_gravity="center_horizontal"><EditTextandroid:id="@+id/ed_stuLogin_username"android:layout_width="350dp"android:layout_height="48dp"android:layout_gravity="center"android:hint="Username"android:maxLines="1"android:textSize="16sp"/><RelativeLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="horizontal"><EditTextandroid:id="@+id/et_stuLogin_Password"android:layout_width="350dp"android:layout_height="48dp"android:layout_marginTop="6dp"android:layout_gravity="center_horizontal"android:hint="Password"android:inputType="textPassword"android:maxLines="1"android:textSize="16sp"/><ImageViewandroid:id="@+id/img_stuLogin_eye"android:layout_width="32dp"android:layout_height="32dp"android:layout_alignBottom="@+id/et_stuLogin_Password"android:layout_alignEnd="@+id/et_stuLogin_Password"android:layout_alignTop="@+id/et_stuLogin_Password"android:src="@drawable/invisible"/></RelativeLayout><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="horizontal"android:layout_gravity="center_horizontal"><CheckBoxandroid:id="@+id/cb_stuLogin_remember"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:text="记住密码"android:textSize="13sp"/><CheckBoxandroid:id="@+id/cb_stuLogin_autologin"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:text="自动登录"android:textSize="13sp"android:layout_marginLeft="76dp"/></LinearLayout></LinearLayout><RelativeLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"><Buttonandroid:id="@+id/btn_stuLogin_login"android:layout_width="200dp"android:layout_height="wrap_content"android:text="Login"android:textSize="17sp"/><Buttonandroid:id="@+id/btn_stuLogin_find"android:layout_width="95dp"android:layout_height="wrap_content"android:text="found"android:textSize="17sp"android:layout_toRightOf="@id/btn_stuLogin_login"android:layout_marginLeft="5dp"/></RelativeLayout>![请添加图片描述](https://i-blog.csdnimg.cn/direct/f87f0feedfa348a4a6e3add4981b8eed.png)<TextViewandroid:id="@+id/tv_result"android:layout_width="match_parent"android:layout_height="wrap_content"android:paddingLeft="5dp"android:textColor="@color/black"android:textSize="17sp" />
</LinearLayout>

之后我们通过数据库中的student_id和code来进行登录,同时还要保证网络连接的相同,就可以正常连接了。

请添加图片描述

相关文章:

  • 论文阅读笔记——Quo Vadis, Action Recognition? A New Model and the Kinetics Dataset
  • vscode实用配置
  • 基于SpringBoot+Redis实现RabbitMQ幂等性设计,解决MQ重复消费问题
  • 获取 HTTP 请求从发送到接收响应所花费的总时间
  • GpuGeek如何成为AI基础设施市场的中坚力量
  • uni-app学习笔记二十一--pages.json中tabBar设置底部菜单项和图标
  • 组件化:软件工程化的基础
  • 工作流引擎-11-开源 BPM 项目 jbpm
  • Spring Boot对一些技术框架进行了统一版本号管理
  • 【AI面试秘籍】| 第25期:RAG的关键痛点及解决方案深度解析
  • 「数据采集与网络爬虫(使用Python工具)」【数据分析全栈攻略:爬虫+处理+可视化+报告】
  • 【前端】Vue中使用CKeditor作为富文本编辑器
  • [网页五子棋][对战模块]处理连接成功,通知玩家就绪,逻辑问题(线程安全,先手判定错误)
  • [Windows]在Win上安装bash和zsh - 一个脚本搞定
  • openssl 怎么生成吊销列表
  • Docker容器创建Redis主从集群
  • 如何排查Redis单个Key命中率骤降?
  • 【Linux系统编程】Ext系列文件系统
  • Ansible 剧本精粹 - 编写你的第一个 Playbook
  • 告别手动绘图!基于AI的Smart Mermaid自动可视化图表工具搭建与使用指南
  • seo网站是什么/产品推广方案ppt模板
  • 有了网站模板 还要怎样做/长沙seo袁飞
  • ps做旅游网站/seo搜索排名
  • 简单网页设计模板网站/qq推广链接生成
  • 网站html优化/免费的外贸b2b网站
  • 做旅行网站多少钱/新媒体运营主要做什么