(一)将考试界面分为若干个JPanel组合实现
如图所示我们需要把整个界面大体划分为三个区域,具体分为四个区域,多一个带虚线的倒计时区域。
(二)、具体实现
想要显示试题我们必须先拿到试题,而试题是通过数据库中读出以二维数文本组保存如果直接用的话很不方便,因此我们就需要考虑数据元的创建即试题类(Questions)
1.Questions类:
public class Questions { boolean isChoice;//是否为单选题 boolean isMultipleChoice;//是否为多选题 boolean isJudgement;//是否为判断题 String mContext="";//试题内容 String mChoiceAContext="";//A选项内容 String mChoiceBContext="";//B选项内容 String mChoiceCContext="";//C选项内容 String mChoiceDContext="";//D选项内容 String mImageName="";//图片名称 String mCurrectAnswer="";//正确答案 String mUserAnswer="";//用户答案 String mApplicable="";//适用工程 String mType="";//试题类型 int mID;//初始化数组编号 //设置是否为单选题 public void setIsChoice(boolean isChoice) { this.isChoice=isChoice; } //获取是否为单选题 public boolean getIsChoice() { return this.isChoice; } //设置是否为多选题 public void setIsMultipleChoice(boolean isMultipleChoice) { this.isMultipleChoice=isMultipleChoice; } //获取是否为单选题 public boolean getIsMultipleChoice() { return this.isMultipleChoice; } //设置是否为判断选题 public void setIsJudgement(boolean isJudgement) { this.isJudgement=isJudgement; } //得到是否为判断选题 public boolean getIsJudgement() { return this.isJudgement; } //设置题目内容 public void setContext(String context) { this.mContext=context; } //得到题目内容 public String getContext() { return this.mContext; } //设置图片名字 public void setImageName(String imageName) { this.mImageName=imageName; } //得到图片名字 public String getImageName() { return this.mImageName; } //设置a选项内容 public void setChoiceAContext(String ChoiceAContext) { this.mChoiceAContext=ChoiceAContext; } //得到a选项内容 public String getChoiceAContext() { return this.mChoiceAContext; } //b选项内容 public void setChoiceBContext(String ChoiceBContext) { this.mChoiceBContext=ChoiceBContext; } //得到b选项内容 public String getChoiceBContext() { return this.mChoiceBContext; } //c选项内容 public void setChoiceCContext(String ChoiceCContext) { this.mChoiceCContext=ChoiceCContext; } //得到c选项内容 public String getChoiceCContext() { return this.mChoiceCContext; } //d选项内容 public void setChoiceDContext(String ChoiceDContext) { this.mChoiceDContext=ChoiceDContext; } //得到d选项内容 public String getChoiceDContext() { return this.mChoiceDContext; } //设置正确答案 public void setCurrectAnswer(String CurrectAnswer) { this.mCurrectAnswer=CurrectAnswer; } //得到正确答案 public String getCurrectAnswer() { return this.mCurrectAnswer; } //设置用户答案 public void setUserAnswer(String userAnswer) { this.mUserAnswer=userAnswer; } //得到用户答案 public String getUserAnswer() { return this.mUserAnswer; } //设置适用工程 public void setApplicable(String applicable) { this.mApplicable=applicable; } //得到适用工程 public String getApplicable() { return this.mApplicable; } //设置题型 public void setType(String type) { this.mType=type; } //得到题型 public String getType() { return this.mType; } //设置题目序号 public void setNumber(int number) { this.mID=number; } //得到题目序号 public int getNumber() { return this.mID; } }
我们知道试题肯定不止一道因此就需要一个试题数组来存放所有试题,因此我们还需要一个模型来管理所有试题
2.QuestionModel:
public class QuestionModel { private Questions[] mQuestions=null; int mIndex=0; //设置全部试题 public void setQuestions(Questions[] Questions) { this.mQuestions=Questions; } //返回所有试题 public Questions[] getAllQuestions() { if(mQuestions==null) { return null; }else if(mQuestions.length==0) { return null; } return this.mQuestions; } //返回对应索引试题 public Questions getQuestions(int index) { if(mQuestions==null) { return null; }else if(mQuestions.length==0) { return null; }else if(index>=mQuestions.length||index<0) { return null; } return this.mQuestions[index]; } //获取下一题 public Questions getNextQuestion() { mIndex++; if(mQuestions==null) { return null; }else if(mQuestions.length==0) { return null; }else if(mIndex==mQuestions.length) { mIndex=0; } return this.mQuestions[mIndex]; } //获取上一题 public Questions getPreviousQuestion() { mIndex--; if(mQuestions==null) { return null; }else if(mQuestions.length==0) { return null; }else if(mIndex<0) { mIndex=mQuestions.length-1; } return this.mQuestions[mIndex]; } }
有了上述两个类后我们就可以很轻松的管理所有试题了,接下来就需要对每一个视图去写一个相关的数据处理类
3.QuestionHandle对应上图左边最大矩形的视图的数据处理
public class QuestionHandle implements ActionListener { QuestionView mView; QuestionModel mModel; Questions mQuestions; Questions[] mAllQuestions; QuestionNumberView mQuestionNumberView; int selectNumbers=0;//勾选数量 String userAnswer="";//用户答案 int questionNumbers=0;//试题数量 int number;//保存试题对应序号 //绑定试题选择视图 public void setQuestionNumberView(QuestionNumberView questionNumberView) { this.mQuestionNumberView=questionNumberView; } //绑定试题视图 public void setQuestionView(QuestionView view) { this.mView=view; } //得到试题在数组中的序号 public void setQuestionNumbers(int number) { questionNumbers=number; } //得到试题模型 public void setQuestionModel(QuestionModel questionModel) { this.mModel=questionModel; } //得到相应问题 public void setmQuestions(Questions questions) { this.mQuestions = questions; } @Override public void actionPerformed(ActionEvent e) { //不同题型勾选答案数量并不一样,所以要进行判断 if(mQuestions.isChoice) { if(mView.mCheckBoxA.isSelected()) { userAnswer="A"; selectNumbers++; } if(mView.mCheckBoxB.isSelected()) { userAnswer="B"; selectNumbers++; } if(mView.mCheckBoxC.isSelected()) { userAnswer="C"; selectNumbers++; } if(mView.mCheckBoxD.isSelected()) { userAnswer="D"; selectNumbers++; } if(selectNumbers>1&&!userAnswer.equals("")) { JOptionPane.showMessageDialog (null,"保存试题失败!!!该提为单选题!请查看是否选择了多个选项!或者未选择答案","消息对话框", JOptionPane.WARNING_MESSAGE); }else { number=mQuestions.getNumber(); mQuestionNumberView.buttons[number].setForeground(Color.GREEN); mQuestions.setUserAnswer(userAnswer); } selectNumbers=0; }else if(mQuestions.isMultipleChoice) { StringBuilder sBuilder=new StringBuilder(); if(mView.mCheckBoxA.isSelected()) { sBuilder.append("A"); selectNumbers++; } if(mView.mCheckBoxB.isSelected()) { sBuilder.append("B"); selectNumbers++; } if(mView.mCheckBoxC.isSelected()) { sBuilder.append("C"); selectNumbers++; } if(mView.mCheckBoxD.isSelected()) { sBuilder.append("D"); selectNumbers++; } if(selectNumbers==1) { JOptionPane.showMessageDialog (null,"保存试题失败!!!该提为多选题!请查看是否只选择了一个选项!","消息对话框", JOptionPane.WARNING_MESSAGE); }else { number=mQuestions.getNumber(); mQuestionNumberView.buttons[number].setForeground(Color.GREEN); mQuestions.setUserAnswer(sBuilder.toString()); } selectNumbers=0; }else if(mQuestions.isJudgement) { if(mView.mCheckBoxA.isSelected()) { userAnswer="A"; selectNumbers++; } if(mView.mCheckBoxB.isSelected()) { userAnswer="B"; selectNumbers++; } if(selectNumbers>1) { JOptionPane.showMessageDialog (null,"保存试题失败!!!该提为判断题!请查看是否选择了多个选项!","消息对话框", JOptionPane.WARNING_MESSAGE); }else { number=mQuestions.getNumber(); mQuestionNumberView.buttons[number].setForeground(Color.GREEN); mQuestions.setUserAnswer(userAnswer); } selectNumbers=0; } } }
4.QuestionNumberHandle类对应上图右上角试题选择区域的数据处理
public class QuestionNumberHandle implements ActionListener{ QuestionNumberView mView; QuestionModel mModel; Questions mQuestions; QuestionView mQuestionView; ExamImageView examImageView;//图片显示视图 ImageIcon icon; Image temp; String imagePath="./试题图片/"; int index; //绑定窗口 public void setQuestionNumberView(QuestionNumberView view) { this.mView=view; } public void setQuestionView(QuestionView view) { this.mQuestionView=view; } public void setQuestionModel(QuestionModel questionModel) { this.mModel=questionModel; } public void setQestion(Questions questions) { this.mQuestions=questions; } @Override public void actionPerformed(ActionEvent e) { //获取是几号按钮触发 index=Integer.parseInt(e.getActionCommand()); //找到该索引对应试题 mQuestions=mModel.getQuestions(index); if(mQuestions.getIsJudgement()) { mQuestionView.mJLabelC.setVisible(false); mQuestionView.mJLabelD.setVisible(false); mQuestionView.mTextC.setVisible(false); mQuestionView.mTextD.setVisible(false); mQuestionView.mCheckBoxC.setVisible(false); mQuestionView.mCheckBoxD.setVisible(false); }else if(mQuestions.getIsMultipleChoice()){ mQuestionView.mJLabelC.setVisible(true); mQuestionView.mJLabelD.setVisible(true); mQuestionView.mTextC.setVisible(true); mQuestionView.mTextD.setVisible(true); mQuestionView.mCheckBoxC.setVisible(true); mQuestionView.mCheckBoxD.setVisible(true); }else if(mQuestions.getIsChoice()) { mQuestionView.mJLabelC.setVisible(true); mQuestionView.mJLabelD.setVisible(true); mQuestionView.mTextC.setVisible(true); mQuestionView.mTextD.setVisible(true); mQuestionView.mCheckBoxC.setVisible(true); mQuestionView.mCheckBoxD.setVisible(true); } //保存用户选择答案信息 if(mQuestions.getUserAnswer().equals("")||mQuestions.getUserAnswer()==null) { mQuestionView.mCheckBoxA.setSelected(false); mQuestionView.mCheckBoxB.setSelected(false); mQuestionView.mCheckBoxC.setSelected(false); mQuestionView.mCheckBoxD.setSelected(false); }else { mQuestionView.mCheckBoxA.setSelected(false); mQuestionView.mCheckBoxB.setSelected(false); mQuestionView.mCheckBoxC.setSelected(false); mQuestionView.mCheckBoxD.setSelected(false); if(mQuestions.getUserAnswer().contains("A")) { mQuestionView.mCheckBoxA.setSelected(true); } if(mQuestions.getUserAnswer().contains("B")) { mQuestionView.mCheckBoxB.setSelected(true); } if(mQuestions.getUserAnswer().contains("C")) { mQuestionView.mCheckBoxC.setSelected(true); } if(mQuestions.getUserAnswer().contains("D")) { mQuestionView.mCheckBoxD.setSelected(true); } } //更新试题视图 mQuestionView.mContext.setText(mQuestions.getContext()); icon=new ImageIcon(imagePath+mQuestions.getImageName()); temp=icon.getImage().getScaledInstance(300,200, icon.getImage().SCALE_DEFAULT); icon=new ImageIcon(temp); mQuestionView.mQuestionHandle.setmQuestions(this.mQuestions); mQuestionView.mImageView.setImageNmae(mQuestions.getImageName()); mQuestionView.mImageView.showImageJLabel.setIcon(icon); mQuestionView.mTextA.setText(mQuestions.getChoiceAContext()); mQuestionView.mTextB.setText(mQuestions.getChoiceBContext()); mQuestionView.mTextC.setText(mQuestions.getChoiceCContext()); mQuestionView.mTextD.setText(mQuestions.getChoiceDContext()); mQuestionView.mQusetionType.setText(mQuestions.getType()); mQuestionView.validate(); } }
5.ControlHandle对应上图右下角倒计时和交卷视图的数据处理
package MyExaminationSystem; import java.awt.Image; import java.awt.event.*; import javax.swing.ImageIcon; import javax.swing.JOptionPane; import javax.swing.JScrollPane; import javax.swing.JTextArea; public class ControlHandle implements ActionListener{ ControlView mControlView; QuestionView mQuestionView; QuestionModel mQuestionModel; Questions mQuestions; Questions[] mAllQuestions; TimeView mTimeView; int count=0;//计算试题正确数量 ImageIcon icon;//试题中带有的图片以图标形式显示 Image temp; String imagePath="./试题图片/";//图片路径父目录 //绑定试题模型 public void setQuestionModel(QuestionModel model) { this.mQuestionModel=model; } //绑定QuestionView视图 public void setQuestionView(QuestionView view) { this.mQuestionView=view; } //绑定ControlView视图 public void setControlView(ControlView view) { this.mControlView=view; } //绑定TimeView视图 public void setTimeView(TimeView timeView) { mTimeView=timeView; } @Override public void actionPerformed(ActionEvent e) { //是否点击提交按钮 if(e.getActionCommand().equals("submit")) { //获取所有试题 mAllQuestions=mQuestionModel.getAllQuestions(); //判题 for(int i=0;i<mAllQuestions.length;i++) { if(mAllQuestions[i].getUserAnswer().equals(mAllQuestions[i].getCurrectAnswer())) { count++; } } //使倒计时停止计时 mTimeView.isOK=true; //使提交按钮不可以继续按下 mControlView.submitBN.setEnabled(false); //弹出对话框告知用户答对数量 JOptionPane.showMessageDialog(null,"您答对了:"+count+"道题!","成绩",JOptionPane.OK_OPTION); } } }
这里需要单独说明试题带图像时图像显示类,我为了将图片显示时统一大小就将图片作为JLable的图标来显示,而且用户点击图片后图片可以在一个大窗口中放大显示
6.ExamIamgeView
public class ExamImageView extends JPanel implements MouseListener{ String Path="./试题图片/";//试题图片父目录 JLabel showImageJLabel;//显示图片所用标签 ImageIcon icon;//将图片转换为图标 Image temp; String imagePath;//图片完整路径 String name;//图片名称 //设置图片名称 public void setImageNmae(String name) { //将图片路径补充完整 imagePath=Path+name; } //显示图片 public void showImage() { //这里将图片转换为图标的目的是为了使图片大小更容易控制,而且重绘方便 icon=new ImageIcon(imagePath); temp=icon.getImage().getScaledInstance(showImageJLabel.getWidth(), showImageJLabel.getHeight(), icon.getImage().SCALE_DEFAULT); icon=new ImageIcon(temp); //将图片绘制到标签上 showImageJLabel.setIcon(icon); add(showImageJLabel); //监听鼠标点击事件,在点击后放大图片 addMouseListener(this); } ExamImageView() { showImageJLabel=new JLabel(); showImageJLabel.setBounds(0, 0, 300,200 ); } @Override public void mousePressed(MouseEvent e) { //showImageFrame窗口负责放大图片 ShowImageFrame showImageFrame=new ShowImageFrame(imagePath); } @Override public void mouseClicked(MouseEvent e) { } @Override public void mouseReleased(MouseEvent e) {} @Override public void mouseEntered(MouseEvent e) {} @Override public void mouseExited(MouseEvent e) {} }
放大图片并显示的类与这个类相似,只是继承了JFrame所以代码就不放了
倒计时显示类,这个类要处理耗时任务所以要在新线程中执行
7.TimeView
package MyExaminationSystem; import java.awt.Color; import java.awt.Font; import javax.swing.*; public class TimeView extends JPanel{ private JFrame frame; private JLabel hourLabel; private JLabel minuteLabel; private JLabel secondsLabel; private JTextField minuteField; long time; long hour; long minute; long seconds; ControlView mView; QuestionModel mQuestionModel; Questions[] mAllQuestions; int count=0; boolean isOK=false; public void setControlView(ControlView view) { this.mView=view; } public void setQuestionModel(QuestionModel questionModel) { this.mQuestionModel=questionModel; } //获取倒计时时间,在新线程中执行否则会阻塞主线程 public void getTime(final long minuteInput) { new Thread(new Runnable() { @Override public void run() { time= minuteInput*60; // 自定义倒计时时间 hour= 0; minute= 0; seconds = 0; while (time >= 0) { //判断是否用户点击了交卷 if(isOK==false) { hour = time / 3600; minute = (time - hour * 3600) / 60; seconds = time - hour * 3600 - minute * 60; hourLabel.setText(hour + "小时"); minuteLabel.setText(minute + "分钟"); secondsLabel.setText(seconds + "秒"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } time--; }else { break; } } if(time==0) { mView.submitBN.setEnabled(false); mAllQuestions=mQuestionModel.getAllQuestions(); for(int i=0;i<mAllQuestions.length;i++) { if(mAllQuestions[i].getUserAnswer().equals(mAllQuestions[i].getCurrectAnswer())) { count++; } } JOptionPane.showMessageDialog(null,"您答对了:"+count+"道题!","成绩",JOptionPane.OK_OPTION); } } }).start(); } private void init() { hourLabel = new JLabel(""); hourLabel.setForeground(Color.RED); hourLabel.setFont(new Font("", 1, 30)); minuteLabel = new JLabel(""); minuteLabel.setForeground(Color.RED); minuteLabel.setFont(new Font("", 1, 30)); secondsLabel = new JLabel(""); secondsLabel.setForeground(Color.RED); secondsLabel.setFont(new Font("", 1, 30)); add(hourLabel); add(minuteLabel); add(secondsLabel); } public TimeView(){ init(); setVisible(true); } }
到这里就基本介绍完了考试界面的各模块的实现,只是部分视图类没有写,需要的话请去下载源码自己查看。
点击下载源码ExamProject
打赏
微信扫一扫~听说打赏我的人,都进了福布斯排行榜!