Android:答題APP的設計與實現(mysql+jsp+Android) Android:答題APP的設計與實現(mysql+jsp+Android)
Android:答題APP的設計與實現(mysql+jsp+Android)
還沒有整理完,待續……
學校開了Android課,最後讓交一個大作業。正好拿來練練手,記錄下思路。也希望能給有需要的朋友們一些幫助。恩,純小白教程,大神們可以繞路了。
作業的題目是這樣的:
考試APP系統:
1)要求有使用者登陸功能:從遠端伺服器進行登陸驗證。
2)要有考試測試介面,主要是選擇、判斷、簡答題目測試。
3)要有統計成績介面和錯題顯示介面。
評分標準:
1、介面設計佔評分的30%
2、系統執行正確; 功能完善;工作量充分; 系統實現有一定的技術的難度。50%
3、要求有適當的系統主要模組的文件說明和程式碼註釋。
4、直接將資料庫檔案(資料庫一定要備份成SQL語句格式,指明資料庫)和專案檔案提交。乍一看挺簡單的,真要研究起來,寫的實用一些,還真有點不知如何下手,那跟著我的思路,一起來吧!恩,不想看思路的,可以直接戳Android原始碼下載原始碼來看了。
下載地址:
如果不嫌棄的話,可以直接下載。
沒有積分的話……幫忙點個贊,然後留個郵箱,我發給你哈~~
https://download.csdn.net/download/zheng_weichao/10310185
功能需求設計:
- 登入註冊
- 答題:選擇題,判斷題,簡答題
- 答題得分計算
- 錯題檢視
最後效果
總體思路
總體思路是這樣的,App通過http連線伺服器,進行登入或者註冊服務,登入成功之後,伺服器查詢資料庫並以json的形式返回試題資料。App接收資料之後,解析並存到本地資料庫,然後展示給使用者答題。點選交卷按鈕後,進行評分並可進行錯題檢視。內容比較雜亂,大家可以根據目錄來快速檢視自己需要或者感興趣的地方。
資料庫設計
首先,就登入註冊的功能來說,得先有一個使用者表,包含使用者名稱,密碼,id號這些基本的內容。我在這裡又加了一個許可權欄位,用來返回狀態。(設定許可權欄位,方便日後進行擴充套件,可設定用不同數字代表不同等級或身份)
tbl_user_info
欄位 | 型別 | 含義 |
---|---|---|
id | int(自增長主鍵) | 使用者唯一識別符號 |
username | varchar | 使用者 |
password | varchar | 密碼 |
perssion | varchar(預設為0) | 許可權 |
其次,就是題庫了。為了使專案具有實用性,減小安裝包體積,便於更新修正,題庫同樣也需要放在伺服器上才合適。
tbl_question
欄位 | 型別 | 含義 |
---|---|---|
id | int(自增長主鍵) | 題目唯一標識id |
q_type: | int | 題型: 1:選擇 2:判斷 3:簡答 |
title: | varchar | 問題 |
optionA: | varchar | 選項A |
optionB: | varchar | 選項B |
optionC: | varchar | 選項C |
optionD: | varchar | 選項D |
tips: | varchar | 提示 |
answer: | varchar | 答案 |
explain: | varchar | 解釋 |
write_answer: | varchar | 你的答案 |
jsp程式
jsp依賴了兩個jar包,分別是連線mysql的驅動:mysql-connector-java-5.1.34-bin還有生成json用的:json 。為了減少程式碼的耦合性,這裡採用MVC模式進行設計。(自以為是MVC)。目錄結構如下:
登入註冊
連線資料庫
資料庫操作類,封裝了連線,查詢,關閉資料庫的方法。大家如果使用這部分程式碼,別忘了把資料庫連線常量改成自己的。
//****連線資料庫**DBManager***
public class DBManager {
// 資料庫連線常量
public static final String DRIVER = "com.mysql.jdbc.Driver";
public static final String USER = "root";
public static final String PASS = "root";
public static final String URL = "jdbc:mysql://localhost:3306/shop";
// 靜態成員,支援單態模式
private static DBManager per = null;
private Connection conn = null;
private Statement stmt = null;
// 單態模式-懶漢模式
private DBManager() {
}
public static DBManager createInstance() {
if (per == null) {
per = new DBManager();
per.initDB();
}
return per;
}
// 載入驅動
public void initDB() {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (Exception e) {
e.printStackTrace();
}
}
// 連線資料庫,獲取控制代碼+物件
public void connectDB() {
System.out.println("Connecting to database...");
try {
conn = DriverManager.getConnection(URL, USER, PASS);
stmt = conn.createStatement();
} catch (SQLException e) {
e.printStackTrace();
}
System.out.println("SqlManager:Connect to database successful.");
}
// 關閉資料庫 關閉物件,釋放控制代碼
public void closeDB() {
System.out.println("Close connection to database..");
try {
stmt.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
System.out.println("Close connection successful");
}
// 查詢
public ResultSet executeQuery(String sql) {
ResultSet rs = null;
try {
rs = stmt.executeQuery(sql);
} catch (SQLException e) {
e.printStackTrace();
}
return rs;
}
// 增添/刪除/修改
public int executeUpdate(String sql) {
int ret = 0;
try {
ret = stmt.executeUpdate(sql);
} catch (SQLException e) {
e.printStackTrace();
}
return ret;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
登入
客戶端提交過來一個使用者名稱,一個密碼,jsp連線資料庫查詢,如果兩者都符合,返回登入成功資訊,否則返回登入失敗資訊。(我這裡用許可權來代表,當權限>-1即為登入成功)。
註冊
客戶端同樣提交過來一個使用者名稱,一個密碼,但是需要首先查詢資料庫,看看該使用者名稱是否已被註冊,若沒有,則執行資料庫插入操作。成功則返回註冊成功,否則返回失敗資訊。
//****服務程式碼****
public class StartService {
/**
* 登入方法
* @param username
* @param password
* @return 登入成功與否
*/
public HashMap<String, String> login(String username, String password) {
HashMap<String, String> hashMap = new HashMap<String, String>();
// 獲取Sql查詢語句
String logSql = "select perssion from userinfo where username ='"
+ username + "' and password ='" + password + "'";
System.out.println(logSql);
// 獲取DB物件
DBManager sql = DBManager.createInstance();
sql.connectDB();
hashMap.put("permission", "-1");
hashMap.put("username", username);
// 操作DB物件
try {
ResultSet rs = sql.executeQuery(logSql);
if (rs.next()) {
hashMap.put("permission", rs.getInt(1) + "");
System.out.print("許可權===" + rs.getInt(1) + "\t");
sql.closeDB();
return hashMap;
}
} catch (SQLException e) {
e.printStackTrace();
}
sql.closeDB();
return hashMap;
}
/**
* 註冊方法
* @param username
* @param password
* @return 註冊成功與否
*/
public HashMap<String, String> register(String username, String password) {
HashMap<String, String> hashMap = new HashMap<String, String>();
hashMap.put("username", username);
hashMap.put("msg", "notok");
if (this.namerepeat(username)) {
hashMap.put("msg", "rename");
} else {
// 獲取Sql查詢語句
String regSql = "insert into userinfo(username,password) values('"
+ username + "','" + password + "')";
System.out.println(regSql);
// 獲取DB物件
DBManager sql = DBManager.createInstance();
sql.connectDB();
int ret = sql.executeUpdate(regSql);
if (ret != 0) {
hashMap.put("msg", "ok");
sql.closeDB();
return hashMap;
}
sql.closeDB();
}
return hashMap;
}
/**
* 檢測該賬戶是否已經註冊
*
* @param username
* @return 註冊狀態
*/
public Boolean namerepeat(String username) {
String checkSql = "select username from userinfo where username ='"
+ username + "'";
// 獲取Sql查詢語句
System.out.println(checkSql);
// 獲取DB物件
DBManager sql = DBManager.createInstance();
sql.connectDB();
// 操作DB物件
try {
ResultSet rs = sql.executeQuery(checkSql);
if (rs.next()) {
sql.closeDB();
return true;
}
} catch (SQLException e) {
e.printStackTrace();
}
sql.closeDB();
return false;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
登入註冊部分的程式碼基本一樣,只把 serv.login變成serv.reglet就可以了。
//****登入程式碼****
String username = request.getParameter("username");
String password = request.getParameter("password");
username = new String(username.getBytes("ISO-8859-1"), "UTF-8");
System.out.println("客戶端引數:" + username + " ====== " + password);
// 新建服務物件,預設許可權為-1(未登入狀態)
StartService serv = new StartService();
int permission = -1;
HashMap<String, String> logiii = serv.login(username, password);
String a = logiii.get("username");
// 登陸驗證處理(許可權>-1為登入成功)
if ("-1".equals(logiii.get("permission"))) {
// 登入失敗
System.out.print("登入失敗Failed");
} else {
// 登陸成功
permission = Integer.parseInt(logiii.get("permission"));
}
JSONObject jsonObj = new JSONObject();
try {
jsonObj.put("username", username);
jsonObj.put("permission", permission);
System.out.println(jsonObj);
// 返回資訊到客戶端
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.print(jsonObj);
out.flush();
out.close();
} catch (JSONException e) {
e.printStackTrace();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
json格式設計
註冊結果
{
"username": "001",
"msg": "ok"
}
- 1
- 2
- 3
- 4
登入結果
{
"username": "001",
"permission": 0
}
- 1
- 2
- 3
- 4
答題
json格式設計
{
"status": ok, //連線狀態
"code": "200" //錯誤程式碼
"messages": [ //題目內容
{
"_id": 1, //題目id
"title": "1+1=?" //題目
"q_type": 0, //題目型別
"optionA": "1", //A選項
"optionB": "2", //B選項
"optionC": "3", //C選項
"optionD": "4", //D選項
"tips”:"這麼簡單還要提示?", //提示
"answer": "B", //答案
},
{
"_id": 2,
"title": "2+2=?"
"q_type": 0,
"optionA": "1",
"optionB": "2",
"optionC": "3",
"optionD": "4",
"tips":"這麼簡單還要提示?",
"answer": "D",
}
],
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
Android程式設計
恩,這才是全文的重點好不好?畢竟這是Android課的大作業誒。開發工具Android studio,依賴的庫比較多,所以程式碼相當簡單。話不多說,開擼了!
應該有哪些介面?
- SplashActivity(啟動頁面):展示下logo,還可以做一些耗時操作。
- LoginActivity(登入頁面):用來登入
- SignupActivity(註冊頁面):用來註冊
- AnswerActivity(答題頁面):答題,上面設定的viewpager繫結的fragment。
- GradeActivity(得分頁面):答題結束後用來展示分數。
AnswerFragment:繫結在AnswerActivity之上,根據題目數量動態建立
關係圖大概就是下面這個樣子。
用到了哪些知識?依賴了什麼第三方框架?
恩……這部分是寫在文件裡的,想了想,一併拿出來吧。工程聯網部分依賴以okhttp為基礎的OkGo框架,資料庫部分採用GreenDao框架。其他的,都是特別基礎的一些知識,大致如下:
- 頁面intent跳轉,引數的傳遞
- 聯網操作以及json資料的解析
- sqlite資料庫的連線以及增刪改查
- viewpager與fragment的繫結。
- 計時器的設計實現
- 主題樣式的自定義設定
- 自定義對話方塊
- 背景選擇器selector的使用
- 頁面跳轉動畫效果的設計與實現
- listview資料填充及優化
頁面詳解
BaseActivity(Activity基類)
為了讓增強程式碼可讀性,減少重複程式碼,所以把一些共性程式碼做了抽取。
public abstract class BaseActivity extends FragmentActivity implements View.OnClickListener {
@Override
protected final void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//得到佈局檔案
setContentView(getLayoutId());
//初始化
initView();
initData();
initListener();
}
/**
* @return 佈局檔案id
*/
abstract int getLayoutId();
/**
* 初始化View
*/
void initView() {
}
/**
* 初始化介面資料
*/
void initData() {
}
/**
* 繫結監聽器與介面卡
*/
void initListener() {
}
/**
* 對統一的按鈕進行統一處理
*
* @param v 點選的View
*/
@Override
public void onClick(View v) {
switch (v.getId()) {
default:
processClick(v);
break;
}
}
/**
* 點選事件
*
* @param v 點選的View
*/
void processClick(View v) {
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
SplashActivity(啟動頁面)
繫結activity_splash佈局檔案,延時2秒鐘跳轉主頁面。
public class SplashActivity extends BaseActivity {
@Override
int getLayoutId() {
return R.layout.activity_splash;
}
@Override
void initData() {
//延時2s,跳轉。
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//跳轉主頁面
Intent intent = new Intent(SplashActivity.this, MainActivity.class);
startActivity(intent);
finish();
}
}, 2000);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
LoginActivity(登入頁面)
java程式碼LoginActivity.java
public class LoginActivity extends BaseActivity {
private static final int REQUEST_SIGNUP = 0;
EditText et_username;
EditText et_password;
Button btn_login;
TextView tv_signup;
@Override
int getLayoutId() {
return R.layout.activity_login;
}
@Override
void initView() {
//通過id找控制元件
et_username = (EditText) findViewById(R.id.input_username);
et_password = (EditText) findViewById(R.id.input_password);
btn_login = (Button) findViewById(R.id.btn_login);
tv_signup = (TextView) findViewById(R.id.link_signup);
}
@Override
void initListener() {
//登入按鈕,註冊連結設定點選監聽事件
btn_login.setOnClickListener(this);
tv_signup.setOnClickListener(this);
}
@Override
void processClick(View v) {
switch (v.getId()) {
//點選登入按鈕,執行登入操作
case R.id.btn_login:
login();
break;
//如果點選了註冊連結,則跳轉到註冊頁面
case R.id.link_signup:
Intent intent = new Intent(getApplicationContext(), SignupActivity.class);
startActivityForResult(intent, REQUEST_SIGNUP);
finish();
//動畫效果
overridePendingTransition(R.anim.push_left_in, R.anim.push_left_out);
break;
default:
break;
}
}
/**
* 登入方法
*/
public void login() {
//如果內容不合法,則直接返回,顯示錯誤。
if (!validate()) {
onLoginFailed();
return;
}
//輸入合法,將登入按鈕置為不可用,顯示圓形進度對話方塊
btn_login.setEnabled(false);
final ProgressDialog progressDialog = new ProgressDialog(LoginActivity.this, R.style.AppTheme_Dark_Dialog);
progressDialog.setIndeterminate(true);
progressDialog.setMessage("登入中...");
progressDialog.show();
//獲取輸入內容
String username = et_username.getText().toString().trim();
String password = et_password.getText().toString().trim();
//聯網,獲取資料
OkGo.get(CONFIG.URL_LOGIN)
.params("username", username)
.params("password", password)
.execute(new StringCallback() {
@Override
public void onSuccess(String s, Call call, Response response) {
Gson gson = new Gson();
JsonLoginBean jsonLoginBean = gson.fromJson(s, JsonLoginBean.class);
//如果得到許可權>0,則登入成功。
if (jsonLoginBean.getPermission() > 0) {
Log.e("zwc", "onSuccess: ===");
onLoginSuccess();
progressDialog.dismiss();
} else {
onLoginFailed();
progressDialog.dismiss();
}
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_SIGNUP) {
if (resultCode == RESULT_OK) {
this.finish();
}
}
}
/**
* 重寫返回鍵的返回方法
*/
@Override
public void onBackPressed() {
// Disable going back to the MainActivity
moveTaskToBack(true);
}
/**
* 登入成功
*/
public void onLoginSuccess() {
btn_login.setEnabled(true);
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
startActivity(intent);
finish();
}
/**
* 登入失敗
*/
public void onLoginFailed() {
Toast.makeText(getBaseContext(), "登陸失敗", Toast.LENGTH_LONG).show();
btn_login.setEnabled(true);
}
/**
* @return 判斷是否賬號密碼是否合法
*/
public boolean validate() {
//設定初值,預設為合法
boolean valid = true;
//獲取輸入內容
String email = et_username.getText().toString().trim();
String password = et_password.getText().toString().trim();
//判斷賬號
if (email.isEmpty()) {
et_username.setError("請輸入你的賬號");
valid = false;
} else {
et_username.setError(null);
}
//判斷密碼
if (password.isEmpty()) {
et_password.setError("請輸入你的密碼");
valid = false;
} else {
et_password.setError(null);
}
return valid;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
佈局