这几天利用空余的时间学习了下android,选择了类似答题的应用来边做边学,美名曰:答题王
既然刚开始,也不搞那么难,能做一个简单的就ok了
首先我定义了三个页面:
1) 首页:FirstPageActivity
2) 答题页面:AnswerQuestionActivity
3) 结果页面:ResultActivity
具体流程是:
1) 用户在首页选择试题类型(有3套题目)
2) 根据用户选择的题型在数据库中查找对应的题目,用户答题
3) 显示用户答题的成绩,并获取用户的排名
主要知识点:
1)android简单布局,元素使用及监听
2)android数据存储及查询(sqlite使用)
3)页面之间数据传递
4)访问服务端接口获取数据
1. 首页
使用3个button,每个button监听用户是否点击
Activity的主要代码是:
Button button1;
Button button2;
Button button3;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.firstpage);
button1 = (Button)this.findViewById(R.id.Button01);
button2 = (Button)this.findViewById(R.id.Button02);
button3 = (Button)this.findViewById(R.id.Button03);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startNewActivity(1);
}
});
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startNewActivity(2);
}
});
button3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startNewActivity(3);
}
});
}
private void startNewActivity(int parm) {
Intent i = new Intent();
i.setClass(FirstPageActivity.this, AnswerQuestionActivity.class);
i.putExtra("exam_id", parm);
startActivity(i);
FirstPageActivity.this.finish();
}
以上使用Intent类作为页面之间的跳转,并且可以在页面之间传递参数。
2. 答题页面
1) 首先需要通过首页传过来的参数查询数据库,数据库使用的是sqlite,里面有一张表t_question,在查询数据库之前首先要将数据库放入应用程序的数据目录下:切换到DDMS视图下,选择File Explorer标签,进入/data/data/(package)/databases,在右上角可以看到有一个代右箭头的图标,点击这个图标,选择需要导入的数据库文件。
查询数据库的代码:
private List> loadData() {
Intent intent = this.getIntent();
int exam_id = intent.getIntExtra("exam_id", 0);
mSQLiteDatabase = this.openOrCreateDatabase("dati_db.s3db", MODE_PRIVATE, null);
String sql = "select * from t_questions where exam_id="+exam_id;
Cursor cursor = mSQLiteDatabase.rawQuery(sql, null);
List> datas = new ArrayList>();
if(cursor != null) {
Log.v("loadData", "cursor is not null");
HashMap map = null;
if(cursor.moveToFirst()) {
do{
try {
map = new HashMap();
int questionColumnIdx = cursor.getColumnIndex("question");
byte[] question = cursor.getBlob(questionColumnIdx);
map.put("question", new String(question,"GBK"));
int answersColumnIdx = cursor.getColumnIndex("answer");
byte[] answer = cursor.getBlob(answersColumnIdx);
map.put("answer", new String(answer,"GBK"));
int correct_answer = cursor.getInt(cursor.getColumnIndex("correct_answer"));
map.put("correct_answer", String.valueOf(correct_answer));
datas.add(map);
} catch (Exception e) {
e.printStackTrace();
}
}while(cursor.moveToNext());
}
}
return datas;
}
以上使用了SQLiteDatabase类(mSQLiteDatabase)操作数据库,将查询的数据封装成一个List对象,值得注意的是,因为数据库中有中文,我使用了cursor.getBlob()方法获取数据后再转化编码的方式。
答题页面的样式为一个文本标题,另外还有答案选项,答案选项我使用的是单项选择类RadioGroup和RadioButton,所以有一个方法:resetQuestion(设置问题)
private void resetQuestion(List> datas, int question_index) {
if(question_index < datas.size()) {
question_text.setText(datas.get(question_index).get("question"));
String answers = datas.get(question_index).get("answer");
String[] ansArr = answers.split("\\|");
ans_radioGroup.removeAllViews();
for(String s : ansArr) {
RadioButton radioButton = new RadioButton(this.getApplicationContext());
radioButton.setText(s);
radioButton.setTextColor(Color.BLACK);
ans_radioGroup.addView(radioButton);
}
correct_answer = Integer.parseInt(datas.get(question_index).get("correct_answer"));
}else {
String deviceId = tm.getDeviceId();
Log.v("deviceId", deviceId);
String rankStr = RequestUtil.sendGet("http://XXX.XX.XX.XX/rank.php", "user="+deviceId+"&total="+datas.size()+"&correct="+correct_number);
Log.v("rankResult", rankStr);
try {
JSONObject jsonObj = new JSONObject(rankStr);
int rank = Integer.parseInt(jsonObj.get("rank").toString());
Log.v("rank", String.valueOf(rank));
this.goIntoResultView(rank);
} catch (JSONException e) {
e.printStackTrace();
}
}
}
question_index为定义的一个全局变量,当用户回答的问题数小于总数,则执行初始化题目的页面,这里使用一个ans_radioGroup变量就可以了,监听用户的行为,代码后面会讲到,每次都需要使用ans_radioGroup.removeAllViews()清除掉上次的答案选项,再根据下次答案选项初始化单选按钮的个数,correct_answer也是一个全局变量,记录当前题目的正确选项。
当用户答玩所有的题目后获取用户的排名,跳转到成绩页面,这里通过获取手机的设备号来标识唯一用户,使用:
final TelephonyManager tm = (TelephonyManager) getBaseContext().getSystemService(Context.TELEPHONY_SERVICE);
String deviceId = tm.getDeviceId();
另外向服务端请求数据,我封装了一个类RequestUtil,发送一个GET请求,具体代码如下:
/**
* 向指定URL发送GET方法的请求
*
* @param url
* 发送请求的URL
* @param params
* 请求参数,请求参数应该是name1=value1&name2=value2的形式。
* @return URL所代表远程资源的响应
*/
public static String sendGet(String url, String params) {
String result = "";
BufferedReader in = null;
try {
String urlName = url + "?" + params;
URL realUrl = new URL(urlName);
// 打开和URL之间的连接
URLConnection conn = realUrl.openConnection();
// 设置通用的请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += "\n" + line;
}
} catch (Exception e) {
System.out.println("发送GET请求出现异常!" + e);
e.printStackTrace();
}
finally {
try {
if (in != null) {
in.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
return result.length()>1?result.substring(1):result;
}
4)ans_radioGroup监听用户的行为:
ans_radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
Log.v("correct_answer", String.valueOf(correct_answer));
RadioButton correctButton = getRadioButton(correct_answer);
if(checkedId == correctButton.getId()) {
displayToast("回答正确");
correct_number ++;
}else {
displayToast("回答错误");
}
question_index ++;
resetQuestion(datas, question_index);
}
});
里面有两个小方法:
4.1) getRadioButton(correct_answer),根据正确答案,获取对应的RadioButton,correct_answer我在数据库中存储的是题目的编号,具体方法代码如下:
private RadioButton getRadioButton(int correct_answer) {
return (RadioButton)ans_radioGroup.getChildAt(correct_answer - 1);
}
因为RadioGroup是从0开始计数的
4.2) displayToast(str),提示框,具体介绍可查看这里,代码如下:
private Toast displayToast(String str) {
Toast toast = Toast.makeText(this, str, Toast.LENGTH_SHORT);
toast.setGravity(Gravity.TOP, 0, 220);
toast.show();
return toast;
}
3. 成绩页面
很简单,获取答题页面传过来的参数,显示即可。