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

发布于:2025-06-02 ⋅ 阅读:(34) ⋅ 点赞:(0)

我们已经讲过了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/setter
    public 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 {

    @Autowired
    private 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 {
    @Autowired
    private 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_info
        WHERE 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和setter
    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    // code 属性的getter和setter
    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    // student_id 属性的getter和setter
    public int getStudent_id() {
        return student_id;
    }

    public void setStudent_id(int student_id) {
        this.student_id = student_id;
    }

    // Ptteacher 属性的getter和setter
    public String getPtteacher() {
        return Ptteacher;
    }

    public void setPtteacher(String Ptteacher) {
        this.Ptteacher = Ptteacher;
    }

    // Zone 属性的getter和setter
    public int getZone() {
        return Zone;
    }

    public void setZone(int Zone) {
        this.Zone = Zone;
    }

    // grade 属性的getter和setter
    public 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; // 新增标志位

    @Override
    protected 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); // 确保有这个TextView

        loginPreferences = 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() {
            @Override
            public 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() {
            @Override
            public 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() {
            @Override
            public 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. 构建带参数的URL
        HttpUrl 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() {
            @Override
            public 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;
                });
            }

            @Override
            public 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;
    }

    // PermissionInfo
    private 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);
        }
    }

    @Override
    public 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">

    <ImageView
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:src="@drawable/kids"
        android:layout_marginTop="200dp"
        android:layout_gravity="center_horizontal"/>
    <TextView
        android: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"/>
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_marginTop="65dp"
        android:layout_gravity="center_horizontal">
        <EditText
            android: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"/>
        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <EditText
                android: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"/>
            <ImageView
                android: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>
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_gravity="center_horizontal">
            <CheckBox
                android:id="@+id/cb_stuLogin_remember"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="记住密码"
                android:textSize="13sp"/>
            <CheckBox
                android: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>

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal">
        <Button
            android:id="@+id/btn_stuLogin_login"
            android:layout_width="200dp"
            android:layout_height="wrap_content"
            android:text="Login"
            android:textSize="17sp"/>
        <Button
            android: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)

    <TextView
        android: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来进行登录,同时还要保证网络连接的相同,就可以正常连接了。

请添加图片描述


网站公告

今日签到

点亮在社区的每一天
去签到