简体   繁体   English

如何在自定义ArrayAdapter中引用每个视图?

[英]How do I reference each view in a custom ArrayAdapter?

I'm very new to programming as well as Android deveopment so please pardon me if my basics aren't well versed yet. 我对编程以及Android开发都很陌生,所以请原谅我,如果我的基础知识还不够精通的话。 I have provided my code at the bottom. 我在底部提供了我的代码。

As mentioned in the title, how can I reference each view among the views created by my custom arrayadapter? 如标题中所述,如何在我的自定义arrayadapter创建的视图中引用每个视图? So that I could access variables of each view object in the arrayadapter. 这样我就可以访问arrayadapter中每个视图对象的变量。

Actually, I have 2 questions to what I need to achieve. 实际上,我有两个问题需要实现。 So right now I am trying to create a simple app which consist of a science test. 所以现在我正在尝试创建一个由科学测试组成的简单应用程序。 I have managed to display all the questions(TextViews) paired with a CheckBox view into a ListView layout. 我已设法将所有与CheckBox视图配对的问题(TextViews)显示到ListView布局中。 My idea is that when a user checks the CheckBox, it will store a 'true' Boolean value somewhere (probably in an ArrayList). 我的想法是,当用户检查CheckBox时,它将在某处(可能在ArrayList中)存储“true”布尔值。 Afterwards at the score summary activity, I will compare the user's answers with the original Boolean values (the question's correct answer) to check if the right answer was given for each question and return the score. 然后在得分摘要活动中,我将用户的答案与原始布尔值(问题的正确答案)进行比较,以检查是否为每个问题给出了正确答案并返回分数。 Is it advisable to do this? 这样做是否明智?

TestActivity.java TestActivity.java

package com.example.android.scienceknowledgetest;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.ListView;

import java.util.ArrayList;

public class TestActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);

        // ArrayList of all the questions and their boolean (answer) values
        ArrayList<Question> questions = new ArrayList<Question>();
        questions.add(new Question("Plants derive most of their dry mass from the air", true, ""));
        questions.add(new Question("Aluminium is the most common metal in the Earth's crust", true, ""));
        questions.add(new Question("Vitamin C has be shown to prevent colds", false, ""));
        questions.add(new Question("We lose most of our heat through our heads", false, ""));
        questions.add(new Question("Dogs are unable to digest chocolate", false, ""));
        questions.add(new Question("Apple pips contain cyanide", true, ""));
        questions.add(new Question("Cholesterol is a naturally-occurring toxic substance", false, ""));
        questions.add(new Question("When you're on a diet, you lose weight by oxidising fat to a gas and exhaling it", true, ""));
        questions.add(new Question("Human beings are unable to sense when the oxygen level of the air is low", true, ""));
        questions.add(new Question("Most of the Y chromosome is passed unchanged from father to son", true, ""));

        // Constructing a new TestAdapter instance
        TestAdapter adapter = new TestAdapter(this, questions);

        // Find the ListView to display the questions and check boxes
        ListView questions_checkbox_list = (ListView) findViewById(R.id.question_checkbox_list);
        // Display the questions and check boxes to the ListView questions_checkbox_list
        questions_checkbox_list.setAdapter(adapter);

        // Configuring the Button Submit
        Button submitAnswers = (Button) findViewById(R.id.submit_answers);
        submitAnswers.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent endTest = new Intent(TestActivity.this, TestSummary.class);
                startActivity(endTest);
            }
        });
    }
}

TestAdapter TestAdapter

package com.example.android.scienceknowledgetest;

import android.app.Activity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.TextView;
import java.util.ArrayList;

public class TestAdapter extends ArrayAdapter<Question> {

    // TestAdapter constructor
    public TestAdapter(Activity context, ArrayList<Question> questions) {
        super(context, 0, questions);
    }

    private View listItemsView;

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // Check if the current view is recycled, otherwise inflate new view
        listItemsView = convertView;
        if (listItemsView == null) {
            listItemsView = LayoutInflater.from(getContext()).inflate(R.layout.list_item, parent, false);
        }

        // Find the current Question object and store in the variable currentQuestion
        Question currentQuestion = getItem(position);

        // Find the TextView texts of the Question and set the question
        TextView questionText = (TextView) listItemsView.findViewById(R.id.question_text);
        questionText.setText(currentQuestion.getmQuestion());

        // Find the CheckBox of the Question and set the boolean value
        CheckBox answerBool = (CheckBox) listItemsView.findViewById(R.id.answer_boolean);

        // Return the item as a view
        return listItemsView;
    }
}

Question.java Question.java

package com.example.android.scienceknowledgetest;

public class Question {

    // Declaring the question variable
    private String mQuestion;

    // Declaring the answer variable
    private boolean mAnswer;

    // Declaring the attempt variable
    private String mAttempt;

    // Getter method to request for the question
    public String getmQuestion() {
        return  mQuestion;
    }

    // Getter method to request for the boolean value of the answer
    public boolean getmAnswer() {
        return mAnswer;
    }

    // Question Constructor
    public Question(String question, boolean answer, String attempt) {
        mQuestion = question;
        mAnswer = answer;
        mAttempt = attempt;
    }
}

activity_test.xml activity_test.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    android:orientation="vertical">

    <ListView
        android:layout_weight="1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:id="@+id/question_checkbox_list" />

    <Button
        android:layout_weight="0"
        android:id="@+id/submit_answers"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/submit_answers"
        android:textSize="16sp"
        android:layout_margin="16dp"
        android:layout_marginLeft="48dp"/>

</LinearLayout>

list_item.xml list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp">

    <CheckBox
        android:id="@+id/answer_boolean"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/question_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="8dp"
        tools:text="When you're on a diet, you lose weight by oxidising fat to a gas and exhaling it"/>

</LinearLayout>

Here's something along the lines of what you want. 这就是你想要的东西。 It's all within the Adapter. 它都在适配器内。 Here's the code for TestAdpater :- 这是TestAdpater的代码: -

public class TestAdapter extends ArrayAdapter<Question> {

    Context passedcontext;

    // TestAdapter constructor
    public TestAdapter(Activity context, ArrayList<Question> questions) {
        super(context, 0, questions);
        passedcontext = context;
    }

    private View listItemsView;

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // Check if the current view is recycled, otherwise inflate new view
        listItemsView = convertView;
        if (listItemsView == null) {
            listItemsView = LayoutInflater.from(getContext()).inflate(R.layout.list_item, parent, false);
        }

        // Find the current Question object and store in the variable currentQuestion
        Question currentQuestion = getItem(position);

        // Find the TextView texts of the Question and set the question
        TextView questionText = (TextView) listItemsView.findViewById(R.id.question_text);
        questionText.setText(currentQuestion.getmQuestion());

        // Find the CheckBox of the Question and set the boolean value
        CheckBox answerBool = (CheckBox) listItemsView.findViewById(R.id.answer_boolean);
        answerBool.setTag(position);
        answerBool.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                detectCheckBoxChanged(buttonView, isChecked);

            }
        });

        // Return the item as a view
        return listItemsView;
    }

    public void detectCheckBoxChanged(CompoundButton buttonview, boolean ischecked) {
        Toast.makeText(passedcontext,
                "CheckBox " +
                        buttonview.getTag().toString() +
                        " Changed to " + Boolean.toString(ischecked),
                Toast.LENGTH_SHORT).show();
    }
}

The first change is the addition of the line :- 第一个变化是增加了这条线: -

Context passedcontext;

This will hold the context of the invoking activity (next new line of code). 这将保存调用活动的上下文(下一个新的代码行)。

The second change is the addition of a line to set passedcontext with the context from the invoking activity, this done in the TestAdapter's constructor :- 第二个更改是添加一行来设置带有调用活动的上下文的passcontext,这在TestAdapter的构造函数中完成: -

passedcontext = context;

The next change is to set the tag of the CheckBoxes with the position so that the you can determine which CheckBox has fired the Listener. 下一个更改是使用位置设置CheckBoxes的标记,以便您可以确定哪个CheckBox已触发监听器。 This appears immediately (or soon after) after you have determined the id of the CheckBox within the getView method, which conveniently has the position passed to it.:- getView方法中确定了CheckBox的id后,会立即(或很快)出现这种情况,该方法可以方便地将位置传递给它。:-

answerBool.setTag(position);

To do things in order and perhaps simplify the code a new class method is added to the class to handle the CheckBox Changed event (in this case issue a Toast detailing the which CheckBox was clicked and it's state). 为了按顺序执行操作并且可能简化代码,将在类中添加一个新的类方法来处理CheckBox Changed事件(在这种情况下发出一个Toast,详细说明单击了哪个CheckBox及其状态)。 Note it is to use this Toast why we got the invoking activity's context, as Toast needs a context in which to display :- 注意它是使用这个Toast为什么我们得到了调用活动的上下文,因为Toast需要一个上下文来显示: -

    public void detectCheckBoxChanged(CompoundButton buttonview, boolean ischecked) {
        Toast.makeText(passedcontext,
                "CheckBox " +
                        buttonview.getTag().toString() +
                        " Changed to " + Boolean.toString(ischecked),
                Toast.LENGTH_SHORT).show();
    }

The last piece of code sets and includes the listener for the CheckBoxs being changed. 最后一段代码设置并包含要更改的CheckBox的侦听器。 You could incorporate the code above within the listener rather than invoke the new detectCheckBoxChanged method. 您可以将上面的代码合并到侦听器中,而不是调用新的detectCheckBoxChanged方法。 This code is added in the getView method and needs to be (at least the set) after you have ascertained the id of the CheckBox(s) :- 此代码添加在getView方法中,并且在确定CheckBox的ID后需要(至少是设置): -

        answerBool.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                detectCheckBoxChanged(buttonView, isChecked);

            }
        });

You could change the detectCheckBoxChanged method to include something along the lines of :- 您可以更改detectCheckBoxChanged方法以包含以下内容:

    int position = Integer.parseInt(buttonview.getTag().toString());
    TestActivity.handleCheckBoxChanged(position, isChecked);

In which case the handleCheckBoxChanged method in the TestActivity activity (the invoking activity) would be invoked eg 在这种情况下,将调用TestActivity活动(调用活动)中的handleCheckBoxChanged方法,例如

    public static void handleCheckBoxChanged(int position, boolean ischecked) {
        // handle the change here using the position
        Log.d("CHKBOXHIT",
                "Checkbox " +
                        Integer.toString(position) +
                        " was hit it is now "
                        + Boolean.toString(ischecked)
        );
    }

The following changes could be the basis of getting around the "not answered" issue I commented on. 以下更改可能是解决我评论过的“未回答”问题的基础。

1) Add an int array as a class variable to the TestActivity (ie immediately after 'public class TestAdapter extends ArrayAdapter {')eg 1)将一个int数组作为类变量添加到TestActivity中(即在'public class TestAdapter extends ArrayAdapter {')之后立即添加,例如

    static int[] answers;

2) Setup the answers array and initialise the elements to the value that inidcates no answer, this code following the setup of the questions ArrayList. 2)设置answers数组并将元素初始化为无法回答的值,此代码遵循问题ArrayList的设置。 eg 例如

    answers = new int[questions.size()];
    for (int i = 0; i < answers.length; i++ ) {
        answers[i] = -1;
    }

3) Add a new method that will be invoked when an answer is clicked that is passed the index and the state. 3)添加一个新方法,当单击一个传递索引和状态的答案时将调用该方法。

public static void setAnswer(int index, boolean answer) {
    answers[index] = 0;
    if (answer) {
        answers[index] = 1;
    }
}

4) Add the following line to the detectCheckBoxChanged method in the TestAdapter class :- 4)将以下行添加到TestAdapter类中的detectCheckBoxChanged方法: -

    TestActivity.setAnswer(position, ischecked);

5) To test it change the submitAnswers onClick listener in TestActivity to :- 5)要测试它,请将TestActivity中的submitAnswers onClick侦听器更改为: -

    submitAnswers.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            //Intent endTest = new Intent(ScienceKnowledgeTest.this, ScienceKnowledgeTest.class);
            //startActivity(endTest);
            for (int i =0; i < answers.length; i++) {
                switch (answers[i]) {
                    case -1:
                        Log.d("RESULTS", "Question "
                                + Integer.toString(i + 1) +
                                " was not answered."
                        );
                        break;
                    case 0:
                        Log.d("RESULTS", "Question " +
                                Integer.toString(i + 1) +
                                " was answered as False"
                        );
                        break;
                    case 1:
                        Log.d("RESULTS", "Question " +
                                Integer.toString(i + 1) +
                                " was answered as True"
                        );
                        break;
                    default:
                        Log.d("RESULTS", "Well that's unusual something appears to be wrong!!!");
                        break;
                }

            }
        }
    });

However, this has the issue that to select a false answer requires 2 clicks to turn the check box to true and then to false. 但是,这有一个问题,即选择错误答案需要2次单击才能将复选框设置为true,然后再设置为false。 Two check boxes one for true and one for false would be clearer. 两个复选框一个用于true,一个用于false将更清晰。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM