基于Android+SQLite数据库开发Java考试App
项目简介
Java课程考试App是基于AndroidStudio和SQLite数据库开发的一款App可以实现教师考生双端登录并使用相应功能。以Java课程作为设计主题,针对它们设计、实现一个考试APP。满足教师用户通过APP进行考生管理(考生信息的增删改查)、试题管理(试题信息的增删改查,自定义题目的类型,如:判断题、单选题、多选题、填空题等)的考试的需求。满足考生用户通过APP进行科目考试,获取成绩的需求,实现考试自动化管理。
项目功能
教师端功能
1、教师登录:支持教师用户登录APP。
2、试题管理:实现按题型分类进行题目编辑包括:浏览、录入、删除、修改题目。
3、录入需要参加考试的学生用户信息包括:姓名、学号、专业、班级等,并可对考生的个人信息实现浏览、录入、删除、修改、关键字查找考生的功能。
考生端功能
1、学生登录:支持学生用户登录APP,登录时先按姓名、学号信息进行身份验证,成功后切换至考试界面。
2、学生考试:从数据库中随机抽取各题型的题目形成“试卷”(每类至少5题),考试前显示登录学生的个人信息的试前准备页面。
3、考试:学生在一定时间内进行作答,时间到时,如果没有“交卷”,则强制结束考试“收卷”。
4、成绩计算:学生完成考试后,保存所有作答结果并与数据库中标准答案对比,自动评分,显示最终分数。
APP效果展示
源码地址:https://download.csdn.net/download/2302_79553009/89695645
流程设计
Java考试App可供给两类用户,并实现相应功能。
数据库设计
1、EXAM数据库中一共存在老师、同学、三类题目等数据表,数据库结构如图所示。
2、利用android提供的SQLiteOpenHelper类,建立一个MySQLiteOpenHelper进行数据库创建。如图。
3、通过SQL语句实现数据库与各类项目开发所需的数据表的创建。并在OnCreate方法中进行语句执行如图3-2所示。
项目开发
教师登录
设计教师登录界面,需要输入教师账号与密码进行登录验证,如果账号或密码不匹配,则提示登录失败,可点击重置按钮重新输入账号与密码。教师登录界面如图。
登录方法实现,利用在activity中调用数据库帮助类MySQLiteHelper中创建的T_login方法实现。该方法利用SQLite中的query查询语句查询教师数据表中存储的账号与密码。随后与Activity中传入的账号、密码参数做对比,返回相应数据进行登录验证。代码如下。
public int T_login(String name,String password){
SQLiteDatabase db = getWritableDatabase();
boolean result=false;
Cursor teacher = db.query("teacher", null, "t_name like ?", new String[]{name}, null, null, null);
if(teacher!=null){
while(teacher.moveToNext()){
String password1 = teacher.getString(2);
result = password1.equals(password);
if(result){
return 1;
}
}
}
return 2;
}
考生管理
教师登录后进入教师端菜单页面,点击考生管理即可进行考生管理页面。教师端菜单页面如图。
考生管理实现考生信息(姓名、学号、专业、班级)展示功能、关键字搜索考生功能、考生添加功能、考生信息编辑功能、考生删除功能。考生管理界面如图。
考生信息浏览
考生信息浏览功能需要利用listview控件进行item排列,同时新建Adapter类进行数据展示逻辑代码的书写。在adapter进行数据展示的时候,提前准备好listview中每一个item将要应用的布局文件。随后在adapter中利用id.layout.name进行获取。这样即可实现将一个样式同时应用到listview中的所有item上的效果。样式如图。
添加考生
添加考生功能的实现需要在数据库帮助类中写下考生添加方法,利用SQLite中的insert语句进行实现。代码如下。
public void student_add(String ...id){
SQLiteDatabase db = getWritableDatabase();
db.execSQL("INSERT INTO student (s_name,s_number,s_profession,s_class) VALUES(?,?,?,?)",id);
}
点击添加考生按钮,app将跳转至考生添加页面。跳转页面通过Intent intent = new Intent(当前Activity.this,目标Activity.class);startActivity(intent);后即可成功跳转。完成跳转随后进行手动输入考生信息(姓名、学号、专业、班级)。在Activity中声明各个EditText控件并获取每个输入值,利用对应四个变量存储,在Activity中调用方法时进行参数传入,完成考生添加。代码如下。
private void init(){
addName = findViewById(R.id.add_name);
addNumber = findViewById(R.id.add_number);
addProfession = findViewById(R.id.add_profession);
addClass = findViewById(R.id.add_class);
}
public void studentAdd(View view) {
Intent intent = getIntent();
String id = intent.getStringExtra("id");
String name=addName.getText().toString().trim();
String number=addNumber.getText().toString().trim();
String profession=addProfession.getText().toString().trim();
String cla=addClass.getText().toString().trim();
if(name.isEmpty()){
ToastUtil.toastShort(StudentAddActivity.this,"请输入姓名!");
}else if(number.isEmpty()){
ToastUtil.toastShort(StudentAddActivity.this,"请输入学号!");
}else if(profession.isEmpty()){
ToastUtil.toastShort(StudentAddActivity.this,"请输入专业!");
}else if(cla.isEmpty()){
ToastUtil.toastShort(StudentAddActivity.this,"请输入班级!");
}
mySQLiteOpenHelper.student_add(name,number,profession,cla);
ToastUtil.toastShort(StudentAddActivity.this,"考生添加成功!");
Intent intent1 = new Intent(StudentAddActivity.this,StudentManagementActivity.class);
startActivity(intent1);
}
删除考生
删除考生功能通过调用数据库帮助类中写下的delete语句进行实现。在Activity中进行方法调用并传入当前点击的item传入的id。以此寻找到考生表中的某条信息进行删除操作。Listview的点击传值操作主要通过convert View的点击侦听器实现。代码如下。
View finalConvertView = convertView;
convertView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(getContext(), StudentOperateActivity.class);
intent.putExtra("id",student.getS_id());
getContext().startActivity(intent);
}
});
编辑考生信息
删除考生功能通过调用数据库帮助类中写下的update语句进行实现。在Activity中进行方法调用并传入当前点击的item传入的id。以此寻找到考生表中的某条信息进行编辑操作。编辑方法代码如下。
//修改考生信息
public int student_update(String name,String number,String profession,String cla,String id){
SQLiteDatabase db = getWritableDatabase();
ContentValues values = new ContentValues();
//传值准备数据
values.put("s_name",name);
values.put("s_number",number);
values.put("s_profession",profession);
values.put("s_class",cla);
return db.update("student",values,"s_id like ?",new String[] {id});
}
点击编辑某一条item,app将跳转至考生编辑页面。跳转页面通过Intent intent = new Intent(当前Activity.this,目标Activity.class);startActivity(intent);后即可成功跳转。完成跳转随后进行手动修改考生信息(姓名、学号、专业、班级)。在Activity中声明各个EditText控件并获取每个输入值,利用对应四个变量存储,在Activity中调用方法时进行参数传入,完成考生添加编辑。考生编辑页面如图。
该编辑页面需要获取点击的item上的所有信息,获取方法则是在点击item时传入当前id,随后在数据库帮助类中利用query语句进行特定信息查询。将查询结果放置在编辑页面即可实现数据同步。
查询考生
查询考生功能通过在搜索框中输入学生name或者学号。利用变量进行存储,随后调用数据库帮助类中写好的查询方法,并传入该输入的参数进行符合条件的数据展示。即可实现考生搜索功能。代码如下。
//搜索功能
search.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
listView.setAdapter(null);
String title = search.getText().toString();
List<Student> temp = null;
if(title.isEmpty()){
temp = mySQLiteOpenHelper.getAllStudent();
}else{
temp = mySQLiteOpenHelper.getAllStudent(title);
}
StudentAdapter studentAdapter = new StudentAdapter(StudentManagementActivity.this,temp);
listView.setAdapter(studentAdapter);
return false;
}
});
search.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
listView.setAdapter(null);
String title = search.getText().toString();
List<Student> temp = null;
if(title.isEmpty()){
temp = mySQLiteOpenHelper.getAllStudent();
}else{
temp = mySQLiteOpenHelper.getAllStudent(title);
}
StudentAdapter studentAdapter = new StudentAdapter(StudentManagementActivity.this,temp);
listView.setAdapter(studentAdapter);
}
});
试题管理
试题管理实现试题信息(选择题、判断题、填空题)展示功能、试题添加功能、试题信息编辑功能、试题删除功能。类比考生管理界面。
试题分类浏览
试题一共有三类,分别为选择题、判断题、填空题。对应数据库中三张题目表,采取query查询所有题目的方法进行题目查找并存入List。存入成功后利用对应的三类adapter以及提前准备好的三类item样式进行setadapter展示试题。试题展示页面如图。
新增试题
点击添加试题按钮,app将跳转至试题添加页面。跳转页面通过Intent intent = new Intent(当前Activity.this,目标Activity.class);startActivity(intent);后即可成功跳转。完成跳转随后进行手动输入相应试题信息,不同试题需要的信息不同。随后在Activity中声明各个EditText控件并获取每个输入值,利用对应若干个变量存储,在Activity中调用方法时进行参数传入,完成试题添加。代码如下。
public void choiceAdd(View view) {
Intent intent = getIntent();
String id = intent.getStringExtra("id");
String topic=c_topic.getText().toString().trim();
String A=optiona.getText().toString().trim();
String B=optionb.getText().toString().trim();
String C=optionc.getText().toString().trim();
String D=optiond.getText().toString().trim();
String answer=c_answer.getText().toString().trim();
if(topic.isEmpty()){
ToastUtil.toastShort(ChoiceAddActivity.this,"请输入题目!");
}else if(A.isEmpty()){
ToastUtil.toastShort(ChoiceAddActivity.this,"请输入选项A!");
}else if(B.isEmpty()){
ToastUtil.toastShort(ChoiceAddActivity.this,"请输入选项B!");
}else if(C.isEmpty()){
ToastUtil.toastShort(ChoiceAddActivity.this,"请输入选项C!");
}else if(D.isEmpty()){
ToastUtil.toastShort(ChoiceAddActivity.this,"请输入选项D!");
}else if(answer.isEmpty()){
ToastUtil.toastShort(ChoiceAddActivity.this,"请输入答案!");
}
mySQLiteOpenHelper.choice_add(topic,A,B,C,D,answer);
ToastUtil.toastShort(ChoiceAddActivity.this,"选择题添加成功!");
Intent intent1 = new Intent(ChoiceAddActivity.this,QuestionManagementActivity.class);
startActivity(intent1);
}
试题还可进行删除与修改,方法与上述考生删除修改一致。
考生登录
设计考生登录界面,需要输入考生姓名与学号进行登录验证,如果姓名或学号不匹配,则提示登录失败,可点击重置按钮重新输入姓名与学号。考生登录界面如图。
登录方法实现,利用在activity中调用数据库帮助类MySQLiteHelper中创建的S_login方法实现。该方法利用SQLite中的query查询语句查询考生数据表中存储的账号与密码。随后与Activity中传入的姓名、学号参数做对比,返回相应数据进行登录验证。代码下。
public int S_login(String name,String number){
SQLiteDatabase db = getWritableDatabase();
boolean result=false;
Cursor student = db.query("student", null, "s_name like ?", new String[]{name}, null, null, null);
if(student!=null){
while(student.moveToNext()){
String password1 = student.getString(2);
result = password1.equals(number);
if(result){
return 1;
}
}
}
return 2;
}
课程考试
考生登录成功以后进入到考试准备页面,考试准备页面信息包括考试科目、当前登录考生姓名、学号、专业、班级、以及考试时间。考试准备页面如图。
获取当面登录的考生信息,需要在登录页面进行考生的姓名传输,利用Intent intent = new (当前Activity.this,目标Activity.class);intent.putExtra(“name”,name);startActivity(intent);来进行name值的传输。随后在考试准备页面的Activity中即可调用数据库帮助类里写好的query语句查询方法并传入name参数,获取整条特定考生信息回到Activity并进行相应控件的赋值。代码如下。
package com.example.examinationapp;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import com.example.examinationapp.bean.Student;
public class StudentTestActivity extends AppCompatActivity {
private TextView stuName,stuNumber,stuProfession,stuClass;
MySQLiteOpenHelper mySQLiteOpenHelper = new MySQLiteOpenHelper(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_student_test);
init();
Intent intent = getIntent();
String name = intent.getStringExtra("name");
Student student = mySQLiteOpenHelper.getTheOneStudent(name);
stuName.setText(student.getS_name());
stuNumber.setText(student.getS_number());
stuProfession.setText(student.getS_profession());
stuClass.setText(student.getS_class());
}
private void init(){
stuName = (TextView) findViewById(R.id.stu_name);
stuNumber = (TextView)findViewById(R.id.stu_number);
stuProfession = (TextView)findViewById(R.id.stu_profession);
stuClass = (TextView)findViewById(R.id.stu_class);
}
public void startTest(View view) {
Intent intent = new Intent(StudentTestActivity.this,TestActivity.class);
String name = getIntent().getStringExtra("name");
intent.putExtra("name",name);
startActivity(intent);
}
}
考生试卷获取
考生试卷一共有三种题型,分别为选择题、判断题、填空题,三类题目各从数据库中获取五道,并分三类展示道考试界面。点击上方导航跳即可进行切换。(切换同样利用Intent,并且在每个切换过程中都传输了name值,为了最后输出成绩时进行考生信息的输出)。三类考题下都有提交按钮,提交以后所得的答案都会存储到数据库中。考试界面如图。
成绩结算
为了计算分数,需要获取三类题目中每一道题目所选所填入的值,并传入到数据库中的相应数据表中。因此提前准备三张数据表,作为答案存储表。在答案存储完成以后,需要进行答案的获取,在数据库帮助类中实现了得到答案的三类题型相应方法。(在设计获取答案方法时,应该提前添加各个题目的封装类,并写好构造函数与set、get方法,以便获取答案的时候可以get到具体答案值)答案获取方法代码如下。
//录入选择题答案
public void canswer_add(String ...id){
SQLiteDatabase db = getWritableDatabase();
db.execSQL("INSERT INTO canswer (ca_answerone,ca_answertwo,ca_answerthree,ca_answerfour,ca_answerfive) VALUES(?,?,?,?,?)",id);
}
public void janswer_add(String ...id){
SQLiteDatabase db = getWritableDatabase();
db.execSQL("INSERT INTO janswer (ja_answerone,ja_answertwo,ja_answerthree,ja_answerfour,ja_answerfive) VALUES(?,?,?,?,?)",id);
}
public void fanswer_add(String ...id){
SQLiteDatabase db = getWritableDatabase();
db.execSQL("INSERT INTO fanswer (fa_answerone,fa_answertwo,fa_answerthree,fa_answerfour,fa_answerfive) VALUES(?,?,?,?,?)",id);
}
public void score(){
}
public List<ChAnswer> getCH(){
SQLiteDatabase db = getWritableDatabase();
List<ChAnswer> list = new ArrayList<>();
String limit ="5";
Cursor res = db.query("choice", null, null,null, null, null, null,limit);
while (res.moveToNext()){
ChAnswer chAnswer = new ChAnswer(res.getString(0), res.getString(6));
list.add(chAnswer);
}
return list;
}
public List<JuAnswer> getJU(){
SQLiteDatabase db = getWritableDatabase();
List<JuAnswer> list = new ArrayList<>();
String limit ="5";
Cursor res = db.query("judge", null, null,null, null, null, null,limit);
while (res.moveToNext()){
JuAnswer juAnswer = new JuAnswer(res.getString(0), res.getString(2));
list.add(juAnswer);
}
return list;
}
public List<FiAnswer> getFI(){
SQLiteDatabase db = getWritableDatabase();
List<FiAnswer> list = new ArrayList<>();
String limit ="5";
Cursor res = db.query("choice", null, null,null, null, null, null,limit);
while (res.moveToNext()){
FiAnswer fiAnswer = new FiAnswer(res.getString(0), res.getString(2));
list.add(fiAnswer);
}
return list;
}
public CCA getCC(String id){
SQLiteDatabase db = getWritableDatabase();
CCA cca =null;
Cursor res = db.rawQuery("select * from canswer where ca_id=?",new String[]{id});
while (res.moveToNext()){
cca = new CCA(res.getString(0), res.getString(1), res.getString(2),res.getString(3),res.getString(4),res.getString(5));
}
return cca;
}
public JJA getJJ(String id){
SQLiteDatabase db = getWritableDatabase();
JJA jja =null;
Cursor res = db.rawQuery("select * from janswer where ja_id=?",new String[]{id});
while (res.moveToNext()){
jja = new JJA(res.getString(0), res.getString(1), res.getString(2),res.getString(3),res.getString(4),res.getString(5));
}
return jja;
}
public FFA getFF(String id){
SQLiteDatabase db = getWritableDatabase();
FFA ffa =null;
Cursor res = db.rawQuery("select * from fanswer where fa_id=?",new String[]{id});
while (res.moveToNext()){
ffa = new FFA(res.getString(0), res.getString(1), res.getString(2),res.getString(3),res.getString(4),res.getString(5));
}
return ffa;
}
在计算分数的时候,先进行相应Score展示Activity的创建,并写好展示模板。分数展示页面如图。
在计算分数时,需要同时获取已经设置好的题目的答案已经刚刚做完试卷填入的答案。一个存放到泛型中,一个存放到List中。泛型类中的答案通过.get方法即可得到并重新建立数组传入(为了后面答案对比做准备)。而List中的答案值需要通过.get(i).getCha_answer()方法得到。二者进行for循环,循环次数为题目数量。三种题目答对则进行加分。最后在考试成绩展示页面上进行成绩赋值即可。代码如下。
package com.example.examinationapp;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import com.example.examinationapp.bean.CCA;
import com.example.examinationapp.bean.ChAnswer;
import com.example.examinationapp.bean.Choice;
import com.example.examinationapp.bean.FFA;
import com.example.examinationapp.bean.FiAnswer;
import com.example.examinationapp.bean.JJA;
import com.example.examinationapp.bean.JuAnswer;
import com.example.examinationapp.bean.Student;
import java.util.List;
public class ScoreActivity extends AppCompatActivity {
private TextView stuName,stuNumber,stuProfession,stuClass,getScore;
MySQLiteOpenHelper mySQLiteOpenHelper = new MySQLiteOpenHelper(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_score);
init();
Intent intent = getIntent();
String name = intent.getStringExtra("name");
Student student = mySQLiteOpenHelper.getTheOneStudent(name);
stuName.setText(student.getS_name());
stuNumber.setText(student.getS_number());
stuProfession.setText(student.getS_profession());
stuClass.setText(student.getS_class());
String id = intent.getStringExtra("id");
CCA cca = mySQLiteOpenHelper.getCC(id);
JJA jja = mySQLiteOpenHelper.getJJ(id);
FFA ffa = mySQLiteOpenHelper.getFF(id);
List<ChAnswer> chAnswers = mySQLiteOpenHelper.getCH();
List<JuAnswer> juAnswers = mySQLiteOpenHelper.getJU();
List<FiAnswer> fiAnswers = mySQLiteOpenHelper.getFI();
int TotalScore = 0;
String[] jAnswer = new String[5];
jAnswer[0]=jja.getJj_answerone();
jAnswer[1]=jja.getJj_answertwo();
jAnswer[2]=jja.getJj_answerthree();
jAnswer[3]=jja.getJj_answerfour();
jAnswer[4]=jja.getJj_answerfive();
String[] cAnswer = new String[5];
cAnswer[0]=cca.getCc_answerone();
cAnswer[1]=cca.getCc_answertwo();
cAnswer[2]=cca.getCc_answerthree();
cAnswer[3]=cca.getCc_answerfour();
cAnswer[4]=cca.getCc_answerfive();
String[] fAnswer = new String[5];
fAnswer[0]=ffa.getFf_answerone();
fAnswer[1]=ffa.getFf_answertwo();
fAnswer[2]=ffa.getFf_answerthree();
fAnswer[3]=ffa.getFf_answerfour();
fAnswer[4]=ffa.getFf_answerfive();
for(int i = 0;i<5;i++){
if(cAnswer[i].equals(chAnswers.get(i).getCha_answer())){
TotalScore+=5;
}
if(jAnswer[i].equals(juAnswers.get(i).getJua_answer())){
TotalScore+=20;
}
if(fAnswer[i].equals(fiAnswers.get(i).getFia_answer())){
TotalScore+=5;
}
}
getScore.setText(String.valueOf(TotalScore));
}
private void init(){
stuName = (TextView) findViewById(R.id.stu_name);
stuNumber = (TextView)findViewById(R.id.stu_number);
stuProfession = (TextView)findViewById(R.id.stu_profession);
stuClass = (TextView)findViewById(R.id.stu_class);
getScore = findViewById(R.id.getscore);
}
public void review(View view) {
}
}
考试限时功能实现
考试显示功能实现需要调用一个CountDownTimer,进行倒计时,其中可以设置倒计时时长、倒计时结束以后进行的逻辑操作,以及若在倒计时前完成考试进行Timer的cancle方法取消倒计时。代码如下。
CountDownTimer countDownTimer = new CountDownTimer(20 * 1000, 1000) { // 设置倒计时的总时间和间隔
public void onTick(long millisUntilFinished) {
timer.setText("填空题倒计时 " + millisUntilFinished / 1000);
}
public void onFinish() {
methodToCallAfterTimerEnds();
}
}.start();
private void methodToCallAfterTimerEnds() {
submit();
}
项目总结
设计过程中碰到的难点与解决方法
难点一、在计算分数时,每次获取的答案作为一行数据存储到数据表中,每次计算分数应该调
取最近的那次考试答案来进行答案对比。此时需要传递一个会随着考试次数改变的id。
解决办法:在考试提交的Activity中设置一个int i=0;使得i+=1,并通过intent传递到成绩展示页面。(这里i是int类型,需要利用String.valueof(i)进行字符串的转化并传递)。代码下。
mySQLiteOpenHelper.fanswer_add(ChoiceAs[0],ChoiceAs[1],ChoiceAs[2],ChoiceAs[3],ChoiceAs[4]);
int i=3;
i+=1;
ToastUtil.toastShort(TestFillActivity.this,"试卷已提交");
Intent intent = new Intent(TestFillActivity.this,ScoreActivity.class);
String name = getIntent().getStringExtra("name");
intent.putExtra("name",name);
intent.putExtra("id",String.valueOf(i));
startActivity(intent);
难点二、SQLite无法建立第二张数据表
解决办法:SQLite数据库在建立库的时候,只会调用一次oncreate方法,因此,尽量在项目开始前就确定要用几张表,否则无法中途新添第二、三张表。如果中途想起要新增表,则打开Device File Explore找到data->data->当前项目->找到.db文件,将上级database下的所有文件删除,再重新运行建库即可。
难点三、在成绩计算页面中的考生信息需要接收到一个数据表值才可进行指定查询。
解决办法:Intent在传递值时只能传递给下一个跳转的页面,因此可以采用连续Intent传值,每跳转一个页面即传递该值到下个页面,直到进入成绩结算页面供给查询当前考生值。