在此声明,此在线考试系统是利用业余时间写的一个Demo,功能并没有完善,还有两个功能模块没写完,代码注释详细,
并且我会在文章结尾附上源码的下载地址。
我们先来看两个功能模块的效果图,觉得喜欢你再下载源码,由于github账号忘记了,所以源码只能放在博客里.
注:次项目没用到网络,考试题目都来自己本地数据库里面的数据 数据表在res raw下,名为kaoshi.db
如有看不懂的地方,或者源码跑不起来可关注我的快手id:北海狂鲛
私聊我 我看到了就会帮你解决
功能模块一 顺序练习
功能模块二 随机练习
下面上一部分代码,代码有点多,我就不全部贴上来了
首先在总布局里面我用到了约束布局 需要导入一个依赖
compile 'com.android.support.constraint:constraint-layout:1.0.2'
然后就是一个db文件的数据库, 数据结构如下(如果不会查看db文件,可查看我的上一篇博客,有介绍怎么查看数据表)
接下来是总体的布局
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.hnkjxy.z.onlinetestsystemsdemo.MainActivity">
<LinearLayout
android:id="@+id/main_activity_headings"
android:gravity="center"
android:orientation="horizontal"
android:layout_width="match_parent"
android:background="@color/LightBlue"
android:layout_height="40dp">
<TextView
android:text="在线考试系统"
android:textSize="20dp"
android:textColor="@color/white"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
<LinearLayout
app:layout_constraintTop_toBottomOf="@+id/main_activity_Wrong_topic_practise_imageView"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:background="@drawable/layout_round_a"
android:layout_width="250dp"
android:gravity="center"
android:layout_height="250dp">
<LinearLayout
android:id="@+id/l2"
android:gravity="center"
android:background="@drawable/layout_round_b"
android:layout_width="220dp"
android:layout_height="220dp">
<LinearLayout
android:id="@+id/l3"
android:background="@drawable/layout_round_c"
android:layout_width="180dp"
android:gravity="center"
android:layout_height="180dp">
<TextView
android:text="在线考试"
android:textColor="@color/white"
android:textSize="30dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<ImageView
android:id="@+id/main_activity_simulation_test_imageView"
android:layout_marginTop="20dp"
app:layout_constraintTop_toBottomOf="@id/main_activity_headings"
android:background="@mipmap/monikaoshi"
android:layout_width="50dp"
android:layout_height="50dp"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginLeft="30dp"/>
<TextView
android:id="@+id/main_activity_simulation_test_text"
android:text="@string/simulationTest"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/main_activity_simulation_test_imageView"
app:layout_constraintLeft_toLeftOf="@id/main_activity_simulation_test_imageView"
android:textSize="12dp"
app:layout_constraintRight_toRightOf="@id/main_activity_simulation_test_imageView"
android:layout_marginTop="10dp"/>
<ImageView
android:id="@+id/main_activity_Wrong_topic_practise_imageView"
android:layout_marginTop="20dp"
app:layout_constraintTop_toBottomOf="@id/main_activity_headings"
android:background="@mipmap/cuoti"
android:layout_width="50dp"
android:layout_height="50dp"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginRight="30dp"/>
<TextView
android:id="@+id/main_activity_wrong_topic_practise_text"
android:text="@string/wrongTopicpractise"
android:textSize="12dp"
app:layout_constraintLeft_toLeftOf="@id/main_activity_Wrong_topic_practise_imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/main_activity_Wrong_topic_practise_imageView"
app:layout_constraintRight_toRightOf="@id/main_activity_Wrong_topic_practise_imageView"
android:layout_marginTop="10dp"/>
<ImageView
android:id="@+id/main_activity_random_contact_imageView"
app:layout_constraintTop_toBottomOf="@id/main_activity_Wrong_topic_practise_imageView"
android:layout_marginTop="240dp"
android:background="@mipmap/suiji"
android:layout_width="50dp"
android:layout_height="50dp"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginRight="30dp"/>
<TextView
android:id="@+id/main_activity_random_contact_text"
android:text="@string/RandomContact"
android:textSize="12dp"
app:layout_constraintLeft_toLeftOf="@id/main_activity_random_contact_imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/main_activity_random_contact_imageView"
app:layout_constraintRight_toRightOf="@id/main_activity_random_contact_imageView"
android:layout_marginTop="10dp"/>
<ImageView
android:id="@+id/main_activity_my_collect_imageView"
app:layout_constraintTop_toBottomOf="@id/main_activity_simulation_test_imageView"
android:layout_marginTop="240dp"
android:background="@mipmap/shoucang"
android:layout_width="50dp"
android:layout_height="50dp"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginLeft="30dp"/>
<TextView
android:id="@+id/main_activity_my_collect_text"
android:text="@string/myCollect"
android:textSize="12dp"
app:layout_constraintLeft_toLeftOf="@id/main_activity_my_collect_imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/main_activity_my_collect_imageView"
app:layout_constraintRight_toRightOf="@id/main_activity_my_collect_imageView"
android:layout_marginTop="10dp"/>
</android.support.constraint.ConstraintLayout>
注:布局里面用到了一些文字,图片,还有背景样式都在源码里,这里我就贴部分出来,图片就不贴了,源码里有
接下来是背景,自定义的圆角
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval"
android:useLevel="false">
<solid android:color="@color/a"/>
<padding
android:left="2dp"
android:top="1dp"
android:right="2dp"
android:bottom="1dp" />
<size android:width="15dp"
android:height="15dp" />
</shape>
还有就是文字了 我也贴一下吧
<resources>
<string name="app_name">OnlineTestSystemsDemo</string>
<string name="simulationTest">顺序练习</string>
<string name="wrongTopicpractise">错题练习</string>
<string name="myCollect">我的收藏</string>
<string name="RandomContact">随机练习</string>
</resources>
接下来就是MainActivity的代码了,分别为几个功能模块的点击事件
package com.hnkjxy.z.onlinetestsystemsdemo;
import android.Manifest;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.hnkjxy.z.onlinetestsystemsdemo.othersActivity.OnlinetestActivity;
import com.hnkjxy.z.onlinetestsystemsdemo.othersActivity.RandomPracticeActivity;
import com.hnkjxy.z.onlinetestsystemsdemo.othersActivity.SimulationActivity;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
ImageView main_activity_simulation_test_imageView, main_activity_Wrong_topic_practise_imageView,
main_activity_my_collect_imageView, main_activity_random_contact_imageView;
TextView main_activity_simulation_test_text, main_activity_wrong_topic_practise_text,
main_activity_my_collect_text, main_activity_random_contact_text;
LinearLayout l3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 找activity_main布局的方法
activityMainFindViewById();
// 安卓动态申请权限 因为在高版本的安卓手机上跑不申请会闪退 记住同时在Manifest里配置权限
//检查权限
if(ActivityCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){
// 申请权限
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
}
if(ActivityCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)!= PackageManager.PERMISSION_GRANTED){
// 申请权限
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS},1);
}
if(ActivityCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.READ_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){
// 申请权限
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},1);
}
}
// 找activity_main布局的方法
private void activityMainFindViewById() {
// 布局四个角落的图片id
main_activity_simulation_test_imageView = (ImageView) findViewById(R.id.main_activity_simulation_test_imageView);
main_activity_Wrong_topic_practise_imageView = (ImageView) findViewById(R.id.main_activity_Wrong_topic_practise_imageView);
main_activity_my_collect_imageView = (ImageView) findViewById(R.id.main_activity_my_collect_imageView);
main_activity_random_contact_imageView = (ImageView) findViewById(R.id.main_activity_random_contact_imageView);
// 布局四个图片下的文字的id
main_activity_simulation_test_text = (TextView) findViewById(R.id.main_activity_simulation_test_text);
main_activity_wrong_topic_practise_text = (TextView) findViewById(R.id.main_activity_wrong_topic_practise_text);
main_activity_my_collect_text = (TextView) findViewById(R.id.main_activity_my_collect_text);
main_activity_random_contact_text = (TextView) findViewById(R.id.main_activity_random_contact_text);
// linearlayout的布局id
l3 = (LinearLayout) findViewById(R.id.l3);
//
// 布局id找到后开始给他们注册点击事件
// 注册点击事件的方法
registrationSetOnClickListener();
}
// 注册点击事件方法
private void registrationSetOnClickListener() {
main_activity_simulation_test_imageView.setOnClickListener(this);
main_activity_Wrong_topic_practise_imageView.setOnClickListener(this);
main_activity_my_collect_imageView.setOnClickListener(this);
main_activity_random_contact_imageView.setOnClickListener(this);
main_activity_simulation_test_text.setOnClickListener(this);
main_activity_wrong_topic_practise_text.setOnClickListener(this);
main_activity_my_collect_text.setOnClickListener(this);
main_activity_random_contact_text.setOnClickListener(this);
l3.setOnClickListener(this);
}
// 这是实现OnClickListener接口必须写的一个方法 ,在里面写点击之后的逻辑
// 在代码的第十行实现的接口
@Override
public void onClick(View v) {
Intent intent;
AlertDialog.Builder ad = new AlertDialog.Builder(MainActivity.this);
switch (v.getId()) {
// 模拟考试和文字点击事件的跳转
case R.id.main_activity_simulation_test_imageView:
intent = new Intent(MainActivity.this, SimulationActivity.class);
startActivity(intent);
break;
case R.id.main_activity_simulation_test_text:
intent = new Intent(MainActivity.this, SimulationActivity.class);
startActivity(intent);
break;
// 在线考试的点击事件
case R.id.l3:
intent = new Intent(MainActivity.this, OnlinetestActivity.class);
startActivity(intent);
break;
// 随机练习文字的击事件
case R.id.main_activity_random_contact_text:
intent = new Intent(MainActivity.this, RandomPracticeActivity.class);
startActivity(intent);
break;
// 随机练习图片的点击事件
case R.id.main_activity_random_contact_imageView:
intent = new Intent(MainActivity.this, RandomPracticeActivity.class);
startActivity(intent);
break;
// 收藏的图标点击
case R.id.main_activity_my_collect_imageView:
ad.setTitle("通知");
ad.setMessage("此功能暂未实现,对代码有疑问或者不懂可以私聊我快手号\r\n快手id:北海狂鲛");
ad.setPositiveButton("我知道了", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int i) {
// 点击隐藏对话框
dialog.dismiss();
}
});
// 创建对话框
ad.create();
// 让用户必须选择对话框,解决弹出对话框点击空白对话框消失的问题
ad.setCancelable(false);
// 显示对话框
ad.show();
break;
// 收藏的文字点击
case R.id.main_activity_my_collect_text:
ad.setTitle("通知");
ad.setMessage("此功能暂未实现,对代码有疑问或者不懂可以私聊我快手号\r\n快手id:北海狂鲛");
ad.setPositiveButton("我知道了", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int i) {
// 点击隐藏对话框
dialog.dismiss();
}
});
// 创建对话框
ad.create();
// 让用户必须选择对话框,解决弹出对话框点击空白对话框消失的问题
ad.setCancelable(false);
// 显示对话框
ad.show();
break;
// 错题练习图片点击事件
case R.id.main_activity_Wrong_topic_practise_imageView:
break;
// 错题练习文字点击事件
case R.id.main_activity_wrong_topic_practise_text:
// AlertDialog.Builder add=new AlertDialog.Builder(MainActivity.this);
// add.setTitle("通知");
// add.setMessage("暂无错题数据");
// add.setPositiveButton("我知道了", new DialogInterface.OnClickListener() {
// @Override
// public void onClick(DialogInterface dialog, int which) {
// dialog.dismiss();
// }
// });
break;
}
}
}
还有几个工具类我也贴出来吧,第一个是存放变量名的类
package com.hnkjxy.z.onlinetestsystemsdemo.Finally;
import android.os.Environment;
import java.io.File;
/**
* Created by zhouweixiong on 2017/12/21.
*/
/**
* 定义一些常量
* 一些表名和字段名
* 不可修改字段名,否则会得不到数据
* 在res目录下面有一个raw文件夹,raw里面有一个数据表
* 如果要android studio查看此表,就去下载一个插件 database
*/
public class MyFinally {
public static final String TABLE_NAME = "t_question";//表名
public static final String TABLE_SOUCANG_NAME = "t_soucang";//表名
// 字段名
public static final String T01_COLUMN_ID = "question_id";
public static final String T01_COLUMN_OPTION_TYPE = "option_type";
public static final String T01_COLUMN_SOU_TYPE = "sou_type";
public static final String T01_COLUMN_ERROR_TYPE = "error_type";
//Environment.getExternalStorageDirectory() 获得根路径
//File.separator是用来分隔同一个路径字符串中的目录的,例如:
// C:\Program Files\Common Files
// 就是指“\”
public static final String FILE_PAPER_PATH = Environment.getExternalStorageDirectory() + File.separator + "kaoshi";
// 文件的详细地址,找到此文件
// FILE_PAPER_PATH 是第31行的地址,拼起来就是详细的文件地址和文件名
public static final String FILE_PATH = FILE_PAPER_PATH + File.separator + "kaoshi.db";
}
再就是创建系统文件夹和把数据表写入文件夹里的工具类
package com.hnkjxy.z.onlinetestsystemsdemo.Dao;
import android.content.Context;
import android.os.Environment;
import android.util.Log;
import android.widget.Toast;
import com.hnkjxy.z.onlinetestsystemsdemo.Finally.MyFinally;
import com.hnkjxy.z.onlinetestsystemsdemo.R;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
/**
* Created by zhouweixiong on 2017/12/22.
*/
public class DBWriteSDCard {
private static final String TAG = "ReleaseDataBase";
private Context context;
public DBWriteSDCard(Context context) {
super();
this.context = context;
}
public void whetherToWriteFile() {// 把raw下的sq写进sd卡
// 如果你的储存卡存在并且可用
// 只有在SD卡状态为MEDIA_MOUNTED时/mnt/sdcard目录才是可读可写,并且可以创建目录及文件
// 判断sd卡是否挂载
if (Environment.MEDIA_MOUNTED.equals(Environment
.getExternalStorageState())) {
// 建立sd卡对象
File file = new File(MyFinally.FILE_PAPER_PATH);
// 如果没有此文件夹就创建
if (!file.exists()) {
// 创建
file.mkdirs();
} else {
Log.d(TAG,"sd已有");
}
// 实例化文件名对象
File filePath = new File(MyFinally.FILE_PATH);// 建立sd卡下 的db文件
// 如果文件不存在
if (!filePath.exists()) {
// 不存在就把raw目录下的kaoshi文件通过流读出来,读出来是为了等会好写进去
InputStream in = this.context.getResources().openRawResource(
R.raw.kaoshi);
// 对程序异常奔溃的捕捉 try catch
try {
// FileOutputStream是文件输出流,是用于将数据写入File或 FileDescriptor的输出流
// 实例化文件输入流对象(文件名)
FileOutputStream fileOutputStream = new FileOutputStream(MyFinally.FILE_PATH);// 得到输出流 文件夹下文件路径
// 定义了一个byte类型的数组,数组长度为8192
byte[] buffer = new byte[8192];
int t = 0;
while ((t = in.read(buffer)) != -1) {// 半读边写
fileOutputStream.write(buffer, 0, t);
}
// 开了流就一定要记得关 因为会占用系统资源
// 文件读写完 关闭流
in.close();
fileOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
接下来是一个比较重要重要的类了,解析本地数据库的类,其他功能模块都到了此类里的方法来获取题目
package com.hnkjxy.z.onlinetestsystemsdemo.Dao;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import com.hnkjxy.z.onlinetestsystemsdemo.Finally.MyFinally;
import com.hnkjxy.z.onlinetestsystemsdemo.entityClass.Question;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Random;
/**
* Created by zhouweixiong on 2017/12/21.
*/
public class ExamDao {
//调错用的
private static final String TAG = "ExamDao";
// 用来操控数据表的,获取数据等
// 建议百度 SQLiteDatabase 的详细用法
public static SQLiteDatabase sqLiteDatabase ;
//上下文
// 其他功能模块要用到的
private Context context;
public ExamDao(Context context) {
this.context = context;
}
// 定义一个指数
private static int index = 0;
// 打开数据库的方法
public void openDatabase() {
// 判断如果没有数据表就打开数据表
if (sqLiteDatabase == null) {
// DBWriteSDCard这个类的作用是 判断是否有没有此文件夹和此文件,没有就创建,和写入数据库到SD卡的
// 有的话就不会创建
DBWriteSDCard dbWriteSDCard = new DBWriteSDCard(context);
// whetherToWriteFile方法里面写的就是上述的注释
dbWriteSDCard.whetherToWriteFile();
// 建议详细去了解SQLiteDatabase的方法 MyFinally.FILE_PATH 是MyFinally类写的常量的路径,也就是打开文件的路径
sqLiteDatabase = SQLiteDatabase.openOrCreateDatabase(MyFinally.FILE_PATH, null);
}
}
// 关闭数据库的方法
public void closeDatabase() {
//判断如果打开了就关闭
if (sqLiteDatabase != null) {
// SQLiteDatabase的close方法
sqLiteDatabase.close();
sqLiteDatabase = null;
}
}
// 按顺序查询得到题目的方法
// 返回一个ArrayList<Question>的集合
public ArrayList<Question> sequenceQuery() {
// 实例化一个集合 用来保存数据
ArrayList<Question> arrayList = new ArrayList<Question>();
// 判断有没有打开数据库 没有就打开
if (sqLiteDatabase == null) {
// 上面写的打开数据库的方法
openDatabase();
}
// 判断有没有打开数据库 有就进行操作
if (sqLiteDatabase != null) {
// 对于数据库记录的操作,可以使用Cursor(游标)来进行
// sqLiteDatabase.query 查询数据表,后面的null是查询条件,这里是按顺序获得所有数据
Cursor cursor = sqLiteDatabase.query(MyFinally.TABLE_NAME,
null, null, null, null,
null, null);
//循环 添加下一条
while (cursor != null && cursor.moveToNext()) {
// 把得到的数据添加到数组中去
// 对应Question类的数据 参数是表示列数
arrayList.add(new Question(cursor.getInt(0), cursor.getInt(1),
cursor.getString(2), cursor.getString(3),
cursor.getBlob(4), cursor.getInt(5),
cursor.getString(6), cursor.getString(7), cursor.getString(8),
cursor.getString(9), cursor.getString(10), cursor.getInt(11), cursor.getInt(12),
cursor.getInt(13), cursor.getInt(14)));
}
// 循环执行完关闭cursor
cursor.close();
}
// 关闭数据库的连接
closeDatabase();
// 返回数组(已获得的数据都在数组里面)
return arrayList;
}
//得到所以题目试题
public ArrayList<Question> getAllTopic(int type) {
// 同上面方法注释
ArrayList<Question> arrayList = new ArrayList<Question>();
if (sqLiteDatabase == null) {
openDatabase();
}
if(sqLiteDatabase != null){
// type=0就得到一百题 否则就得到所有题目
if(type==0){
// limit 设置query语句返回行的数量,相当于SQL语句中的“LIMIT”关键字,
// 传递null表示没有设置limit语句。注意格式为String,传递的时候需要传递数字字符串,例如“12
// 这里的代码意思是只能出一百题 type=0就只有一百题
Cursor cursor = sqLiteDatabase.query(MyFinally.TABLE_NAME, null, null, null, null,
null,null,"100" );
while (cursor != null&&cursor.moveToNext()) {
arrayList.add(new Question(cursor.getInt(0),cursor.getInt(1),
cursor.getString(2), cursor.getString(3),
cursor.getBlob(4), cursor.getInt(5),
cursor.getString(6), cursor.getString(7), cursor.getString(8),
cursor.getString(9),cursor.getString(10),cursor.getInt(11),cursor.getInt(12),
cursor.getInt(13),cursor.getInt(14)));
}
cursor.close();
}else{
Cursor cursor = sqLiteDatabase.query(MyFinally.TABLE_NAME, null, null, null, null,
null, null,null);
while (cursor != null&&cursor.moveToNext()) {
arrayList.add(new Question(cursor.getInt(0),cursor.getInt(1),
cursor.getString(2), cursor.getString(3),
cursor.getBlob(4), cursor.getInt(5),
cursor.getString(6), cursor.getString(7), cursor.getString(8),
cursor.getString(9),cursor.getString(10),cursor.getInt(11),cursor.getInt(12),
cursor.getInt(13),cursor.getInt(14)));
}
cursor.close();
}
}
closeDatabase();
return arrayList;
}
// 更新 也就是修改数据 values:代表想要更新的数据 这个方法只传了三个参数进来 修改数据则有四个参数,因为第一个参数
// 表名是固定了的
public int updateQuestion(ContentValues values , String where , String [] param){
int questens=0;
if (sqLiteDatabase == null) {
openDatabase();
}
if(sqLiteDatabase != null){
// sqLiteDatabase更新数据表的方法 第一个参数是表名 第二个是要更新的数据
// 第三第四连起来用的 第三个是哪一条(这里传字段名+占位符,占位符就是?号) 第四个就是那一条的具体数据 就是第三个占位符
questens =sqLiteDatabase.update("t_question", values, where, param);
}
closeDatabase();
return questens;
}
public ArrayList<Question> RandomgetTopic() {
// 注释同上面一样
HashSet<Question> questions = new HashSet<Question>();
if (sqLiteDatabase == null) {
openDatabase();
}
if(sqLiteDatabase != null){
// 先查询数据表
Cursor cursor1 = sqLiteDatabase.query(MyFinally.TABLE_NAME, null, null, null, null,
null, null,null);
// 得到数据表的行数 count是126 意思就是有126条数据
int count = cursor1.getCount();
// 只是这里的差别 随机得到题目
index = count/100;
Random random = new Random();
index= (random.nextInt(index)*10);
Log.d(TAG, "random.nextInt(index);="+index);
Cursor cursor = sqLiteDatabase.query(MyFinally.TABLE_NAME, null, null, null, null,
null, null,index+","+(100));
while (cursor != null&&cursor.moveToNext()) {
questions.add(new Question(cursor.getInt(0),cursor.getInt(1),
cursor.getString(2), cursor.getString(3),
cursor.getBlob(4), cursor.getInt(5),
cursor.getString(6), cursor.getString(7), cursor.getString(8),
cursor.getString(9),cursor.getString(10),cursor.getInt(11),cursor.getInt(12),
cursor.getInt(13),cursor.getInt(14)));
}
cursor.close();
}
closeDatabase();
// set转list
ArrayList<Question> arrayList = new ArrayList<Question>(questions);
return arrayList;
}
}
接下贴一个功能模块的代码,就贴在线考试这个功能模块的代码的,首先是这个功能模块的布局
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.hnkjxy.z.onlinetestsystemsdemo.othersActivity.OnlinetestActivity">
<LinearLayout
android:id="@+id/activity_onlinetest_headings"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="@color/LightBlue"
android:gravity="center"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="在线考试"
android:textColor="@color/white"
android:textSize="20dp" />
</LinearLayout>
<Chronometer
android:id="@+id/activity_onlinetest_countdown"
android:textSize="20dp"
android:gravity="center"
android:textColor="@color/red"
app:layout_constraintTop_toBottomOf="@id/activity_onlinetest_headings"
android:layout_width="match_parent"
android:layout_below="@id/activity_onlinetest_headings"
android:layout_height="30dp" />
<android.support.v4.view.ViewPager
android:id="@+id/activity_onlinetest_viewPager"
android:layout_width="match_parent"
app:layout_constraintTop_toBottomOf="@id/activity_onlinetest_countdown"
android:layout_height="390dp"
>
</android.support.v4.view.ViewPager>
<LinearLayout
android:orientation="horizontal"
app:layout_constraintBottom_toTopOf="parent"
android:layout_width="match_parent"
android:layout_height="60dp">
<TextView
android:id="@+id/activity_onlinetest_their_papers"
android:layout_width="0dp"
android:layout_weight="1"
android:text="交卷"
android:gravity="center"
android:textColor="@color/black"
android:layout_height="match_parent" />
</LinearLayout>
</android.support.constraint.ConstraintLayout>
接下来是代码了,此功能模块还有一个倒计时的功能,因为在线考试嘛,没有时间限制不好,源码会有注释,这里我就不多哗哗
了
package com.hnkjxy.z.onlinetestsystemsdemo.othersActivity;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentValues;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Chronometer;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;
import com.hnkjxy.z.onlinetestsystemsdemo.Dao.ExamDao;
import com.hnkjxy.z.onlinetestsystemsdemo.MainActivity;
import com.hnkjxy.z.onlinetestsystemsdemo.R;
import com.hnkjxy.z.onlinetestsystemsdemo.entityClass.Question;
import java.util.ArrayList;
public class OnlinetestActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener,
View.OnClickListener, RadioGroup.OnCheckedChangeListener, Chronometer.OnChronometerTickListener {
// 定义控件id
private RadioButton onlinetest_radioA;
private RadioButton onlinetest_radioB;
private RadioButton onlinetest_radioC;
private RadioButton onlinetest_radioD;
// 计时器
public Chronometer chronometer;
// 考试题目显示
private TextView onlinetest_show_question;
// 考试图片显示
private ImageView onlinetest_imageview;
// 做题选择
private RadioGroup onlinetest_radioGroup;
private TextView activity_onlinetest_their_papers;
private ViewPager viewpager;
private static ArrayList<View> viewpagelist;
// 对错多少题
private Integer errorCount = 0;
private Integer scoreCount = 0;
private Integer rightCount = 0;
// 指数
private int index;
//倒计时的时间
int minutes = 44, seconds = 59;//45
// 做题的页数,做完滑动的
private Integer viewpagerIndex = 0;
//
private static OnlinetestActivity onlinetestActivity;
// 实例化获得题目的类
private ExamDao examDao;
// 所有考试题目
public static ArrayList<Question> arrayList;
// 答题选项
public int answer;
//
public Intent intent;
// 标记
public int flag;
//自己定义的view的myadapter
private myadapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_onlinetest);
// 实例化ExamDao
examDao = new ExamDao(this);
//
intent = getIntent();
// flag 传参数用的
initData();
init();
}
@SuppressLint("WrongConstant")
public void init() {
viewpager = (ViewPager) findViewById(R.id.activity_onlinetest_viewPager);
viewpager.setOnPageChangeListener(this);
// 实例化自己定义的myadapter
adapter = new myadapter();
// viewpager.setAdapter(new myadapter());
viewpager.setAdapter(adapter);
//初始化交卷
activity_onlinetest_their_papers = (TextView) findViewById(R.id.activity_onlinetest_their_papers);
//初始化时间
chronometer = (Chronometer) findViewById(R.id.activity_onlinetest_countdown);
//初始化时间显示
SharedPreferences shar = getSharedPreferences("saveTime",
Activity.MODE_PRIVATE);
// 倒计时设置 nowtime()是下面写的一个方法
chronometer.setText(nowtime());
chronometer.setVisibility(0);
chronometer.start();
chronometer.setOnChronometerTickListener(this);
// 点击事件
activity_onlinetest_their_papers.setOnClickListener(this);
// 设置页码
viewpager.setCurrentItem(viewpagerIndex);
}
private CharSequence nowtime() {
if (seconds < 10) {
return (minutes + ":0" + seconds);
} else {
return (minutes + ":" + seconds);
}
}
@SuppressLint("WrongConstant")
public void initData() {
arrayList = examDao.getAllTopic(0);// 查询所有题目
LayoutInflater inflter = LayoutInflater.from(this);
StringBuffer questionId = new StringBuffer();
viewpagelist = new ArrayList<View>();
for (int i = 0; i < arrayList.size(); i++) {
Question q = arrayList.get(i);
// arrayList.get(i) 应该改成 Question q = arrayList.get(i); 你那样写有效率问题
if (i == arrayList.size() - 1) {
questionId.append(q.getQuestion_id());
} else {
questionId.append(q.getQuestion_id() + "#");
}
View view = inflter.inflate(R.layout.activity_onlinetest_viewpager, null);
onlinetest_show_question = (TextView) view.findViewById(R.id.onlinetest_show_question);
onlinetest_show_question.setText(q.getLabel() + q.getQuestion());
/**
* 获取图片
*/
onlinetest_imageview = (ImageView) view.findViewById(R.id.onlinetest_imageview);
if (null != q.getBlob()) {
// 0意思是可见的
onlinetest_imageview.setVisibility(0);
// 获取图片
Bitmap bitmap = BitmapFactory.decodeByteArray(q
.getBlob(), 0, q.getBlob().length);
onlinetest_imageview.setImageBitmap(bitmap);
} else {
onlinetest_imageview.setVisibility(8);
// 常量值为4,意思是不可见的
// 常量值为8,意思是不可见的,而且不占用布局空间
}
onlinetest_radioA = view.findViewById(R.id.onlinetest_radioA);
/**
* 获得a选项内容
*/
onlinetest_radioA.setText(q.getOptionA());
onlinetest_radioB = view.findViewById(R.id.onlinetest_radioB);
/**
* 获得b选项内容
*/
onlinetest_radioB.setText(q.getOptionB());
onlinetest_radioC = view.findViewById(R.id.onlinetest_radioC);
/**
* 获得c选项内容
* 为什么C,D会有判断了,因为有的题目是判断题,判断题没有C,D选择 所以判断一下
* 没有内容不要显示C,D选项
*/
if ("null".equals(q.getOptionC())) {
onlinetest_radioC.setVisibility(View.GONE);
} else {
onlinetest_radioC.setText(q.getOptionC());
onlinetest_radioC.setVisibility(0);
}
onlinetest_radioD = view.findViewById(R.id.onlinetest_radioD);
/**
* 获得d选项内容
*/
if ("null".equals(q.getOptionD())) {
onlinetest_radioD.setVisibility(View.GONE);
} else {
onlinetest_radioD.setText(q.getOptionD());
onlinetest_radioD.setVisibility(0);
}
if (q.getOption_type() == 1) {
if (q.getUser_Answer() == 1) {
onlinetest_radioA.setChecked(true);
} else if (q.getUser_Answer() == 2) {
onlinetest_radioB.setChecked(true);
} else if (q.getUser_Answer() == 3) {
onlinetest_radioC.setChecked(true);
} else {
onlinetest_radioD.setChecked(true);
}
onlinetest_radioA.setEnabled(false);
onlinetest_radioB.setEnabled(false);
onlinetest_radioC.setEnabled(false);
onlinetest_radioD.setEnabled(false);
viewpagerIndex += 1;
}
onlinetest_radioGroup = view.findViewById(R.id.onlinetest_radioGroup);
onlinetest_radioGroup.setOnCheckedChangeListener(this);
viewpagelist.add(view);
}
}
// 内部类 viewpager适配器 即做完题左右滑动切换下一道上一道
class myadapter extends PagerAdapter {
@Override
public int getCount() {
// TODO Auto-generated method stub
return viewpagelist.size();
}
@Override
public boolean isViewFromObject(View arg0, Object arg1) {
// TODO Auto-generated method stub
return arg0 == arg1;
}
@Override
public Object instantiateItem(View container, int position) {
((ViewPager) container).addView(viewpagelist.get(position));
return viewpagelist.get(position);
}
@Override
public void destroyItem(View container, int position, Object object) {
((ViewPager) container).removeView(viewpagelist.get(position));
}
}
public void onPageScrollStateChanged(int arg0) {
// TODO Auto-generated method stub
}
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub
}
public void onPageSelected(int v) {
}
@SuppressLint("WrongConstant")
public void onClick(View v) {
// 交卷的点击事件
switch (v.getId()) {
case R.id.activity_onlinetest_their_papers:
Toast.makeText(OnlinetestActivity.this, "得分" + scoreCount + "\r\n对题" + rightCount
+ "\r\n错题" + errorCount, Toast.LENGTH_SHORT).show();
AlertDialog.Builder ad = new AlertDialog.Builder(OnlinetestActivity.this);
ad.setTitle("结束考试?");
ad.setMessage("考试还没结束,是否交卷?\r\n再检查一下看还有未做题吧");
ad.setPositiveButton("是", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int i) {
// 点击是结束当前Activity
finish();
}
});
ad.setNegativeButton("接着考试", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int i) {
// 让对话框从屏幕上消失
dialog.dismiss();
}
});
// 创建对话框
ad.create();
// 让用户必须选择对话框,解决弹出对话框点击空白对话框消失的问题
ad.setCancelable(false);
// 显示对话框
ad.show();
//得分,答错多少题 答对多少题
int score = 0;
int answerCount = 0;
int error = 0;
int size = arrayList.size();
for (int i = 0; i < size; i++) {
Question q = arrayList.get(i);
if (q.getOption_type() == 1) {
answerCount += 1;
if (q.getAnswer() == q.getUser_Answer()) {
score += 10;
} else {
error += 1;
}
}
}
break;
}
}
@SuppressLint("WrongConstant")
public void onCheckedChanged(RadioGroup arg0, int checkid) {
boolean isRigth = false;
boolean isOnChangge = false;
switch (checkid) {
// ABCD四个选项,判断你选择的答案对不对
case R.id.onlinetest_radioA:
isOnChangge = true;
// 选择之后禁止选择选项的点击
noClick();
// 清除选中状态
onlinetest_radioGroup.clearCheck();
// 判断答案是否正确
answer = 1;
if (arrayList.get(viewpager.getCurrentItem()).getAnswer() == answer) {
isRigth = true;
}
// 自动滑到下一页
viewpager.setCurrentItem(viewpager.getCurrentItem() + 1);
break;
case R.id.onlinetest_radioB:
isOnChangge = true;
noClick();
onlinetest_radioGroup.clearCheck();
answer = 2;
if (arrayList.get(viewpager.getCurrentItem()).getAnswer() == answer) {
isRigth = true;
}
viewpager.setCurrentItem(viewpager.getCurrentItem() + 1);
break;
case R.id.onlinetest_radioC:
isOnChangge = true;
noClick();
onlinetest_radioGroup.clearCheck();
answer = 3;
if (arrayList.get(viewpager.getCurrentItem()).getAnswer() == answer) {
isRigth = true;
}
viewpager.setCurrentItem(viewpager.getCurrentItem() + 1);
break;
case R.id.onlinetest_radioD:
isOnChangge = true;
noClick();//1
onlinetest_radioGroup.clearCheck();//2 1和2放在前面才有效果
answer = 4;
if (arrayList.get(viewpager.getCurrentItem()).getAnswer() == answer) {
isRigth = true;
}
viewpager.setCurrentItem(viewpager.getCurrentItem() + 1);
break;
}
if (isRigth) {
// 分数一道题十分
scoreCount += 1;
// 对的题数
rightCount += 1;
} else {
if (isOnChangge) {
// 错题数累加
errorCount += 1;
// Toast.makeText 测试同的
// Toast.makeText(OnlinetestActivity.this, "错,答案"+arrayList.get(viewpager.getCurrentItem()-1).getAnswer(), Toast.LENGTH_SHORT).show();
// ContentValues 是名值对 不是键值对,对我没打错 可能你妹听过名值对,这里建议去百度一下,说不清
ContentValues values = new ContentValues();
values.put("error_type", 1);
// 把错题加入数据库
examDao.updateQuestion(values, "question_id= ?",
new String[]{arrayList.get(viewpager.getCurrentItem() - 1).getQuestion_id() + ""});
// arrayList.get(viewpager.getCurrentItem() - 1).setError_type(1);
}
// 选择完题目判断错题个数 判断如果错了十题就停止考试
// if (errorCount == 10) {
// AlertDialog.Builder ad = new AlertDialog.Builder(OnlinetestActivity.this);
//
// ad.setTitle("模拟考试已结束");
// ad.setMessage("模拟考试结束,因为你已答错十题");
//
// ad.setPositiveButton("离开考试", new DialogInterface.OnClickListener() {
//
// @Override
// public void onClick(DialogInterface dialog, int i) {
// Intent intent = new Intent(OnlinetestActivity.this, MainActivity.class);
// startActivity(intent);
//// 销毁掉当前Activity
// finish();
// }
// });
//
//// 创建对话框
// ad.create();
//// 解决弹出对话框点击空白和返回键消失的问题
// ad.setCancelable(false);
//// 显示对话框
// ad.show();
// return;
//
// }
}
// 更新
if (isOnChangge) {
ContentValues values = new ContentValues();
// 这个
values.put("option_type", 1);
// 记住之前做了的题你选择的答案
values.put("USER_ANSWER", answer);
//LIST 下标示重0开始的 , 应该要减一
arrayList.get(viewpager.getCurrentItem() - 1).setOption_type(1);
}
}
//时间显示
public void onChronometerTick(Chronometer moni_chronometer) {
// TODO Auto-generated method stub
seconds--;
if (seconds == -1) {
minutes--;
seconds = 59;
}
if (minutes == 0 && seconds == 00) {
moni_chronometer.stop();
// 因为一些问题 在倒计时结束时 强制让他变成0:00
// 可以把这行代码注释掉跑下看看
moni_chronometer.setText("0:00");
AlertDialog.Builder ad = new AlertDialog.Builder(OnlinetestActivity.this);
ad.setTitle("结束已考试");
ad.setMessage("考试时间到停止作答");
ad.setPositiveButton("交卷", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int i) {
Intent intent = new Intent(OnlinetestActivity.this, MainActivity.class);
startActivity(intent);
// 销毁掉当前Activity
finish();
}
});
// 创建对话框
ad.create();
ad.setCancelable(false);
// 显示对话框
ad.show();
} else {
moni_chronometer.setTextColor(Color.RED);
moni_chronometer.setText(nowtime());
}
}
/**
* 禁止点击的方法
*/
public void noClick() {
initRadioButton();
onlinetest_radioA.setEnabled(false);
onlinetest_radioB.setEnabled(false);
onlinetest_radioC.setEnabled(false);
onlinetest_radioD.setEnabled(false);
}
/**
* 初始化radiobutton 这个方法决定点击有没有用
*/
public void initRadioButton() {
onlinetest_radioA = viewpagelist.get(viewpager.getCurrentItem()).findViewById(R.id.onlinetest_radioA);
onlinetest_radioB = viewpagelist.get(viewpager.getCurrentItem()).findViewById(R.id.onlinetest_radioB);
onlinetest_radioC = viewpagelist.get(viewpager.getCurrentItem()).findViewById(R.id.onlinetest_radioC);
onlinetest_radioD = viewpagelist.get(viewpager.getCurrentItem()).findViewById(R.id.onlinetest_radioD);
}
// 监听手机自带的按键
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// 如果点击的返回键
if (keyCode == KeyEvent.KEYCODE_BACK) {
AlertDialog.Builder ad = new AlertDialog.Builder(OnlinetestActivity.this);
ad.setTitle("结束考试?");
ad.setMessage("考试还没结束,是否交卷?\r\n再检查一下看还有未做题吧");
ad.setPositiveButton("是", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int i) {
// 点击是结束当前Activity
finish();
}
});
ad.setNegativeButton("接着考试", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int i) {
// 让对话框从屏幕上消失
dialog.dismiss();
}
});
// 创建对话框
ad.create();
// 让用户必须选择对话框,解决弹出对话框点击空白对话框消失的问题
ad.setCancelable(false);
// 显示对话框
ad.show();
}
return false;
}
}
复制上面代码修改一些地方是可以跑出一个功能模块的
希望对大家有帮助,写的不好的地方还请多多指教
对了贴上源码下载地址在线考试系统下载地址