1. 程式人生 > >Android:答題APP的設計與實現(mysql+jsp+Android) Android:答題APP的設計與實現(mysql+jsp+Android)

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)。目錄結構如下:
jsp目錄結構

登入註冊

連線資料庫

資料庫操作類,封裝了連線,查詢,關閉資料庫的方法。大家如果使用這部分程式碼,別忘了把資料庫連線常量改成自己的。

//****連線資料庫**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之上,根據題目數量動態建立

    關係圖大概就是下面這個樣子。
    答題app設計

用到了哪些知識?依賴了什麼第三方框架?

恩……這部分是寫在文件裡的,想了想,一併拿出來吧。工程聯網部分依賴以okhttp為基礎的OkGo框架,資料庫部分採用GreenDao框架。其他的,都是特別基礎的一些知識,大致如下:

  1. 頁面intent跳轉,引數的傳遞
  2. 聯網操作以及json資料的解析
  3. sqlite資料庫的連線以及增刪改查
  4. viewpager與fragment的繫結。
  5. 計時器的設計實現
  6. 主題樣式的自定義設定
  7. 自定義對話方塊
  8. 背景選擇器selector的使用
  9. 頁面跳轉動畫效果的設計與實現
  10. 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

佈局