我正在开发Android测验游戏。我试图生成尚未被挑选的随机测验问题。
My Main class(不适合这里,对不起)。
您可以在导致崩溃的public void GenerateQuiz()
中找到执行操作。
该应用程序在没有do-while功能的情况下工作正常,因此必定有问题。
它只会使6./7./8上的应用程序崩溃。随机问题,但它至少起作用。
它的作用只是检查是否已经提出了随机问题。
如果是 - >生成一个新的随机问题,直到它成为一个新的随机问题。以前没有被问过。
如果没有 - >那将是下一个问题。
段:
public void GenerateQuiz() {
do{
QuizID = ShuffleQuiz();
}while(CheckIfQuestionIsNew(QuizID)==false);
有3个困难:简单,中等和困难。他们每个人只有10个问题=总共30个问题。通过随机生成的1-10个INT生成问题。 用户完成10个问题后,应用程序会将难度更改为次高。示例:如果问题10(简单)的回答正确,则会将难度更改为MEDIUM。完成第10个MEDIUM问题后,它将变为HARD。
已更新 LogCat错误消息一旦崩溃:
05-12 16:45:00.232 14067-14067 /? E / ClockAlarmWidget: [AlarmWidgetIdManager] getListItem():itemIndex = 0,widgetID:1 05-12 16:45:00.232 14067-14067 /? E / ClockAlarmWidget:[AlarmWidgetIdManager] getListItem():ItemIndex超过ListItemCount。 itemIndex = 1 05-12 16:45:00.232 14067-14067 /? E / ClockAlarmWidget:[AlarmWidgetIdManager] getListItem():itemIndex = 1,widgetID:1
来源:
boolean CheckIfQuestionIsNew(int element) {
List<Integer> ListDifficulty = new ArrayList<Integer>();
//#########GET ARRAYLIST#########
//Determine the Difficulty since each Difficulty got it's own arraylist.
if (QuizDifficulty==1){//Example: If Difficulty==1, copy it's arrays to the new list of array.
ListDifficulty.addAll(QuizIDsPassedD1);
}else if (QuizDifficulty==2){
ListDifficulty.addAll(QuizIDsPassedD2);
}else if (QuizDifficulty==3){
ListDifficulty.addAll(QuizIDsPassedD3);
}
if (ListDifficulty.contains(element))
return false;
//#########UPDATE ARRAYLIST#########
// If Question was not asked before then --> Add the new question ID to the arraylist
ListDifficulty.add(element);
//#########SAVE NEW ARRAYLIST#########
//Now it needs to determine the difficulty aggain to update its arraylist with the new items.
if (QuizDifficulty==1){
QuizIDsPassedD1.removeAll((QuizIDsPassedD1));//Remove All (Double Make Sure)
QuizIDsPassedD1.addAll(ListDifficulty);//Transfer new Arraylist to the difficultyies array list
}else if (QuizDifficulty==2){
QuizIDsPassedD2.removeAll((QuizIDsPassedD2));
QuizIDsPassedD2.addAll(ListDifficulty);
}else if (QuizDifficulty==3){
QuizIDsPassedD3.removeAll((QuizIDsPassedD3));
QuizIDsPassedD3.addAll(ListDifficulty);
}
return true;
}
答案 0 :(得分:0)
而是使用下面的代码,在此之前检查CheckIfQuestionIsNew(QuizID)是否返回布尔值
do{
QuizID = ShuffleQuiz();
}while(CheckIfQuestionIsNew(QuizID));
答案 1 :(得分:0)
在“DO / WHILE”循环中,大括号之间的代码至少执行一次。因此,如果你得到一个例外,这应该是因为你不应该进入循环。考虑使用while子句吗?
如果这是导致崩溃的大括号之间的一行,请添加方法的代码。
请同时添加logcat。
答案 2 :(得分:0)
问题是OutOfMemoryException
:
java.lang.OutOfMemoryError at java.util.ArrayList.addAll(ArrayList.java:194) at
de.hackert.wwequiz2014.QuizScreen.CheckIfQuestionIsNew(QuizScreen.java:49) at
这是因为在ArrayList
方法中向CheckIfQuestionIsNew()
添加项目。很可能在那里的某个地方存在一个无限循环,因为你必须添加大量项目 - 我会说成千上万到数百万 - 到ListView
来获得OutOfMemoryException
。我会仔细查看您的代码,但我认为您可以在熟悉代码时更快地识别出确切的错误。
我想我发现了错误。
你到底想做什么:
ListDifficulty = QuizIDsPassedD1;
ListDifficulty.addAll(QuizIDsPassedD1);
对我来说没有多大意义。这将一遍又一遍地复制列表中的所有项目与do / while循环相结合,这很可能是罪魁祸首。
我评论了CheckIfQuestionIsNew()
的代码:
boolean CheckIfQuestionIsNew(int element) {
List<Integer> ListDifficulty = new ArrayList<Integer>();
// What is this line suppsed to do? You are creating a new ArrayList, it is already empty why would you want to remove somthing here?
ListDifficulty.removeAll(ListDifficulty);
if (QuizDifficulty==1){
// Now you are adding items to the new list for reasons I don't understand
ListDifficulty.addAll(QuizIDsPassedD1);
}else if (QuizDifficulty==2){
// Same here
ListDifficulty.addAll(QuizIDsPassedD2);
}else if (QuizDifficulty==3){
// Same here
ListDifficulty.addAll(QuizIDsPassedD3);
}
if (ListDifficulty.contains(element))
return false;
// Where does this code belong? It is not part of the if statement above
// I added empty lines around it to make it more clear that this is a statement which is not contained in any if statement
ListDifficulty.add(element);
if (QuizDifficulty==1){
// What are you doing here? You remove all the items from the list and than add the List from above?
// This code does absolutely nothing and makes the ifs at the top and the ifs right here completely useless
QuizIDsPassedD1.removeAll((QuizIDsPassedD1));
QuizIDsPassedD1.addAll(ListDifficulty);
}else if (QuizDifficulty==2){
// Same here
QuizIDsPassedD2.removeAll((QuizIDsPassedD2));
QuizIDsPassedD2.addAll(ListDifficulty);
}else if (QuizDifficulty==3){
// Same here
QuizIDsPassedD3.removeAll((QuizIDsPassedD3));
QuizIDsPassedD3.addAll(ListDifficulty);
}
return true;
}
我想我的主要困惑来自于:
你在do / while循环中调用ShuffleQuiz();
,但出于什么目的?在这里想要实现什么?如果您只是想要一个或多个已经/未经过回答的问题,那么为什么不实施ShuffleQuiz()
直接返回新问题而不是诉诸于此 - 我想 - 随机挑选然后检查如果它循环好吗?
好的,我已经改进了你的代码。您的旧代码超过770行代码,我的改进版本仅低于210行代码。首先,我创建了两个类Question
和Answer
。它们分别保存与一个问题和一个答案相关的所有数据。 Question
类看起来像这样:
public class Question {
private final int imageResId;
private final int questionTextResId;
private final List<Answer> answers = new ArrayList<Answer>();
public Question(int questionTextResId, int imageResId) {
this.imageResId = imageResId;
this.questionTextResId = questionTextResId;
}
public Question addAnswer(int answerTextResId, boolean correct) {
Answer answer = new Answer(answerTextResId, correct);
this.answers.add(answer);
return this;
}
public int getQuestionTextResId() {
return questionTextResId;
}
public int getImageResId() {
return imageResId;
}
public List<Answer> getAnswers() {
return answers;
}
}
你可以看到没什么特别的。它具有成员变量作为问题文本的资源ID,图像的资源ID以及List
Answers
。另外,我添加了一个addAnswer()
方法来方便地添加问题的答案。
Answer
类看起来像这样:
public class Answer {
private final int answerTextResId;
private final boolean correct;
public Answer(int answerTextResId, boolean correct) {
this.answerTextResId = answerTextResId;
this.correct = correct;
}
public int getAnswerTextResId() {
return answerTextResId;
}
public boolean isCorrect() {
return correct;
}
}
正如你再也看不到什么特别的,但这里只有两个成员变量,一个是答案文本的资源id,另一个是布尔值,如果这个答案是否正确。
在向您展示我改进的QuizScreen
Activity
的完整代码之前,我将向您解释我所做的所有更改及其工作原理。首先,我为您使用的所有Views
创建了成员变量。你不应该经常打电话给findViewById()
。在成员变量中保存引用时,您不必再次调用findViewById()
:
private Button buttonAntwort1;
private Button buttonAntwort2;
private Button buttonAntwort3;
private Button buttonAntwort4;
private TextView textViewFrage;
private ImageView imageViewBild;
private Button[] answerButtons;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_quiz_screen);
this.imageViewBild = (ImageView) findViewById(R.id.imageViewBild);
this.textViewFrage = (TextView) findViewById(R.id.textViewFrage);
this.buttonAntwort1 = (Button) findViewById(R.id.buttonAntwort1);
this.buttonAntwort2 = (Button) findViewById(R.id.buttonAntwort2);
this.buttonAntwort3 = (Button) findViewById(R.id.buttonAntwort3);
this.buttonAntwort4 = (Button) findViewById(R.id.buttonAntwort4);
this.answerButtons = new Button[] { this.buttonAntwort1, this.buttonAntwort2, this.buttonAntwort3, this.buttonAntwort4 };
createQuestions();
startGame();
}
如您所见,我还创建了一个Button[]
,其中包含可用于回答问题的所有Buttons
。我的解决方案在这方面是完全灵活的。您可以Questions
拥有尽可能多Answers
个Question
,并且每个Answers
可以拥有不同数量的Buttons
。您只需要确保Button[]
中有足够的Question
。如果您希望Buttons
包含4个以上的答案,只需根据需要Button[]
添加Questions
,然后按照下面的说明添加Buttons
。其余的是完全自动的,您不必担心有太多Buttons
。 Question
不需要/使用的所有Lists
会因隐藏而自动隐藏。
我创建了3个List
,其中每个都包含一个难度级别的所有问题,另外一个private final List<Question> easyQuestions = new ArrayList<Question>();
private final List<Question> mediumQuesitons = new ArrayList<Question>();
private final List<Question> hardQuestions = new ArrayList<Question>();
private final List<Question> questionQueue = new ArrayList<Question>();
将是一个问题队列。当游戏实际运行时,我们一个接一个地通过问题队列。问题队列在游戏开始时生成一次,因此我们不必随时挑选问题并检查问题是否得到了解答。
Lists
我还添加了3种便捷方法,可以轻松地将Questions
与private Question newEasy(int questionTextResId, int imageResId) {
Question question = new Question(questionTextResId, imageResId);
this.easyQuestions.add(question);
return question;
}
private Question newMedium(int questionTextResId, int imageResId) {
Question question = new Question(questionTextResId, imageResId);
this.mediumQuesitons.add(question);
return question;
}
private Question newHard(int questionTextResId, int imageResId) {
Question question = new Question(questionTextResId, imageResId);
this.hardQuestions.add(question);
return question;
}
一起填充:
createQuestions()
在方法Questions
中,所有Questions
都将使用这3种便捷方法创建,正如我已经说过的,我没有复制源代码中的private void createQuestions() {
newEasy(R.string.question1_text, R.drawable.question1_picture1)
.addAnswer(R.string.question1_answer1, false).addAnswer(R.string.question1_answer2, true)
.addAnswer(R.string.question1_answer3, false).addAnswer(R.string.question1_answer4, false);
newMedium(R.string.question2_text, R.drawable.question2_picture1)
.addAnswer(R.string.question2_answer1, false).addAnswer(R.string.question2_answer2, true)
.addAnswer(R.string.question2_answer3, false).addAnswer(R.string.question2_answer4, false);
newHard(R.string.question3_text, R.drawable.question3_picture1)
.addAnswer(R.string.question3_answer1, false).addAnswer(R.string.question3_answer2, true)
.addAnswer(R.string.question3_answer3, false).addAnswer(R.string.question3_answer4, false);
}
,你将不得不在这里再次添加它们:
newEasy()
如果您想要添加简约Question
,newMedium()
,如果您想添加媒介Question
或{{1},则可以看到newHard()
如果你想添加一个硬Question
。您可以通过菊花链式调用addAnswer()
来向Answers
添加Question
个Questions
。这应该都是非常自我解释的。
创建startGame()
之后,将调用startGame()
private void startGame() {
Collections.shuffle(this.easyQuestions);
Collections.shuffle(this.mediumQuesitons);
Collections.shuffle(this.hardQuestions);
this.questionQueue.clear();
this.questionQueue.addAll(this.easyQuestions);
this.questionQueue.addAll(this.mediumQuesitons);
this.questionQueue.addAll(this.hardQuestions);
this.questionIndex = 0;
moveToQuestion(0);
}
。您可以随时通过调用Collections.shuffle()
重新启动游戏,因此如果您想添加该功能以重新启动游戏,您可以非常简单地完成游戏。 startGame()方法如下所示:
Lists
顶部的questionQueue
随机播放clear()
,它会随机重新排序元素。在中间我们创建了questionIndex
。首先我们questionIndex
删除之前游戏中的所有问题,然后我们首先添加简单问题,然后是中等问题,最后是硬问题。我们将quesitonQueue
重置为0. moveToQuestion(0);
会跟踪我们在moveToQuestion()
中的位置。最后在底部我们调用private void moveToQuestion(int index) {
// First we check if we have reached the end of the queue
if(index < this.questionQueue.size()) {
// If not we get the current question
Question question = this.questionQueue.get(index);
// Here we set the question text to the TextView
int questionTextResId = question.getQuestionTextResId();
this.textViewFrage.setText(questionTextResId);
// And here the question image to the ImageView.
int imageResId = question.getImageResId();
this.imageViewBild.setImageResource(imageResId);
// We get the answers from the question and create two count variables for convenience
List<Answer> answers = question.getAnswers();
int answerCount = answers.size();
int buttonCount = this.answerButtons.length;
// We start a loop through all the answer buttons
for(int i = 0; i < buttonCount; i++) {
// We get the current button from the Button[] which contains all the answer buttons
Button button = this.answerButtons[i];
// There might not be as many answers as there are buttons, that's what we check here
if(i < answerCount) {
// If there is an answer for this button make it visible
button.setVisibility(View.VISIBLE);
// We get the answer and bind to the button by calling bindAnswerToButton()
Answer answer = answers.get(i);
bindAnswerToButton(button, answer);
} else {
// If no answer exists for the Button we make it invisible.
button.setVisibility(View.GONE);
}
}
} else {
// We have reached the end of the queue
// You have to decide what happens when the game is won
Toast toast = Toast.makeText(this, R.string.game_won, Toast.LENGTH_SHORT);
toast.show();
}
}
转到队列中的第一个问题。
bindAnswerToButton()
方法再次非常直接,但这一次,因为这个方法稍微复杂一点,我会添加注释来解释它。方法如下所示:
OnClickListener
在Button
我们设置文字和private void bindAnswerToButton(Button button, Answer answer) {
int answerTextResId = answer.getAnswerTextResId();
button.setText(answerTextResId);
button.setOnClickListener(new AnswerClickListener(answer));
}
的{{1}}:
OnClickListener
正如您所看到的,OnClickListener
是自定义的,并在其构造函数中将答案作为参数。此自定义OnClickListener
验证了我们的答案,并检查我们是否选择了正确的答案。自定义private class AnswerClickListener implements View.OnClickListener {
private final Answer answer;
private AnswerClickListener(Answer answer) {
this.answer = answer;
}
@Override
public void onClick(View v) {
if(this.answer.isCorrect()) {
gotoNextQuestion();
} else {
// You have to decide what happens when someone picks the wrong answer
Toast toast = Toast.makeText(QuizScreen.this, R.string.toast_wrong_answer, Toast.LENGTH_SHORT);
toast.show();
}
}
}
如下所示:
onClick
它唯一真正做的是检查gotoNextQuestion()
,如果传递给它的答案是正确的,并且是调用Toast
来转移到下一个问题。如果答案目前不正确,则只会显示gotoNextQuestion()
。在这种情况下,你必须决定你想要发生什么。
questionIndex
只是一种方便的方法,只需增加我们的moveToQuestion
,然后使用递增的questionIndex
调用Quesiton
以移至下一个{{1} }}:
private void gotoNextQuestion() {
this.questionIndex++;
moveToQuestion(this.questionIndex);
}
这就是它。这就是整个代码。请记住,您要做的唯一事情是在createQuestions()
中添加所有问题,如上所述。以下是QuizScreen
Activity
的完整源代码:
public static class QuizScreen extends ActionBarActivity {
private final List<Question> easyQuestions = new ArrayList<Question>();
private final List<Question> mediumQuesitons = new ArrayList<Question>();
private final List<Question> hardQuestions = new ArrayList<Question>();
private final List<Question> questionQueue = new ArrayList<Question>();
private int questionIndex = 0;
private Button buttonAntwort1;
private Button buttonAntwort2;
private Button buttonAntwort3;
private Button buttonAntwort4;
private TextView textViewFrage;
private ImageView imageViewBild;
private Button[] answerButtons;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_quiz_screen);
this.imageViewBild = (ImageView) findViewById(R.id.imageViewBild);
this.textViewFrage = (TextView) findViewById(R.id.textViewFrage);
this.buttonAntwort1 = (Button) findViewById(R.id.buttonAntwort1);
this.buttonAntwort2 = (Button) findViewById(R.id.buttonAntwort2);
this.buttonAntwort3 = (Button) findViewById(R.id.buttonAntwort3);
this.buttonAntwort4 = (Button) findViewById(R.id.buttonAntwort4);
this.answerButtons = new Button[] { this.buttonAntwort1, this.buttonAntwort2, this.buttonAntwort3, this.buttonAntwort4 };
createQuestions();
startGame();
}
private void createQuestions() {
newEasy(R.string.question1_text, R.drawable.question1_picture1)
.addAnswer(R.string.question1_answer1, false).addAnswer(R.string.question1_answer2, true)
.addAnswer(R.string.question1_answer3, false).addAnswer(R.string.question1_answer4, false);
newMedium(R.string.question2_text, R.drawable.question2_picture1)
.addAnswer(R.string.question2_answer1, false).addAnswer(R.string.question2_answer2, true)
.addAnswer(R.string.question2_answer3, false).addAnswer(R.string.question2_answer4, false);
newHard(R.string.question3_text, R.drawable.question3_picture1)
.addAnswer(R.string.question3_answer1, false).addAnswer(R.string.question3_answer2, true)
.addAnswer(R.string.question3_answer3, false).addAnswer(R.string.question3_answer4, false);
}
private Question newEasy(int questionTextResId, int imageResId) {
Question question = new Question(questionTextResId, imageResId);
this.easyQuestions.add(question);
return question;
}
private Question newMedium(int questionTextResId, int imageResId) {
Question question = new Question(questionTextResId, imageResId);
this.mediumQuesitons.add(question);
return question;
}
private Question newHard(int questionTextResId, int imageResId) {
Question question = new Question(questionTextResId, imageResId);
this.hardQuestions.add(question);
return question;
}
private void startGame() {
Collections.shuffle(this.easyQuestions);
Collections.shuffle(this.mediumQuesitons);
Collections.shuffle(this.hardQuestions);
this.questionQueue.clear();
this.questionQueue.addAll(this.easyQuestions);
this.questionQueue.addAll(this.mediumQuesitons);
this.questionQueue.addAll(this.hardQuestions);
this.questionIndex = 0;
moveToQuestion(0);
}
private void moveToQuestion(int index) {
if(index < this.questionQueue.size()) {
Question question = this.questionQueue.get(index);
int questionTextResId = question.getQuestionTextResId();
this.textViewFrage.setText(questionTextResId);
int imageResId = question.getImageResId();
this.imageViewBild.setImageResource(imageResId);
List<Answer> answers = question.getAnswers();
int answerCount = answers.size();
int buttonCount = this.answerButtons.length;
for(int i = 0; i < buttonCount; i++) {
Button button = this.answerButtons[i];
if(i < answerCount) {
button.setVisibility(View.VISIBLE);
Answer answer = answers.get(i);
bindAnswerToButton(button, answer);
} else {
button.setVisibility(View.GONE);
}
}
}
}
private void gotoNextQuestion() {
this.questionIndex++;
moveToQuestion(this.questionIndex);
}
private void bindAnswerToButton(Button button, Answer answer) {
int answerTextResId = answer.getAnswerTextResId();
button.setText(answerTextResId);
button.setOnClickListener(new AnswerClickListener(answer));
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.quiz_screen, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
private class AnswerClickListener implements View.OnClickListener {
private final Answer answer;
private AnswerClickListener(Answer answer) {
this.answer = answer;
}
@Override
public void onClick(View v) {
if(this.answer.isCorrect()) {
gotoNextQuestion();
} else {
Toast toast = Toast.makeText(QuizScreen.this, R.string.toast_wrong_answer, Toast.LENGTH_SHORT);
toast.show();
}
}
}
}
如果您有任何其他问题,请随时提出!