最近在学Android(初学者吖),写下这个【Android学习之路】系列记录一下自己学习的过程,欢迎阅读~
本文目录
- 目前进度
- 准备阶段
- 开发工具以及方向构思
- 要实现的功能计划
- 第一阶段
- UI展示
- 功能展示
目前进度
① 总体页面框架基本成型,初始界面和登录界面经过一定的美化
② 注册和登录功能已经实现,密码采用MD5算法进行加密存储
③ 能成功连接SQLite数据库并进行数据的CRUD操作
④ 手机验证码登录功能进行至一半(采用的是MobTech平台)
准备阶段
开发工具以及方向构思
我用的开发工具是Android Studio,是谷歌基于IntelliJ IDEA开发的IDE,因为习惯于用IDEA写Java,所以我选择使用用Android Studio来进行安卓开发,而不是Eclipse(而且谷歌已经停止对Eclipse Android开发工具的一切支持)。
附上我用的版本的百度网盘链接:
Android Studio:链接:https://pan.baidu.com/s/1y2uXHEUl0ungSwA2eaAO7A 提取码:twmm
SDK: 链接:https://pan.baidu.com/s/1OdKp84vJnbMcm8ky-L28AQ 提取码:h5zi
模拟器我用的是夜神,使用效果还可以,同样附上链接:
链接:https://pan.baidu.com/s/1JOI0bWH1aOvP1gpc4-79pQ
提取码:odn9
— — — — — — — — — — — — — — — — — —
构思自己要做的项目时,方向是和学校相关的,因为这样也比较贴近我的生活,更加熟悉一些,哈哈。然后本取名鬼才给该项目和APP均取名为:SchoolSystem
,哈哈,不过现在我发现这个名字在手机里显示不全,后半截直接省略号了
要实现的功能计划
功能方面我的计划是:
① 分为教师和学生两种登录方式(密码采用MD5加密),登录后所能使用的功能是不同的(拿【学习】页面来说老师能出题和修改学生的分数等,学生只能查看成绩结果和分析以及做题等)
② 老师注册和登录时有一定的限制(我提前准备好可以注册的手机号,老师注册的时候比学生多出手机短信验证这一限制),然后在登录后学生需进行身份认证,认证成功后可享受全部功能,否则只有【计划】这一页面可供使用
③ 【学习】页面有学生的成绩绘制成的图表分析,以及自己的所有成绩查询(和全班的成绩),然后在该页面会实现一个可以做题的功能(教师在该页面可出题,然后学生在该页面做题)
④ 【计划】这一页面可供大家写下自己的计划(可设置为自己可见和全部人可见)
⑤ 如果能力达到的条件下,完善【论坛】页面,连接云数据库,大家可在其中发表一些内容
第一阶段
UI展示
APP启动后会进入初始的选择登录身份的界面,如下图(是我手机上的截屏):
APP的图标我采用了AS里提供的炒鸡好用的Image Asset功能进行制作,好处是基本能适应各种机型,包括图标是圆形,设计界面如下图:
选择登录身份的初始界面InitActivity
的布局采用了LinearLayout
垂直方向线性布局,页面中有ImageView
组件显示我设计的欢迎那一张图片和底部的图片,然后是“请选择您的身份”和“Copyright”的TextView
组件,下面部分我采用了RelativeLayout
相对布局,并在其中嵌套了一个相对布局用于教师选择和学生选择的位置控制,使其具有更好地适应能力,该activity_init.xml
文件源代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@mipmap/bg1"
android:padding="3dp"
tools:context=".InitActivity">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="@mipmap/welcome"
android:adjustViewBounds="true">
</ImageView>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="8dp"
android:padding="8dp"
android:background="@drawable/translucent2"
android:lineSpacingExtra="4sp"
android:text="请选择您的身份"
android:textAlignment="center"
android:textSize="20sp"
android:textStyle="bold" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="@+id/ChooseTeacher"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/translucent2"
android:layout_toLeftOf="@+id/canzhao"
android:layout_marginRight="45dp"
android:layout_marginTop="50dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/teacher_ico"
android:layout_margin="10dp"
android:src="@mipmap/teacher_ico"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:text="我是教师"
android:layout_centerHorizontal="true"
android:textColor="#576B95"
android:layout_marginTop="5dp"
android:layout_below="@id/teacher_ico"
android:textStyle="bold"/>
</RelativeLayout>
<TextView
android:id="@+id/canzhao"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true" />
<RelativeLayout
android:id="@+id/ChooseStudent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/translucent2"
android:layout_toRightOf="@+id/canzhao"
android:layout_marginLeft="45dp"
android:layout_marginTop="50dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/student_ico"
android:layout_margin="10dp"
android:src="@mipmap/student_ico"/>
<TextView
android:id="@+id/nameLogined"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:text="我是学生"
android:layout_centerHorizontal="true"
android:textColor="#576B95"
android:layout_marginTop="5dp"
android:layout_below="@id/student_ico"
android:textStyle="bold"/>
</RelativeLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/translucent2"
android:lineSpacingExtra="4sp"
android:text="Copyright © 2020 Henry. All Rights Reserved"
android:layout_marginTop="300dp"
android:textAlignment="center"
android:textSize="12sp"
android:textStyle="bold" />
<ImageView
android:id="@+id/welcomebottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:src="@mipmap/welcomebottom"
android:adjustViewBounds="true">
</ImageView>
</RelativeLayout>
</LinearLayout>
emm,这个代码的颜色有些刺眼哈哈,接下来分别是教师和学生登录页面(因为手机短信验证功能还没调试成功,所以教师页面暂时也是用户名+密码的注册和登录形式):
成功登录后跳转一个新的Activity,其中能通过点击底部的导航栏在4个Fragment中切换
(暂时还未设计出其他三个页面的布局框架,目前初步定下【我的】页面):
在【我的】页面中,点击【关于】可跳转至【关于我】页面,里面简单写了一点我的联系方式,然后可以通过点击图标直接拨打电话或发送短信(需要同意相应的权限):
该【关于我】页面的调用拨号和发短信功能实现具体代码如下(我在这里把我的号码改成123456了嘻嘻):
package com.henry.schoolsystem.ui.me;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageButton;
import com.henry.schoolsystem.R;
public class AboutActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_about);
ImageButton imageButton = (ImageButton) findViewById(R.id.imageButton_phone); //获取电话图片按钮
ImageButton imageButton1 = (ImageButton) findViewById(R.id.imageButton_sms); //获取短信图片按钮
imageButton.setOnClickListener(l); //为电话图片按钮设置单击事件
imageButton1.setOnClickListener(l);//为短信图片按钮设置单击事件
}
//创建监听事件对象
View.OnClickListener l = new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(); //创建Intent对象
switch (v.getId()) { //根据ImageButton组件的id进行判断
case R.id.imageButton_phone: //如果是电话图片按钮
intent.setAction(intent.ACTION_DIAL); //调用拨号面板
intent.setData(Uri.parse("tel:123456")); //设置要拨打的号码
startActivity(intent); //启动Activity
break;
case R.id.imageButton_sms: //如果是短信图片按钮
intent.setAction(intent.ACTION_SENDTO); //调用发送短信息
intent.setData(Uri.parse("smsto:123456")); //设置要发送的号码
intent.putExtra("sms_body", "您好!"); //设置要发送的信息内容
startActivity(intent); //启动Activity
}
}
};
}
功能展示
首先是注册/登录界面功能:
写入数据库需要使用SQLiteOpenHelper
类,该类是一个抽象类,需要创建该类的派生类,我这里是DBOpenHelper
,源代码如下:
package com.henry.schoolsystem.ui.login;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class DBOpenHelper extends SQLiteOpenHelper {
public static final String USER_INFO = "userInfo";
public static final String USER_LOGIN = "userLogin";
public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, null, version); //重写构造方法并设置factory为null
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS " + USER_INFO + "( "
+ "_id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ "userName VARCHAR, "
+ "nickName VARCHAR, "
+ "sex VARCHAR, "
+ "qq VARCHAR, "
+ "wechat VARCHAR, "
+ "motto VARCHAR "
+ ")");
db.execSQL("CREATE TABLE IF NOT EXISTS " + USER_LOGIN + "( "
+ "_id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ "userName VARCHAR, "
+ "password VARCHAR"
+ ")");
}
@Override
// 重写基类的onUpgrade()方法,以便数据库版本更新
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//提示版本更新并输出旧版本信息与新版本信息
System.out.println("---版本更新-----" + oldVersion + "--->" + newVersion);
}
}
注册/登录页面的Activity中的代码如下(以学生页面SLoginActivity
为例):
package com.henry.schoolsystem.ui.login;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.henry.schoolsystem.R;
import com.henry.schoolsystem.SMainActivity;
import com.henry.schoolsystem.TMainActivity;
import com.henry.schoolsystem.utils.MD5Utils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class SLoginActivity extends AppCompatActivity {
private DBOpenHelper dbOpenHelper; //定义DBOpenHelper,用于与数据库连接
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_slogin);
//创建DBOpenHelper对象,指定名称、版本号并保存在databases目录下
dbOpenHelper = new DBOpenHelper(SLoginActivity.this, "student.db", null, 1);
final EditText usernameEditText = (EditText) findViewById(R.id.add_username); //获取添加用户名的编辑框
final EditText passwordEditText = (EditText) findViewById(R.id.add_password); //获取添加密码的编辑框
final Button btn_Save = (Button) findViewById(R.id.register2); //获取注册按钮
final Button btn_Login = (Button) findViewById(R.id.login2); //获取登录按钮
//登录
btn_Login.setOnClickListener(new View.OnClickListener() { //单击登录按钮查看是否有该用户以及密码是否正确
@Override
public void onClick(View v) {
SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
String key = usernameEditText.getText().toString(); //获取输入的登录名
//用Cursor游标进行查询
Cursor cursor = db.query("userLogin", null
, "userName = ?", new String[]{key}, null, null, null);
ArrayList<Map<String, String>> resultList = new ArrayList<Map<String, String>>(); //创建ArrayList对象,用于保存查询出的结果
while (cursor.moveToNext()) { // 遍历Cursor结果集
Map<String, String> map = new HashMap<>(); // 将结果集中的数据存入HashMap中
// 取出查询记录中第2、3列的值
if (resultList.size() != 0) break;
map.put("userName", cursor.getString(1));
map.put("password", cursor.getString(2));//将查询出的数据存入ArrayList中
resultList.add(map);
}
if (resultList.size() == 0) { //如果数据库中没有数据
Toast.makeText(SLoginActivity.this, "用户名不存在,请重新输入或注册!", Toast.LENGTH_SHORT).show();
cursor.close();
} else {
// 否则比较password是否一致
String passwordIn = passwordEditText.getText().toString(); //获取输入的密码
String passwordNeed = cursor.getString(cursor.getColumnIndex("password")); //获取需要填写的密码
if (passwordNeed.equals(MD5Utils.md5(passwordIn))) { //只是比较内容,不能用==
Toast.makeText(SLoginActivity.this, "登录成功!"+key+",欢迎您~", Toast.LENGTH_LONG).show();
cursor.close();
Intent intent = new Intent(SLoginActivity.this, SMainActivity.class); //登录成功跳转至学生端
startActivity(intent);
} else {
Toast.makeText(SLoginActivity.this, "密码错误,请重新输入!", Toast.LENGTH_SHORT).show();
}
}
}
});
//注册
btn_Save.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String username = usernameEditText.getText().toString(); //获取填写的用户名
String password = passwordEditText.getText().toString(); //获取填写的密码
if (username.equals("")||password.equals("")){ //如果填写的用户名或密码为空时
Toast.makeText(SLoginActivity.this, "用户名或密码为空,请重新填写", Toast.LENGTH_SHORT).show();
}else {
// 调用insertData()方法,实现插入用户数据
insertData(dbOpenHelper.getReadableDatabase(), username, password);
// 显示提示信息
dbOpenHelper.close();
Toast.makeText(SLoginActivity.this, "注册成功!可以登录啦", Toast.LENGTH_SHORT).show();
}
}
});
}
//创建insertData()方法实现插入数据
private void insertData(SQLiteDatabase readableDatabase, String username, String password) {
ContentValues values = new ContentValues();
values.put("userName", username); //保存用户名
values.put("password", MD5Utils.md5(password)); //保存密码
try {
readableDatabase.insert("userLogin", null, values);//执行插入操作
}
catch (SQLException e) {
Log.e("SQLiteDatabase", "Error inserting " + values, e);
}
}
}
当填入的用户名或密码中有一个为空时,注册失败同时通过Toast弹出消息提示框,注册成功并登录后会弹出一个欢迎的消息提示框,如下图:
对了,上一步中,如果密码输入错误或是用户名不存在会有如下的登录失败提示:
然后该注册的密码在数据库中是经过MD5算法加密后存储的,MD5加密算法的java类如下:
package com.henry.schoolsystem.utils;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MD5Utils {
// md5 加密的算法
public static String md5(String text){
MessageDigest digest = null;
try {
//获取数据指纹对象
digest = MessageDigest.getInstance("md5");
//字节数组
byte[] result = digest.digest(text.getBytes());
//16进制转换
StringBuffer sb = new StringBuffer();
//获取所有字节进行转换
for (byte b: result){
//使用『与算法』,java使用unicode字符,所以每个字符占位两个
// 需要与两位16进制最大值进行与运算,获取number值
int number = b & 0xff;
//number值转换字符串
String hex = Integer.toHexString(number);
if (hex.length() == 1){
//若转换后的字符长度等于1则进行字符串拼接
sb.append("0"+hex);
}else {
sb.append(hex);
}
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
//发送异常return空字符串
return "";
}
}
}
查看SQLite数据库中的相关记录(我使用的Navicat Premium 15):
相关教程可参考:快速激活Navicat Premium 12
打开后连接我从AS中导出的数据库student.db,查看userLogin表:
— — — — — — — — — — — — — — — — — — — — — — — — — — — —
短信验证功能可以有很多平台使用,我选择的是MobTech(免费的),可由此查看相关文档:SMSSDK for Android
需要在AndroidManifest.xml
中添加以下权限:
以及在项目根目录的build.gradle
中添加依赖,不过你可能还需要像我一样添加上两个库:
以及在使用SMSSDK模块的build.gradle中,添加MobSDK插件和扩展:
// 添加插件
apply plugin: 'com.mob.sdk'
// 在MobSDK的扩展中注册SMSSDK的相关信息
MobSDK {
appKey "申请Mob的appkey"
appSecret "申请Mob的AppSecret"
SMSSDK {}
}
这些设置在文档中都有:SMSSDK for Android,写的挺详细的,该短信验证功能的具体代码实现和调用在下一阶段会完成~
第一阶段主要是构造好了基本的大的框架和初级的登录功能,数据库的详细的各个关系模式将在最近几天设计出来,目前只有userLogin
和userInfo
两个表,每个页面的设计也会在第二阶段弄出来,不过刚学不久,在很多地方遇到了障碍,在努力克服中,继续加油叭!
下一阶段会在这周更新出来,然后在本文附上链接~
对了,安卓连接SQLite数据库的一些具体介绍我会在近期发表一篇博客~
(ps:因为目前代码中一些地方写的还不够好,在最终项目完成后会将项目源码上传至GitHub,目前发表出的代码中如有问题和需改进的地方希望大家积极指出~)