還沒有整理完,,待續(xù)…… 學(xué)校開了Android課,,最后讓交一個大作業(yè)。正好拿來練練手,,記錄下思路,。也希望能給有需要的朋友們一些幫助。恩,,純小白教程,,大神們可以繞路了。 作業(yè)的題目是這樣的: 考試APP系統(tǒng): 1)要求有用戶登陸功能:從遠(yuǎn)程服務(wù)器進(jìn)行登陸驗(yàn)證,。 評分標(biāo)準(zhǔn): 1,、界面設(shè)計(jì)占評分的30% 乍一看挺簡單的,,真要研究起來,,寫的實(shí)用一些,還真有點(diǎn)不知如何下手,,那跟著我的思路,,一起來吧!恩,,不想看思路的,可以直接戳Android源碼下載源碼來看了,。 功能需求設(shè)計(jì): 登錄注冊 最后效果 總體思路 總體思路是這樣的,,App通過http連接服務(wù)器,,進(jìn)行登錄或者注冊服務(wù),登錄成功之后,,服務(wù)器查詢數(shù)據(jù)庫并以json的形式返回試題數(shù)據(jù),。App接收數(shù)據(jù)之后,解析并存到本地?cái)?shù)據(jù)庫,,然后展示給用戶答題,。點(diǎn)擊交卷按鈕后,進(jìn)行評分并可進(jìn)行錯題查看,。內(nèi)容比較雜亂,,大家可以根據(jù)目錄來快速查看自己需要或者感興趣的地方。 數(shù)據(jù)庫設(shè)計(jì) 首先,,就登錄注冊的功能來說,,得先有一個用戶表,包含用戶名,,密碼,,id號這些基本的內(nèi)容。我在這里又加了一個權(quán)限字段,,用來返回狀態(tài),。(設(shè)置權(quán)限字段,方便日后進(jìn)行擴(kuò)展,,可設(shè)置用不同數(shù)字代表不同等級或身份) tbl_user_info 其次,,就是題庫了。為了使項(xiàng)目具有實(shí)用性,,減小安裝包體積,,便于更新修正,,題庫同樣也需要放在服務(wù)器上才合適。 tbl_question jsp程序 jsp依賴了兩個jar包,,分別是連接mysql的驅(qū)動:mysql-connector-java-5.1.34-bin還有生成json用的:json ,。為了減少代碼的耦合性,這里采用MVC模式進(jìn)行設(shè)計(jì),。(自以為是MVC),。目錄結(jié)構(gòu)如下: 登錄注冊 1.連接數(shù)據(jù)庫 數(shù)據(jù)庫操作類,封裝了連接,,查詢,,關(guān)閉數(shù)據(jù)庫的方法。大家如果使用這部分代碼,,別忘了把數(shù)據(jù)庫連接常量改成自己的,。 //****連接數(shù)據(jù)庫**DBManager***public class DBManager { // 數(shù)據(jù)庫連接常量 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'; // 靜態(tài)成員,支持單態(tài)模式 private static DBManager per = null; private Connection conn = null; private Statement stmt = null; // 單態(tài)模式-懶漢模式 private DBManager() { } public static DBManager createInstance() { if (per == null) { per = new DBManager(); per.initDB(); } return per; } // 加載驅(qū)動 public void initDB() { try { Class.forName('com.mysql.jdbc.Driver'); } catch (Exception e) { e.printStackTrace(); } } // 連接數(shù)據(jù)庫,,獲取句柄+對象 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.'); } // 關(guān)閉數(shù)據(jù)庫 關(guān)閉對象,,釋放句柄 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; }} 2.登錄 客戶端提交過來一個用戶名,一個密碼,,jsp連接數(shù)據(jù)庫查詢,,如果兩者都符合,返回登錄成功信息,,否則返回登錄失敗信息,。(我這里用權(quán)限來代表,當(dāng)權(quán)限>-1即為登錄成功),。 3.注冊 客戶端同樣提交過來一個用戶名,,一個密碼,但是需要首先查詢數(shù)據(jù)庫,,看看該用戶名是否已被注冊,,若沒有,則執(zhí)行數(shù)據(jù)庫插入操作,。成功則返回注冊成功,,否則返回失敗信息。 //****服務(wù)代碼****public class StartService { /** * 登錄方法 * @param username * @param password * @return 登錄成功與否 */ public HashMap 登錄注冊部分的代碼基本一樣,,只把 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('客戶端參數(shù):' + username + ' ====== ' + password); // 新建服務(wù)對象,默認(rèn)權(quán)限為-1(未登錄狀態(tài)) StartService serv = new StartService(); int permission = -1; HashMap 4.json格式設(shè)計(jì) 注冊結(jié)果 { 'username': '001', 'msg': 'ok'} 登錄結(jié)果 { 'username': '001', 'permission': 0} 答題 json格式設(shè)計(jì) { 'status': ok, //連接狀態(tài) 'code': '200' //錯誤代碼 'messages': [ //題目內(nèi)容 { '_id': 1, //題目id 'title': '1+1=,?' //題目 'q_type': 0, //題目類型 'optionA': '1', //A選項(xiàng) 'optionB': '2', //B選項(xiàng) 'optionC': '3', //C選項(xiàng) 'optionD': '4', //D選項(xiàng) 'tips”:'這么簡單還要提示,?', //提示 'answer': 'B', //答案 }, { '_id': 2, 'title': '2+2=?' 'q_type': 0, 'optionA': '1', 'optionB': '2', 'optionC': '3', 'optionD': '4', 'tips':'這么簡單還要提示?', 'answer': 'D', } ],} Android程序設(shè)計(jì) 恩,這才是全文的重點(diǎn)好不好,?畢竟這是Android課的大作業(yè)誒,。開發(fā)工具Android studio,依賴的庫比較多,,所以代碼相當(dāng)簡單,。話不多說,開擼了,! 應(yīng)該有哪些界面,? SplashActivity(啟動頁面):展示下logo,還可以做一些耗時操作。 關(guān)系圖大概就是下面這個樣子,。 用到了哪些知識?依賴了什么第三方框架,? 恩……這部分是寫在文檔里的,想了想,,一并拿出來吧,。工程聯(lián)網(wǎng)部分依賴以okhttp為基礎(chǔ)的OkGo框架,數(shù)據(jù)庫部分采用GreenDao框架,。其他的,,都是特別基礎(chǔ)的一些知識,大致如下:
頁面詳解 BaseActivity(Activity基類) 為了讓增強(qiáng)代碼可讀性,減少重復(fù)代碼,,所以把一些共性代碼做了抽取,。 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() { } /** * 初始化界面數(shù)據(jù) */ void initData() { } /** * 綁定監(jiān)聽器與適配器 */ void initListener() { } /** * 對統(tǒng)一的按鈕進(jìn)行統(tǒng)一處理 * * @param v 點(diǎn)擊的View */ @Override public void onClick(View v) { switch (v.getId()) { default: processClick(v); break; } } /** * 點(diǎn)擊事件 * * @param v 點(diǎn)擊的View */ void processClick(View v) { }} SplashActivity(啟動頁面) 綁定activity_splash布局文件,延時2秒鐘跳轉(zhuǎn)主頁面,。 public class SplashActivity extends BaseActivity { @Override int getLayoutId() { return R.layout.activity_splash; } @Override void initData() { //延時2s,,跳轉(zhuǎn)。 new Handler().postDelayed(new Runnable() { @Override public void run() { //跳轉(zhuǎn)主頁面 Intent intent = new Intent(SplashActivity.this, MainActivity.class); startActivity(intent); finish(); } }, 2000); }} 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() { //登錄按鈕,,注冊鏈接設(shè)置點(diǎn)擊監(jiān)聽事件 btn_login.setOnClickListener(this); tv_signup.setOnClickListener(this); } @Override void processClick(View v) { switch (v.getId()) { //點(diǎn)擊登錄按鈕,,執(zhí)行登錄操作 case R.id.btn_login: login(); break; //如果點(diǎn)擊了注冊鏈接,則跳轉(zhuǎn)到注冊頁面 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() { //如果內(nèi)容不合法,,則直接返回,,顯示錯誤。 if (!validate()) { onLoginFailed(); return; } //輸入合法,,將登錄按鈕置為不可用,,顯示圓形進(jìn)度對話框 btn_login.setEnabled(false); final ProgressDialog progressDialog = new ProgressDialog(LoginActivity.this, R.style.AppTheme_Dark_Dialog); progressDialog.setIndeterminate(true); progressDialog.setMessage('登錄中...'); progressDialog.show(); //獲取輸入內(nèi)容 String username = et_username.getText().toString().trim(); String password = et_password.getText().toString().trim(); //聯(lián)網(wǎng),,獲取數(shù)據(jù) 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); //如果得到權(quán)限>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() { //設(shè)置初值,,默認(rèn)為合法 boolean valid = true; //獲取輸入內(nèi)容 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; }} 布局文件activity_login.xml SignupActivity(注冊頁面) SignupActivity.java public class SignupActivity extends BaseActivity { EditText _nameText; EditText _emailText; EditText _passwordText; EditText _reEnterPasswordText; Button _signupButton; TextView _loginLink; @Override int getLayoutId() { return R.layout.activity_signup; } @Override void initView() { //根據(jù)id找控件 _nameText = (EditText) findViewById(R.id.input_name); _emailText = (EditText) findViewById(R.id.input_username); _passwordText = (EditText) findViewById(R.id.input_password); _reEnterPasswordText = (EditText) findViewById(R.id.input_reEnterPassword); _signupButton = (Button) findViewById(R.id.btn_signup); _loginLink = (TextView) findViewById(R.id.link_login); _signupButton.setOnClickListener(this); _loginLink.setOnClickListener(this); } @Override void processClick(View v) { switch (v.getId()) { case R.id.btn_signup: signup(); break; case R.id.link_login: //點(diǎn)擊登錄連接,跳轉(zhuǎn)到登錄頁面 Intent intent = new Intent(getApplicationContext(), LoginActivity.class); startActivity(intent); finish(); overridePendingTransition(R.anim.push_left_in, R.anim.push_left_out); break; default: break; } } public void signup() { //判斷是否合法 if (!validate()) { onSignupFailed(0); return; } _signupButton.setEnabled(false); //顯示圓形進(jìn)度條對話框 final ProgressDialog progressDialog = new ProgressDialog(SignupActivity.this, R.style.AppTheme_Dark_Dialog); progressDialog.setIndeterminate(true); progressDialog.setMessage('創(chuàng)建賬號...'); progressDialog.show(); //獲取數(shù)據(jù) String username = _nameText.getText().toString(); String password = _passwordText.getText().toString();// 聯(lián)網(wǎng)獲取數(shù)據(jù) OkGo.get(CONFIG.URL_SIGNUP) .params('username', username) .params('password', password) .execute(new StringCallback() { @Override public void onSuccess(String s, Call call, Response response) { Gson gson = new Gson(); JsonSignupBean jsonSignupBean = gson.fromJson(s, JsonSignupBean.class); //如果得到返回消息為ok,則注冊成功,。 if (jsonSignupBean.getMsg().equals('ok')) { Log.e('zwc', 'onSuccess: 注冊成功'); onSignupSuccess(); //對話框消失 progressDialog.dismiss(); } else { onSignupFailed(1); progressDialog.dismiss(); } } }); } /** * 登陸成功 */ public void onSignupSuccess() { _signupButton.setEnabled(true); Intent intent = new Intent(SignupActivity.this, MainActivity.class); startActivity(intent); finish(); } /** * 注冊失敗,,按鈕置為可用 * 依據(jù)傳參不同,進(jìn)行不同吐司 */ public void onSignupFailed(int i) { if (i == 1) { Toast.makeText(getBaseContext(), '該用戶名已經(jīng)被注冊', Toast.LENGTH_LONG).show(); } else { Toast.makeText(getBaseContext(), '注冊失敗', Toast.LENGTH_LONG).show(); } _signupButton.setEnabled(true); } /** * @return 輸入內(nèi)容是否合法 */ public boolean validate() { boolean valid = true; //從控件中獲取數(shù)據(jù) String name = _nameText.getText().toString(); String password = _passwordText.getText().toString(); String reEnterPassword = _reEnterPasswordText.getText().toString(); //檢測賬號是否正確 if (name.isEmpty()) { _nameText.setError('賬號不能為空'); valid = false; } else { _nameText.setError(null); } //檢測密碼是否正確 if (password.isEmpty()) { _passwordText.setError('請輸入密碼'); valid = false; } else { _passwordText.setError(null); } //檢測重復(fù)密碼是否正確 if (reEnterPassword.isEmpty() || !(reEnterPassword.equals(password))) { _reEnterPasswordText.setError('兩次密碼不一致'); valid = false; } else { _reEnterPasswordText.setError(null); } return valid; }} 布局文件 activity_signup.xml AnswerActivity ,AnswerFragment(答題頁面) 回答問題的頁面,。當(dāng)?shù)竭_(dá)AnswerActivity頁面的時候,會聯(lián)網(wǎng)獲取題目數(shù)據(jù),并將題目存入本地?cái)?shù)據(jù)庫,。然后通過viewpager綁定n個AnswerFragment,,對題目進(jìn)行展示。作答后,,進(jìn)行存庫操作,。(n由題目數(shù)量決定) public class AnswerActivity extends BaseActivity implements Chronometer.OnChronometerTickListener { private Chronometer chronometer; private ViewPager vp_answer; private ArrayList 恩,,現(xiàn)在就寫了這么多,,對于作業(yè)來說應(yīng)該是綽綽有余了。不過還是有很多需要改善的地方,。以后有時間的話,,會繼續(xù)完善一下。最后,,本文挺長的,。真誠的感謝一下你能看到這里。 以上就是本文的全部內(nèi)容,,希望對大家的學(xué)習(xí)有所幫助,,也希望大家多多支持腳本之家。 |
|