简单的do-while循环导致崩溃

时间:2014-05-12 14:03:54

标签: android while-loop

我正在开发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错误消息一旦崩溃:

http://pastebin.com/fPiNrCEr

  

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;
    }

3 个答案:

答案 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行代码。首先,我创建了两个类QuestionAnswer。它们分别保存与一个问题和一个答案相关的所有数据。 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拥有尽可能多AnswersQuestion,并且每个Answers可以拥有不同数量的Buttons。您只需要确保Button[]中有足够的Question。如果您希望Buttons包含4个以上的答案,只需根据需要Button[]添加Questions,然后按照下面的说明添加Buttons。其余的是完全自动的,您不必担心有太多ButtonsQuestion不需要/使用的所有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种便捷方法,可以轻松地将Questionsprivate 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()

如果您想要添加简约QuestionnewMedium(),如果您想添加媒介Question或{{1},则可以看到newHard()如果你想添加一个硬Question。您可以通过菊花链式调用addAnswer()来向Answers添加QuestionQuestions。这应该都是非常自我解释的。

创建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();
            }
        }
    }
}

如果您有任何其他问题,请随时提出!