系列文章目录
前言(重要提示)
这篇内容的Java后端我使用的是Springboot框架(和实验二相同)编程新技术实务实验二,所以本篇不会对Springboot的相关问题累述,所以实验二用JSP做的小伙伴可能需要谨慎食用。
IDE:IDEA
服务器操作系统:Windows 2019 server
数据库版本 Mysql 5.7.34
用食物标注了一下各个部分的难度和重要性
2020-12-20 更新
这个实验得了A-,可改进之处:
- 部署到Linux服务器上,windows不加分
- 加入短信验证找回密码(这个可以在阿里云腾讯云上买就行,现成API很好弄)
后来知道得了A+,可能是老师怕我骄傲 /ww
Github 链接已更新~~
一、环境配置
然后我们需要做以下配置:
-
申请ssl证书。Android 对于网络的访问比较严格,一般需要访问https开头的网址(当然也可以访问http,但是要配置个啥我忘了),这个是免费的,我在腾讯云上申请的(原因在于我域名在腾讯云买的)腾讯云购买ssl证书
-
(可选)购买一个域名并配置DNS解析。域名很便宜,选个不热门的,只要1元1年,我在腾讯云买的腾讯云买域名
二、实验目的
通过使用Android API进行系统注册模块的开发,包括前台的Android原生app以后后台服务模块的开发,要求后台使用JavaEE框架实现。
三、实验详情
- Android app的开发
对于Android app,至少需要有如下界面:
-
登录界面:包含用户名、密码的文字标识以及相应的输入栏,登录以及注册的按钮。当输入用户名以及密码后,点击登录按钮,则交数据提交至后台进行验证,如通过验证则跳转至欢迎界面,否则跳转回登录界面,并提示用户的验证错误原因;当用户点击注册按钮则跳转至注册界面。
-
注册界面:包含用户名、密码以及确认密码的文字标识以及相应的输入栏,提交以及取消按钮。当输入相关信息后,首先验证输入的信息是否符合要求(用户名至少5位,最多10位,以英文字母开头,只允许包含英文字母、数字以及_,同时必须至少有一个大写英文字母;密码为6-12位,只允许包含英文字母、数字和_,同时要求确认密码必须与密码一致),如不符合要求则在界面内提示错误,只有符合要求才提交给后台进行注册操作。如注册成功则跳转至欢迎界面,否则跳转回注册界面并提示用户的注册错误原因;当用户点击取消按钮则返回登录界面。
-
欢迎界面:显示对用户的欢迎信息,其中必须包括用户的登录名。
- 后台服务的开发
- 对于Android app调用的后台服务而言,要求使用JavaEE中的JSP或者Servlet进行编写,响应信息可以使用XML或JSON等方式进行封装,封装的信息由Android app再进行解析处理。
- 对于注册信息的存储,要求使用Mysql数据库进行存放,其中用户名作为表的主键进行存储。
马上就要开始了!
四、代码详情
Springboot后端实现并部署(先来点简单的)
数据库设计(隔夜剩饭)
本来不应该用username作为主键的,应该用可以自增的变量(例如id),但是实验要求用username,就依他了。
我设计的主要有
- 用户名
- 密码
- 年龄(3位,有可能百岁嘛)
- 性别(0是男,1是女)
- 电话
- 邮箱
- 注册时间
- 没了:)
除了时间全部选varchar。
年龄也是varchar的原因在于如果为int的话,我们不能赋值为null或者空字符串,(可以包装成integer类,但是我懒了)只能赋默认值 -1 或者 0 ,这样在后面所有用到的地方都需要判断,有点麻烦
但是这样设计的话就需要在后面判断年龄是不是纯数字,即验证输入的合法性(我是在前端用正则匹配的)
Springboot后端实现(饮品)
先上项目结构,非常简单:
除了我用红框框起来的,其他和实验二很相似。
主体分为四层:
- bean层为实体层,和数据库字段映射
- mapper层为映射层,映射数据库的各种操作(例如最简单的增删查改)
- service层为业务层,处理业务逻辑细节
- controller层为控制层,处理访问请求
(详情请移步实验二,在此不多做累述实验二)
不同之处
和实验二不同的地方在于,因为我需要服务器返回JSON字符串作为返回值,我使用了阿里巴巴的开源库fastjson来搞定JSON的各种问题(JSON基础请移步JSON。比较简单,不做累述)
使用只需要在pom.xml中添加依赖,pom.xml文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<repositories>
<repository>
<id>maven-ali</id>
<url>http://maven.aliyun.com/nexus/content/repositories/central</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
<checksumPolicy>fail</checksumPolicy>
</snapshots>
</repository>
</repositories>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.0</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>newProgram4</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.xmlunit</groupId>
<artifactId>xmlunit-core</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
因为我要部署到服务器上,所以配置文件也有一点点变化,配置如下:
spring:
datasource:
url: jdbc:mysql://localhost:3306/test4?useSSL=false
username: root
password:
driver-class-name: com.mysql.cj.jdbc.Driver
jackson:
time-zone: GMT+8
server:
port: 8888
ssl:
key-store-password: 请填入自己证书的秘钥
key-store-type: JKS
key-store: classpath:www.csgoha.xyz.jks
主要就是加了个证书,换了个端口,设置了个时区(我数据库的密码为空所以没填)。不放到服务器的小伙伴把server后面的删了就行
controller代码
package com.example.demo.controller;
import com.alibaba.fastjson.JSONObject;
import com.example.demo.bean.Person;
import com.example.demo.service.PersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.jackson.JsonObjectDeserializer;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
@Controller
public class PersonController {
@Autowired
private PersonService personService;
/**
* 检查登录信息
*
* @param request url参数
* @return JSONObject
* statusCode 200为登陆成功,404未未找到,407为密码错误
* 登陆成功返回个人信息
*/
@RequestMapping(value = "/login")
@ResponseBody
public JSONObject checkLogin(HttpServletRequest request){
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println(username);
return personService.checkLogin(username,password);
}
/**
* 注册
*
* @param request url参数
* @return JSONObject
* statusCode 200为注册成功,401为用户名重复
*/
@RequestMapping(value = "/addPerson")
@ResponseBody
public JSONObject addPerson(HttpServletRequest request){
String username = request.getParameter("username");
String password = request.getParameter("password");
String age = request.getParameter("age");
String gender = request.getParameter("gender");
String teleno = request.getParameter("teleno");
String email = request.getParameter("email");
return personService.addPerson(username,password,age,gender,teleno,email);
}
/**
* 获取信息
*
* @param request url参数
* @return JSONObject
* statusCode 200为注册成功,401为用户不存在
*/
@RequestMapping(value = "/queryPerson")
@ResponseBody
public JSONObject queryPerson(HttpServletRequest request){
String username = request.getParameter("username");
return personService.queryPerson(username);
}
/**
* 修改信息,一般在修改信息前需要先获取信息
* username 和 gender 不能改
*
* @param request url参数
* @return JSONObject
* statusCode 200为修改成功,401为用户名重复
*/
@RequestMapping(value = "/updatePerson")
@ResponseBody
public JSONObject updatePerson(HttpServletRequest request){
String username = request.getParameter("username");
String password = request.getParameter("password");
String age = request.getParameter("age");
String teleno = request.getParameter("teleno");
String email = request.getParameter("email");
return personService.updatePerson(username,password,age,teleno,email);
}
}
注释都写的比较明确了,我主要使用url传参
service代码
package com.example.demo.service;
import com.alibaba.fastjson.JSONObject;
import com.example.demo.bean.Person;
import com.example.demo.mapper.PersonMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Service
public class PersonService {
@Autowired(required = false)
private PersonMapper personMapper;
private Map<String, Object> getPersonInfo(Person person){
Map<String, Object> map = new HashMap<>();
map.put("username",person.getUsername());
map.put("password",person.getPassword());
map.put("age",person.getAge());
map.put("gender",person.getGender());
map.put("teleno",person.getTeleno());
map.put("email",person.getEmail());
map.put("time",person.getTime());
return map;
}
private JSONObject makePersonJSON(String statusCode, Person person){
JSONObject checkResult = new JSONObject();
checkResult.put("statusCode",statusCode);
if (person != null){
Map<String, Object> map = getPersonInfo(person);
checkResult.put("userInfo",map);
}
else{
checkResult.put("userInfo",null);
}
return checkResult;
}
public JSONObject checkLogin(String username,String password){
Person person = personMapper.checkLogin(username);
// 查询结果,包含状态码和个人信息(如果正确的话)
JSONObject checkResult ;
if (person == null){
// 未找到该人
checkResult = makePersonJSON("404",null);
return checkResult;
}
else {
if (person.getPassword().equals(password)){
// 正确
checkResult = makePersonJSON("200",person);
return checkResult;
}
else{
// 密码错误
checkResult = makePersonJSON("407",null);
return checkResult;
}
}
}
// private JSONObject makeAddJSON(String statusCode,Person person){
// JSONObject addResult = new JSONObject();
// addResult.put("statusCode",statusCode);
// if (person != null){
// Map<String, Object> map = getPersonInfo(person);
// addResult.put("userInfo",map);
// }else{
// addResult.put("userInfo",null);
// }
// return addResult;
// }
public JSONObject addPerson(String username,String password,String age,String gender,String teleno,String email){
// 首先判断用户名是否存在,存在直接返回错误
JSONObject addResult ;
if (personMapper.findPersonByUsername(username) != null){
// 已经存在
return makePersonJSON("401",null);
}
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = df.format(new Date());// new Date()为获取当前系统时间
personMapper.addPerson(username, password, age, gender, teleno, email,time);
addResult = makePersonJSON("200", personMapper.findPersonByUsername(username));
return addResult;
}
public JSONObject queryPerson(String username){
// 首先判断用户名是否存在,不存在直接返回错误
Person person = personMapper.findPersonByUsername(username);
if (person == null){
return makePersonJSON("401",null);
}
else {
return makePersonJSON("200",person);
}
}
public JSONObject updatePerson(String username,String password,String age,String teleno,String email){
// 首先判断用户名是否存在,不存在直接返回错误
Person person = personMapper.findPersonByUsername(username);
if (person == null){
return makePersonJSON("401",null);
}
else{
personMapper.updatePerson(username, password, age, teleno, email);
return makePersonJSON("200",personMapper.findPersonByUsername(username));
}
}
}
注释也写的比较明确。
说明:
- 我使用的fastjson包装json对象(在makePersonJSON函数中),其中userInfo的键值对用的是hashmap的数据结构(没那么高级我就当个字典用了。。:))返回的json形如
{
"userInfo": {
"password": "666666",
"gender": "1",
"time": "2020-12-10T11:28:45.000+08:00",
"age": "",
"email": "",
"username": "12345",
"teleno": ""
},
"statusCode": "200"
}
-
使用了状态码来给前端,以方便前端判断是什么情况(例如:在登录的时候通过返回不同的状态码来告诉前端,登陆失败的时候究竟是用户不存在还是密码错误)
-
登陆成功会返回用户信息
mapper代码
package com.example.demo.mapper;
import com.example.demo.bean.Person;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
@Mapper
public interface PersonMapper {
@Select("SELECT * FROM personandroid")
List<Person> findAllPerson();
@Select("SELECT * FROM personandroid where username='${username}'")
Person findPersonByUsername(String username);
@Select("select * from personandroid where username='${username}'")
Person checkLogin(String username);
@Insert("insert into personandroid(username,password,age,gender,teleno,email,time)values('${username}', '${password}','${age}','${gender}','${teleno}','${email}','${time}')")
void addPerson(String username,String password,String age,String gender,String teleno,String email,String time);
@Update("update personandroid set password='${password}',age='${age}',teleno='${teleno}',email='${email}' where username = '${username}' ")
void updatePerson(String username,String password,String age,String teleno,String email);
}
主要实现了增删查改
说明:
- 当参数是String(varchar)的时候在sql语句中需要用 ’ ’ 单引号圈住
在服务器上启动服务
我们使用MAVEN打包jar包
步骤如下:
然后把jar包搞到服务器上
在服务器上进入jar包路径 输入 java -jar 名字.jar 即可运行
(服务器需要有java运行环境和数据库环境)
结束!
Android前端实现(正餐)
环境配置可以参考实验三,我用的IDEA基本就是一路next,让安装啥安装就行了
有的同学可能一进来看到这么多代码吓一跳,其实没关系,都很好理解。
下面我会从大到框架,小到代码细节,慢慢讲,保证大家的都能明白。
前端设计总体逻辑:
大致架构思路:
这是我第一次写Android的东西,我发现这个东西的设计思路好像和小程序有异曲同工之妙。原因如下:
- 主要由两部分构成:一部分是用xml写的,在res.layout文件下,主要描述页面的框架(类似于html + css);另一部分用java写的,在java.com.example.newProgram4文件夹下,主要处理后台用户交互逻辑(类似于JS)
项目结构如下:
各种文件如图所示
新建页面的时候如下图:
选择页面即可
我们首先接下来首先看页面框架部分,再看逻辑部分。
页面框架(前菜)
这里有两种设计模式,我们既可以在xml里直接写代码,IDEA也提供给我们更方便的办法,如下图:
选择Design进到下面的页面:
我们可以直接在这里直接把组件拖拽进页面,然后在右边进行属性的设置,会自动在xml文件中生成对应的代码
我是用的是约束布局
项目比较简单,我用到的组件并不多,列举如下:
- TextView :文本显示
- Button:按钮
- EditText:文本输入框
- Switch :开关(用来模拟选择性别)
- ImageView:图片
每个组件都有一些属性,即描述这个组件的样式位置等等
我们用到的属性的解释如下:
-
组件共有属性(***代表全部)
- layout_width ,layout_height:宽和高,值可以选:依据父组件或者根据自身长度自适应
- layout_margin***:和其他组件的距离,值为单位为dp的距离
- layout_constraint***:相对于什么组件的位置,值为其他组件的名字
- visibility:是否可见
- id:这个比较重要,每个组件都有一个id,这是组件在程序中的唯一名字。作用例如:java代码可以依据id来获取组件,自定义值
- 没有了,我比较喜欢 6 这个数字:)
-
组件特有属性
- EditText组件
- hint:提示,在什么都没有输入的时候的显示
- inputType:输入的类型,这个选项有很多,例如数字,纯文本,邮箱之类的,更多的可以去查(有趣的是搜狗输入法可以根据这个类型自动跳出的键盘类型不同。例如你你用的number,键盘就是数字;你用的email,键盘就会在左下角带个@,挺智能的哈哈)
- textColor和textColorHint:字体颜色和提示颜色
- Button组件
- onClick:这个比较重要,这个属性的值为一个这个页面的java代码中的一个函数类型为public的函数名(举个例子:我们onclick的值为onClickRegister,那么当用户一点击这个按钮的时候,我们就会去页面对应的java代码中找到这个函数并执行其中的代码)
- ImageView组件
- src:图片路径,值为在drawable或drawable-v24文件夹下的图片路径
- EditText组件
activity_main.xml
主页面,登录
<?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" tools:context=".MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="50dp"
android:layout_marginLeft="50dp"
android:layout_marginTop="36dp"
android:text="@string/username_description"
app:layout_constraintBottom_toTopOf="@+id/username"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="48dp"
android:layout_marginLeft="48dp"
android:layout_marginTop="48dp"
android:layout_marginEnd="48dp"
android:layout_marginRight="48dp"
android:height="40sp"
android:autofillHints=""
android:hint="@string/username"
android:inputType="text"
android:textColor="@color/colorInput"
android:textColorHint="@color/colorHint"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:layout_constraintHorizontal_bias="0.5" />
<TextView
android:id="@+id/textPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="50dp"
android:layout_marginLeft="50dp"
android:layout_marginTop="10dp"
android:text="@string/password_description"
app:layout_constraintStart_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/username" />
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="48dp"
android:layout_marginLeft="48dp"
android:layout_marginTop="120dp"
android:layout_marginEnd="48dp"
android:layout_marginRight="48dp"
android:height="40sp"
android:autofillHints=""
android:hint="@string/password"
android:inputType="textPassword"
android:textColor="@color/colorInput"
android:textColorHint="@color/colorHint"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/login"
style="@style/Widget.AppCompat.Button.ButtonBar.AlertDialog"
android:layout_width="300dp"
android:layout_height="0dp"
android:layout_marginStart="48dp"
android:layout_marginLeft="48dp"
android:layout_marginTop="52dp"
android:layout_marginEnd="48dp"
android:layout_marginRight="48dp"
android:onClick="onClickLogin"
android:text="@string/login"
android:textColor="#03A9F4"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.466"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/password" />
<Button
android:id="@+id/register"
style="@style/Widget.AppCompat.Button.ButtonBar.AlertDialog"
android:layout_width="300dp"
android:layout_height="0dp"
android:layout_marginStart="48dp"
android:layout_marginLeft="48dp"
android:layout_marginTop="20dp"
android:layout_marginEnd="48dp"
android:layout_marginRight="48dp"
android:onClick="onClickRegister"
android:text="@string/register"
android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/login" />
</androidx.constraintlayout.widget.ConstraintLayout>
activity_register.xml
注册页面
<?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" tools:context=".MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="50dp"
android:layout_marginLeft="50dp"
android:layout_marginTop="36dp"
android:text="@string/username_description"
app:layout_constraintBottom_toTopOf="@+id/username"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="48dp"
android:layout_marginLeft="48dp"
android:layout_marginTop="48dp"
android:layout_marginEnd="48dp"
android:layout_marginRight="48dp"
android:height="40sp"
android:autofillHints=""
android:hint="@string/username"
android:inputType="text"
android:textColor="@color/colorInput"
android:textColorHint="@color/colorHint"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:layout_constraintHorizontal_bias="0.5" />
<TextView
android:id="@+id/textPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="50dp"
android:layout_marginLeft="50dp"
android:layout_marginTop="10dp"
android:text="@string/password_description"
app:layout_constraintStart_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/username" />
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="48dp"
android:layout_marginLeft="48dp"
android:layout_marginTop="120dp"
android:layout_marginEnd="48dp"
android:layout_marginRight="48dp"
android:height="40sp"
android:autofillHints=""
android:hint="@string/password"
android:inputType="textPassword"
android:textColor="@color/colorInput"
android:textColorHint="@color/colorHint"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/login"
style="@style/Widget.AppCompat.Button.ButtonBar.AlertDialog"
android:layout_width="300dp"
android:layout_height="0dp"
android:layout_marginStart="48dp"
android:layout_marginLeft="48dp"
android:layout_marginTop="52dp"
android:layout_marginEnd="48dp"
android:layout_marginRight="48dp"
android:onClick="onClickLogin"
android:text="@string/login"
android:textColor="#03A9F4"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.466"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/password" />
<Button
android:id="@+id/register"
style="@style/Widget.AppCompat.Button.ButtonBar.AlertDialog"
android:layout_width="300dp"
android:layout_height="0dp"
android:layout_marginStart="48dp"
android:layout_marginLeft="48dp"
android:layout_marginTop="20dp"
android:layout_marginEnd="48dp"
android:layout_marginRight="48dp"
android:onClick="onClickRegister"
android:text="@string/register"
android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/login" />
</androidx.constraintlayout.widget.ConstraintLayout>
activity_show_data.xml
登录进去后的页面
TODO:我准备把我CSGO爬到的数据放进来
<?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"
tools:context=".showData">
<Button
android:id="@+id/changeUserInfo"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="34dp"
android:layout_marginEnd="24dp"
android:layout_marginRight="24dp"
android:onClick="navigateToChangeUserInfo"
android:text="@string/changeUserInfo"
android:textColor="#7E8785"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/welcome"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginLeft="32dp"
android:layout_marginTop="44dp"
android:text="@string/welcome"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button3"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginLeft="20dp"
android:layout_marginTop="12dp"
android:onClick="getData"
android:text="@string/getData"
android:textColor="#66EC66"
android:textSize="24sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/welcome" />
</androidx.constraintlayout.widget.ConstraintLayout>
activity_change_user_info.xml
修改个人信息的页面,和注册页面非常像,不做展示了,具体代码参见github
页面逻辑交互(主菜)
这部分代码我们使用java描述,会详细说每个文件的设计逻辑和重要函数还有一些踩过的坑。
每次新建页面的时候都有这样一个函数
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
这个函数非常有趣:它启动于这个页面渲染前,作用是初始化页面,所以如果我们可以把需要启动这个页面的时候做的事情写到这个函数里面(例如:可以获取页面组件;可以接收上一个页面的传值等等)
其中的*setContentView(R.layout.activity_main)*方法的参数为这个文件对应页面的名字,例如我的名字是activity_main.xml,如下:
在网络连接这里,我用的是okhttp开源库,这是一个现在非常火的java网络连接的开源库okhttp
用的时候我们只需要在配置文件
加入:
然后点击右上角弹出的提示上的同步(synchronization的缩写,我这没变化这个提示出不来,我忘了具体是啥了。。)等待下载完毕即可
准备工作结束!
坐稳了,下面开始正餐
MainActivity
初始页面,处理登录逻辑
package com.example.newprogram4;
import android.content.Intent;
import android.os.Looper;
import android.os.StrictMode;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
public class MainActivity extends AppCompatActivity {
private EditText username;
private EditText password;
private final OkHttpClient client = new OkHttpClient();
// static class Person{
// String username;
// String password;
// String age;
// String gender;
// String teleno;
// String email;
// Date time;
// public Person() {
// }
// }
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
username = findViewById(R.id.username);
password = findViewById(R.id.password);
if (android.os.Build.VERSION.SDK_INT > 9) {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
}
private String requestURL(String url) throws IOException {
Request request = new Request.Builder().url(url).build();
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
// String res = Objects.requireNonNull(response.body()).string();
// JSONObject jsonObject = JSON.parseObject(res);
return response.body().string();
//JSONObject jsonObject = response.body().string();
//System.out.println(jsonObject);
} else {
Looper.prepare();
Toast toast=Toast.makeText(getApplicationContext(), "Network Connection Failed", Toast.LENGTH_SHORT);
toast.show();
Looper.loop();// 进入loop中的循环,查看消息队列
throw new IOException("Unexpected code " + response);
}
}
public void onClickLogin(View view){
final String username = this.username.getText().toString();
final String password = this.password.getText().toString();
if (username.equals("")){
// 没有输入用户名
Toast toast=Toast.makeText(getApplicationContext(), "Username Cannot be Empty", Toast.LENGTH_SHORT);
toast.show();
return;
}
if (password.equals("")){
// 没有输入密码
Toast toast=Toast.makeText(getApplicationContext(), "Password Cannot be Empty", Toast.LENGTH_SHORT);
toast.show();
return;
}
new Thread(new Runnable(){
@Override
public void run() {
String url = String.format("https://www.csgoha.xyz:8888/login?username=%s&password=%s",username,password);
System.out.println("url : " + url);
System.out.println("连接获取数据...");
String res = null;
try {
res = MainActivity.this.requestURL(url);
} catch (IOException e) {
Looper.prepare();
Toast toast=Toast.makeText(getApplicationContext(), "Network Connection Failed", Toast.LENGTH_SHORT);
toast.show();
Looper.loop();// 进入loop中的循环,查看消息队列
e.printStackTrace();
}
if (res != null){
// 将json字符串转为json对象
JSONObject object = JSON.parseObject(res);
System.out.println(object.toJSONString());
// 获取状态码
String statusCode = (String) object.get("statusCode");
// JSONObject person = object.getJSONObject("userInfo");
// String username = person.getString("username");
// System.out.println(username);
// System.out.println(statusCode);
//assert statusCode != null;
if (statusCode.equals("200")){
// 登录成功
Looper.prepare();
Toast toast=Toast.makeText(getApplicationContext(), "Access Successful", Toast.LENGTH_SHORT);
toast.show();
Intent intent = new Intent(MainActivity.this, ShowData.class);
intent.putExtra("username",username);
startActivity(intent);
Looper.loop();// 进入loop中的循环,查看消息队列
}else if(statusCode.equals("404")){
// 查无此人
Looper.prepare();
Toast toast=Toast.makeText(getApplicationContext(), "Invalid Password or Username", Toast.LENGTH_SHORT);
toast.show();
Looper.loop();// 进入loop中的循环,查看消息队列
}else{
Looper.prepare();
Toast toast=Toast.makeText(getApplicationContext(), "Invalid Password or Username", Toast.LENGTH_SHORT);
toast.show();
Looper.loop();// 进入loop中的循环,查看消息队列
}
}
}
}).start();
}
public void onClickRegister(View view) {
Intent intent=new Intent(MainActivity.this, Register.class);
//启动
startActivity(intent);
}
}
-
设计逻辑:
- 当点击登录按钮的时候进入onClickLogin函数,首先获取输入,检查用户名或者密码是不是空,然后在子线程内访问网络并查看返回信息,当返回的状态码为200的时候即信息校验正确,那么弹出提示登陆成功并带username参数跳转到下一个页面
- 当点击注册的时候执行跳转到登陆页面即可
-
重要函数:
- requestURL函数,主要是使用okhttp的方法访问url然后获取数据
Register
处理登录
package com.example.newprogram4;
import android.content.Intent;
import android.os.Looper;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.Switch;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
public class Register extends AppCompatActivity {
private EditText username;
private EditText password;
private EditText age;
private EditText teleno;
private EditText email;
private Switch switchGender;
private String gender = "0"; // 0 是男,1 是女
public void setGender(String gender) {
this.gender = gender;
}
private final OkHttpClient client = new OkHttpClient();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register);
username = findViewById(R.id.username);
password = findViewById(R.id.password);
age = findViewById(R.id.age);
switchGender = findViewById(R.id.gender);
//gender = findViewById(R.id.gender);
teleno = findViewById(R.id.teleno);
email = findViewById(R.id.email);
// 监听switch事件
switchGender.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener(){
public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {
if (isChecked) {
// 开启switch,女
setGender("1");
} else {
// 关闭swtich,男
setGender("0");
}
}
});
}
private String requestURL(String url) throws IOException {
Request request = new Request.Builder().url(url).build();
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
// String res = Objects.requireNonNull(response.body()).string();
// JSONObject jsonObject = JSON.parseObject(res);
return response.body().string();
//JSONObject jsonObject = response.body().string();
//System.out.println(jsonObject);
} else {
Looper.prepare();
Toast toast=Toast.makeText(getApplicationContext(), "Network Connection Failed", Toast.LENGTH_SHORT);
toast.show();
Looper.loop();// 进入loop中的循环,查看消息队列
throw new IOException("Unexpected code " + response);
}
}
public void onClickRegisterUser(View view){
final String username = this.username.getText().toString();
final String password = this.password.getText().toString();
final String age = this.age.getText().toString();
final String teleno = this.teleno.getText().toString();
final String email = this.email.getText().toString();
final String gender = this.gender;
// 正则匹配密码,必须为字母+数字,长度6——12
final String passwordPattern = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,12}$";
// 匹配年龄
final String agePattern = "^((1[0-5])|[1-9])?\\d$";
// 手机号
final String telenoPattern = "^[1](([3][0-9])|([4][5,7,9])|([5][0-9])|([6][6])|([7][3,5,6,7,8])|([8][0-9])|([9][8,9]))[0-9]{8}$";
//email
final String emailPattern = "^\\s*\\w+(?:\\.{0,1}[\\w-]+)*@[a-zA-Z0-9]+(?:[-.][a-zA-Z0-9]+)*\\.[a-zA-Z]+\\s*$";
if (username.equals("")){
// 用户名不能为空
Toast toast=Toast.makeText(getApplicationContext(), "Username Cannot be Empty", Toast.LENGTH_SHORT);
toast.show();
return;
}
if (username.length() > 10){
// 用户名长度不大于10
Toast toast=Toast.makeText(getApplicationContext(), "Username Length Limited 10", Toast.LENGTH_SHORT);
toast.show();
return;
}
if (password.equals("")){
// 密码不能为空
Toast toast=Toast.makeText(getApplicationContext(), "Password Cannot be Empty", Toast.LENGTH_SHORT);
toast.show();
return;
}
if (password.length() > 12){
// 密码长度不大于12
Toast toast=Toast.makeText(getApplicationContext(), "Password Length Limited 12", Toast.LENGTH_SHORT);
toast.show();
return;
}
if (password.length() < 6){
// 密码长度不小于6
Toast toast=Toast.makeText(getApplicationContext(), "Password Length Should be Over 6", Toast.LENGTH_SHORT);
toast.show();
return;
}
if (!password.matches(passwordPattern)){
Toast toast=Toast.makeText(getApplicationContext(), "Password Should Mix Letters and Numbers", Toast.LENGTH_SHORT);
toast.show();
return;
}
if (!age.matches(agePattern)){
Toast toast=Toast.makeText(getApplicationContext(), "Age Inappropriate", Toast.LENGTH_SHORT);
toast.show();
return;
}
// 填了电话再匹配
if (!teleno.matches(telenoPattern) && !teleno.equals("")){
Toast toast=Toast.makeText(getApplicationContext(), "Invalid Teleno", Toast.LENGTH_SHORT);
toast.show();
return;
}
if (!email.matches(emailPattern) && !email.equals("")){
Toast toast=Toast.makeText(getApplicationContext(), "Invalid Email", Toast.LENGTH_SHORT);
toast.show();
return;
}
new Thread(new Runnable(){
@Override
public void run() {
String url = String.format("https://www.csgoha.xyz:8888/addPerson?username=%s&password=%s&age=%s&teleno=%s&email=%s&gender=%s",username,password,age,teleno,email,gender);
System.out.println("url : " + url);
System.out.println("连接获取数据...");
String res = null;
try {
res = Register.this.requestURL(url);
} catch (IOException e) {
Looper.prepare();
Toast toast=Toast.makeText(getApplicationContext(), "Network Connection Failed", Toast.LENGTH_SHORT);
toast.show();
Looper.loop();// 进入loop中的循环,查看消息队列
e.printStackTrace();
}
if (res != null){
// 将json字符串转为json对象
JSONObject object = JSON.parseObject(res);
System.out.println(object.toJSONString());
// 获取状态码
String statusCode = (String) object.get("statusCode");
// JSONObject person = object.getJSONObject("userInfo");
// String username = person.getString("username");
// System.out.println(username);
// System.out.println(statusCode);
if (statusCode.equals("200")){
// 注册成功
Looper.prepare();
Toast toast=Toast.makeText(getApplicationContext(), "Register Successful", Toast.LENGTH_SHORT);
toast.show();
Intent intent=new Intent(Register.this,MainActivity.class);
//启动
startActivity(intent);
Looper.loop();// 进入loop中的循环,查看消息队列
// 跳转到登录界面
}else{
// 用户名已存在
Looper.prepare();
Toast toast=Toast.makeText(getApplicationContext(), "Username already Existed", Toast.LENGTH_SHORT);
toast.show();
Looper.loop();// 进入loop中的循环,查看消息队列
}
}
}
}).start();
}
}
-
设计逻辑:
- 点击注册按钮的时候,获取输入,然后进行输入校验,对年龄、电话、邮箱进行正则匹配,错误就弹出错误提示信息。然后访问服务器,依据返回的StatusCode代码判断注册状态(有可能是注册成功或者用户名重复)
-
重要函数:
- requestURL函数,主要是使用okhttp的方法访问url然后获取数据
ShowData
登录进去的页面
这个页面现在啥内容都没有(最近太忙)
只有一个跳往修改信息的按钮
代码就不贴了,也没啥看的
ChangeUserInfo
修改信息
package com.example.newprogram4;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.widget.*;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
public class ChangeUserInfo extends AppCompatActivity {
// 除了username,所有先前的数据都从数据库来,username从上个页面传值来
private String previousUsername;
private String previousPassword;
private String previousAge;
private String previousGender;
private String previousTeleno;
private String previousEmail;
private String previousRegisterTime;
public String getPreviousRegisterTime() {
return previousRegisterTime;
}
public void setPreviousRegisterTime(String previousRegisterTime) {
this.previousRegisterTime = previousRegisterTime;
}
public String getPreviousUsername() {
return previousUsername;
}
public void setPreviousUsername(String previousUsername) {
this.previousUsername = previousUsername;
}
public String getPreviousPassword() {
return previousPassword;
}
public void setPreviousPassword(String previousPassword) {
this.previousPassword = previousPassword;
}
public String getPreviousAge() {
return previousAge;
}
public void setPreviousAge(String previousAge) {
this.previousAge = previousAge;
}
public String getPreviousGender() {
return previousGender;
}
public void setPreviousGender(String previousGender) {
this.previousGender = previousGender;
}
public String getPreviousTeleno() {
return previousTeleno;
}
public void setPreviousTeleno(String previousTeleno) {
this.previousTeleno = previousTeleno;
}
public String getPreviousEmail() {
return previousEmail;
}
public void setPreviousEmail(String previousEmail) {
this.previousEmail = previousEmail;
}
private EditText username;
private EditText password;
private EditText age;
private EditText teleno;
private EditText email;
private Switch switchGender;
private String gender = "0"; // 0 是男,1 是女
private TextView registerTime;
public void setGender(String gender) {
this.gender = gender;
}
private final OkHttpClient client = new OkHttpClient();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_change_user_info);
username = findViewById(R.id.username);
password = findViewById(R.id.password);
age = findViewById(R.id.age);
switchGender = findViewById(R.id.gender);
//gender = findViewById(R.id.gender);
teleno = findViewById(R.id.teleno);
email = findViewById(R.id.email);
registerTime = findViewById(R.id.registerTime);
// 获取上一页的username
this.getLastPageValue();
// 从服务器获取信息
this.getUserInfo();
// 监听switch事件
switchGender.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener(){
public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {
if (isChecked) {
// 开启switch,女
setGender("1");
} else {
// 关闭swtich,男
setGender("0");
}
}
});
}
/**
* 访问函数
*/
private String requestURL(String url) throws IOException {
Request request = new Request.Builder().url(url).build();
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
// String res = Objects.requireNonNull(response.body()).string();
// JSONObject jsonObject = JSON.parseObject(res);
return response.body().string();
//JSONObject jsonObject = response.body().string();
//System.out.println(jsonObject);
} else {
Looper.prepare();
Toast toast=Toast.makeText(getApplicationContext(), "Network Connection Failed", Toast.LENGTH_SHORT);
toast.show();
Looper.loop();// 进入loop中的循环,查看消息队列
throw new IOException("Unexpected code " + response);
}
}
private Handler handler = new Handler();
/**
* 从上一页面获取username
*/
private void getLastPageValue(){
Intent intent = getIntent();
this.setPreviousUsername(intent.getStringExtra("username"));
}
/**
* 从服务器获取完数据后将数据渲染到页面上
*/
private void renderData(){
Looper.prepare();
username.setText(getPreviousUsername());
password.setText(getPreviousPassword());
age.setText(getPreviousAge());
teleno.setText(getPreviousTeleno());
email.setText(getPreviousEmail());
handler.post(new Runnable() {
@Override
public void run() {
registerTime.setText(String.format("注册时间 %s", getPreviousRegisterTime()));
}
});
//registerTime.setText(String.format("注册时间 %s",getPreviousRegisterTime()));
// // 渲染注册时间
// new Thread(new Runnable() {
// @Override
// public void run() {
// registerTime.setText(String.format("注册时间 %s",getPreviousRegisterTime()));
// }
// }).start();
// 性别和用户名不能变
switchGender.setClickable(false);
username.setFocusableInTouchMode(false);
if (getPreviousGender().equals("0")){
// 男
switchGender.setChecked(false);
} else {
switchGender.setChecked(true);
}
Looper.loop();// 进入loop中的循环,查看消息队列
}
/**
* 从服务器获取用户数据作为默认值
*/
private void getUserInfo(){
new Thread(new Runnable(){
@Override
public void run() {
String url = String.format("https://www.csgoha.xyz:8888/queryPerson?username=%s",getPreviousUsername());
System.out.println("url : " + url);
System.out.println("连接获取数据...");
String res = null;
try {
res = ChangeUserInfo.this.requestURL(url);
} catch (IOException e) {
Looper.prepare();
Toast toast=Toast.makeText(getApplicationContext(), "Network Connection Failed", Toast.LENGTH_SHORT);
toast.show();
Looper.loop();// 进入loop中的循环,查看消息队列
e.printStackTrace();
}
if (res != null){
// 将json字符串转为json对象
JSONObject object = JSON.parseObject(res);
System.out.println(object.toJSONString());
// 获取状态码
String statusCode = (String) object.get("statusCode");
// JSONObject person = object.getJSONObject("userInfo");
// String username = person.getString("username");
// System.out.println(username);
// System.out.println(statusCode);
if (statusCode.equals("200")){
// 获取旧信息成功
// 解析获取到的JSON并填充到previous数据中
JSONObject person = object.getJSONObject("userInfo");
setPreviousUsername(person.getString("username"));
setPreviousPassword(person.getString("password"));
setPreviousAge(person.getString("age"));
setPreviousGender(person.getString("gender"));
setPreviousTeleno(person.getString("teleno"));
setPreviousEmail(person.getString("email"));
setPreviousRegisterTime(person.getString("time").toString());
// 渲染信息
renderData();
}else{
// 获取旧信息失败
// 该人不存在,不该出现这种情况
Looper.prepare();
Toast toast=Toast.makeText(getApplicationContext(), "Fatal Error! User doesn't Exist", Toast.LENGTH_SHORT);
toast.show();
Looper.loop();// 进入loop中的循环,查看消息队列
}
}
}
}).start();
}
public void onClickChangeUserInfo(View view){
final String username = this.username.getText().toString();
final String password = this.password.getText().toString();
final String age = this.age.getText().toString();
final String teleno = this.teleno.getText().toString();
final String email = this.email.getText().toString();
final String gender = this.gender;
// 正则匹配密码,必须为字母+数字,长度6——12
final String passwordPattern = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,12}$";
// 匹配年龄
final String agePattern = "^((1[0-5])|[1-9])?\\d$";
// 手机号
final String telenoPattern = "^[1](([3][0-9])|([4][5,7,9])|([5][0-9])|([6][6])|([7][3,5,6,7,8])|([8][0-9])|([9][8,9]))[0-9]{8}$";
//email
final String emailPattern = "^\\s*\\w+(?:\\.{0,1}[\\w-]+)*@[a-zA-Z0-9]+(?:[-.][a-zA-Z0-9]+)*\\.[a-zA-Z]+\\s*$";
if (username.equals("")){
// 用户名不能为空
Toast toast=Toast.makeText(getApplicationContext(), "Username Cannot be Empty", Toast.LENGTH_SHORT);
toast.show();
return;
}
if (username.length() > 10){
// 用户名长度不大于10
Toast toast=Toast.makeText(getApplicationContext(), "Username Length Limited 10", Toast.LENGTH_SHORT);
toast.show();
return;
}
if (password.equals("")){
// 密码不能为空
Toast toast=Toast.makeText(getApplicationContext(), "Password Cannot be Empty", Toast.LENGTH_SHORT);
toast.show();
return;
}
if (password.length() > 12){
// 密码长度不大于12
Toast toast=Toast.makeText(getApplicationContext(), "Password Length Limited 12", Toast.LENGTH_SHORT);
toast.show();
return;
}
if (password.length() < 6){
// 密码长度不小于6
Toast toast=Toast.makeText(getApplicationContext(), "Password Length Should be Over 6", Toast.LENGTH_SHORT);
toast.show();
return;
}
if (!password.matches(passwordPattern)){
Toast toast=Toast.makeText(getApplicationContext(), "Password Should Mix Letters and Numbers", Toast.LENGTH_SHORT);
toast.show();
return;
}
if (!age.matches(agePattern)){
Toast toast=Toast.makeText(getApplicationContext(), "Age Inappropriate", Toast.LENGTH_SHORT);
toast.show();
return;
}
// 填了电话再匹配
if (!teleno.matches(telenoPattern) && !teleno.equals("")){
Toast toast=Toast.makeText(getApplicationContext(), "Invalid Teleno", Toast.LENGTH_SHORT);
toast.show();
return;
}
if (!email.matches(emailPattern) && !email.equals("")){
Toast toast=Toast.makeText(getApplicationContext(), "Invalid Email", Toast.LENGTH_SHORT);
toast.show();
return;
}
new Thread(new Runnable(){
@Override
public void run() {
String url = String.format("https://www.csgoha.xyz:8888/updatePerson?username=%s&password=%s&age=%s&teleno=%s&email=%s",username,password,age,teleno,email);
System.out.println("url : " + url);
System.out.println("连接获取数据...");
String res = null;
try {
res = ChangeUserInfo.this.requestURL(url);
} catch (IOException e) {
Looper.prepare();
Toast toast=Toast.makeText(getApplicationContext(), "Network Connection Failed", Toast.LENGTH_SHORT);
toast.show();
Looper.loop();// 进入loop中的循环,查看消息队列
e.printStackTrace();
}
if (res != null){
// 将json字符串转为json对象
JSONObject object = JSON.parseObject(res);
System.out.println(object.toJSONString());
// 获取状态码
String statusCode = (String) object.get("statusCode");
// JSONObject person = object.getJSONObject("userInfo");
// String username = person.getString("username");
// System.out.println(username);
// System.out.println(statusCode);
if (statusCode.equals("200")){
// 注册成功
Looper.prepare();
Toast toast=Toast.makeText(getApplicationContext(), "Update Successful", Toast.LENGTH_SHORT);
toast.show();
Intent intent=new Intent(ChangeUserInfo.this,MainActivity.class);
//启动
startActivity(intent);
Looper.loop();// 进入loop中的循环,查看消息队列
// 跳转到登录界面
}else{
// 用户名已存在
Looper.prepare();
Toast toast=Toast.makeText(getApplicationContext(), "Fatal Error! User doesn't Exist", Toast.LENGTH_SHORT);
toast.show();
Looper.loop();// 进入loop中的循环,查看消息队列
}
}
}
}).start();
}
}
-
设计逻辑:
- 进来的时候在onCreate函数中获取上一页传过来的username,然后依据username去服务器获取该用户数据,并渲染到各个输入框内作为默认信息 (注意:我设计的是username和性别不能修改!在数据库update语句写的时候就没加这俩,所以有的小伙伴如果有修改需求,比如要改性别请去后端的mapper层加上gender)
- 然后对修改的信息做校验,类似于注册部分
-
重要函数:
- getLastPageValue函数,获取从上一个页面的传参(username)
- getUserInfo函数,从服务器获取用户数据作为默认值
- renderData函数,将获取到的数据渲染,TextView组件使用setText()方法;Switch组件使用setChecked()方法
- requestURL函数,主要是使用okhttp的方法访问url然后获取数据
踩的坑
-
Toast:show的时候如果在子线程内,那么必须在上下加上Looper,例如:
Looper.prepare(); Toast toast=Toast.makeText(getApplicationContext(), "Invalid Password or Username", Toast.LENGTH_SHORT); toast.show(); Looper.loop();// 进入loop中的循环,查看消息队列
否则会报错
-
Switch组件需要监听,有点复杂
// 监听switch事件 switchGender.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener(){ public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) { if (isChecked) { // 开启switch,女 setGender("1"); } else { // 关闭swtich,男 setGender("0"); } } });
-
TextView组件不能在子线程中进行渲染,会出错。必须在子线程中使用handler把它搞到主线程里执行
例如:// 主线程 private Handler handler = new Handler(); // 截取自子线程内 handler.post(new Runnable() { @Override public void run() { registerTime.setText(String.format("注册时间 %s", getPreviousRegisterTime())); } });
测试结果(甜点)
真机测试如下:
注册:
注册成功去登陆:
登陆成功:
修改信息:
成功,美滋滋~
五、Github
晚上再弄
服务器端:服务器端SpringBoot
安卓端:安卓
测试APK 可以在 安卓 项目代码中生成
我在最后的代码中加入了一些信息:
- 在showData页面加入了一个按钮和一个图,点击按钮可以显示图片
- 在注册和修改信息的页面加了 确认密码 (这个是必做)
- 在后端代码中加了数据库md5加密(在mapper层中)(这个可以加分)
总结
- 这次实验难到不太难(比起操作系统和编译原理来说),主要是肝,硬肝了三天,主要是Android的东西啥都不会都是现学现卖
- 因为是现学现卖,所以难免有出错的地方,还请大家不要在意或在评论区指正,不胜感激
- 有问题欢迎在评论区提问讨论~
- 好像没啥总结的。。。:)