0

我正在开发一个 Android 测验应用程序(我是初学者)。所以我有一个抽象类问题和三个问题类型的子类(多个答案、带图像的问题和真/假问题)。我从三个不同的 CSV 文件中读取了我的问题,然后将它们存储在ArrayList<Questions>.

我搜索了如何将 ArrayList 从一个活动传递给另一个活动。我发现我们可以使用 getSerializableExtra 来做到这一点。我试图做到这一点,但后来我发现因为我的 Drawable 而我无法做到这一点。所以我实现了 Parcelable 类来做到这一点。

但是现在,当我开始新的活动时,它因为 TransactionTooLargeException 而转储,我不知道该怎么办。我搜索了答案并阅读了很多 Stack Overflow 帖子,但没有找到任何解决方案。很多人都在使用 Fragment,但我不使用它。是否可以将我的数组列表传递给其他活动?

这是代码

问题.java

public abstract class Questions implements Parcelable {

    public Questions() {
        super();
    }

    protected Questions(Parcel in) {
        super();
    }

    @Override
    public abstract void writeToParcel(Parcel dest, int flags);

    public abstract Themes getTheme();

    public abstract String getQuestion();

    public abstract String getAnswer();

    public abstract boolean isValid(String answer);

    public abstract String[] getAnswers();

    public abstract Drawable getImage();

    public abstract int getType();

    public abstract String toString();
}

多重问题.java

public class MultipleQuestion extends Questions {

    private Themes theme;
    private String question;
    private String[] answers;
    private int idGood;

    public MultipleQuestion(Themes theme, String question, String answer, String wrong_one, String wrong_two, String wrong_three) {
        this.theme = theme;
        this.question = question;
        this.answers = new String[]{answer, wrong_one, wrong_two, wrong_three};

        // The different answers are shuffle directly here and the position of the good answer is saved
        Collections.shuffle(asList(answers));
        for (int i = 0; i < answers.length; i++) {
            if (answers[i].equals(answer)) {
                idGood = i;
            }
        }
    }

    protected MultipleQuestion(Parcel in) {
        super(in);
        theme = Themes.valueOf(in.readString());
        question = in.readString();
        answers = in.createStringArray();
        idGood = in.readInt();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(this.theme.name());
        dest.writeString(question);
        dest.writeStringArray(answers);
        dest.writeInt(idGood);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator<MultipleQuestion> CREATOR = new Creator<MultipleQuestion>() {
        @Override
        public MultipleQuestion createFromParcel(Parcel parcel) {
            return new MultipleQuestion(parcel);
        }

        @Override
        public MultipleQuestion[] newArray(int i) {
            return new MultipleQuestion[i];
        }
    };

    public Themes getTheme() {
        return theme;
    }

    public String getQuestion() {
        return question;
    }

    public String getAnswer() {
        return answers[idGood];
    }

    public boolean isValid(String answer) {
        return answer.equals(answers[idGood]);
    }

    public String[] getAnswers() {
        return answers;
    }

    public Drawable getImage() {
        return null;
    }

    public int getType() {
        return 1;
    }

    public String toString() {
        String res = "";
        res += "Theme : " + theme.toString() + "\n";
        res += "Question : " + question + "\n";
        res += "Answers : " + answers[0] + " ; " + answers[1] + " ; " + answers[2] + " ; " + answers[3] + "\n";
        res += "Good answer : " + answers[idGood] + "\n";
        return res;
    }
}

ImageQuestion.java

public class ImageQuestion extends Questions {

    private Themes theme;
    private String question;
    private Drawable image;
    private String[] answers;
    private int idGood;

    public ImageQuestion(Themes theme, String question, Drawable image, String answer, String wrong_one, String wrong_two, String wrong_three) {
        this.theme = theme;
        this.question = question;
        this.image = image;
        this.answers = new String[]{answer, wrong_one, wrong_two, wrong_three};

        // The different answers are shuffle directly here and the position of the good answer is saved
        Collections.shuffle(asList(answers));
        for (int i = 0; i < answers.length; i++) {
            if (answers[i].equals(answer)) {
                idGood = i;
            }
        }
    }

    protected ImageQuestion(Parcel in) {
        super(in);
        Bitmap bitmap = (Bitmap)in.readParcelable(getClass().getClassLoader());

        theme = Themes.valueOf(in.readString());
        question = in.readString();
        image = new BitmapDrawable(Resources.getSystem(), bitmap);
        answers = in.createStringArray();
        idGood = in.readInt();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        Bitmap bitmap = (Bitmap)((BitmapDrawable) image).getBitmap();

        dest.writeString(this.theme.name());
        dest.writeString(question);
        dest.writeParcelable(bitmap, flags);
        dest.writeStringArray(answers);
        dest.writeInt(idGood);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator<ImageQuestion> CREATOR = new Creator<ImageQuestion>() {
        @Override
        public ImageQuestion createFromParcel(Parcel parcel) {
            return new ImageQuestion(parcel);
        }

        @Override
        public ImageQuestion[] newArray(int i) {
            return new ImageQuestion[i];
        }
    };

    public Themes getTheme() {
        return theme;
    }

    public String getQuestion() {
        return question;
    }

    public Drawable getImage() {
        return image;
    }

    public String getAnswer() {
        return answers[idGood];
    }

    public boolean isValid(String answer) {
        return answer.equals(answers[idGood]);
    }

    public String[] getAnswers() {
        return answers;
    }

    public int getType() {
        return 3;
    }

    public String toString() {
        String res = "";
        res += "Theme : " + theme.toString() + "\n";
        res += "Question : " + question + "\n";
        res += "Answers : " + answers[0] + " ; " + answers[1] + " ; " + answers[2] + " ; " + answers[3] + "\n";
        res += "Good answer : " + answers[idGood] + "\n";
        res += "Image name : " + image.toString() + "\n";
        return res;
    }
}

真假.java

public class TrueFalse extends Questions {

    private Themes theme;
    private String question, answer;

    public TrueFalse(Themes theme, String question, String answer) {
        this.theme = theme;
        this.question = question;
        this.answer = answer;
    }

    protected TrueFalse(Parcel in) {
        super(in);
        theme = Themes.valueOf(in.readString());
        question = in.readString();
        answer = in.readString();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(this.theme.name());
        dest.writeString(question);
        dest.writeString(answer);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator<TrueFalse> CREATOR = new Creator<TrueFalse>() {
        @Override
        public TrueFalse createFromParcel(Parcel parcel) {
            return new TrueFalse(parcel);
        }

        @Override
        public TrueFalse[] newArray(int i) {
            return new TrueFalse[i];
        }
    };

    public Themes getTheme() {
        return theme;
    }

    public String getQuestion() {
        return question;
    }

    public String getAnswer() {
        return answer;
    }

    public boolean isValid(String answer) {
        return this.answer.equals(answer);
    }

    public String[] getAnswers() {
        return null;
    }

    public Drawable getImage() {
        return null;
    }

    public int getType() {
        return 2;
    }

    public String toString() {
        String res = "";
        res += "Theme : " + theme.toString() + "\n";
        res += "Question : " + question + "\n";
        res += "Good answer : " + getAnswer() + "\n";
        return res;
    }
}

MainActivity.java:列表在 play() 方法中传递

public class MainActivity extends AppCompatActivity {

    ArrayList<Questions> questions;

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

    public void readFill(Themes theme) {
        // multiple_questions.csv -> 6 columns : THEME ; question ; answer ; wrong ; wrong ; wrong
        // true_false_questions.csv -> 3 columns : THEME ; question ; answer (0/1)
        // image_question.csv -> 7 columns : THEME ; question ; drawable ; answer ; wrong ; wrong ; wrong

        switch(theme) {
            case ALL: fillAll();
            case TECH: fill(Themes.TECH);
            case CULTURE: fill(Themes.CULTURE);
            case ANIMALS: fill(Themes.ANIMALS);
            case SCIENCE: fill(Themes.SCIENCE);
        }
    }

    public void fill(Themes pTheme) {
        BufferedReader br = null;
        questions = new ArrayList<>();
        try {
            String currentLine;
            br = new BufferedReader(new InputStreamReader(getAssets().open("multiple_questions.csv")));

            while ((currentLine = br.readLine()) != null) {
                String[] row = currentLine.split(";");
                Themes theme = Themes.fromString(row[0]);

                if (theme == pTheme) {
                    Questions question = new MultipleQuestion(theme, row[1], row[2], row[3], row[4], row[5]);
                    questions.add(question);
                }
            }

            br = new BufferedReader(new InputStreamReader(getAssets().open("true_false_questions.csv")));

            while ((currentLine = br.readLine()) != null) {
                String[] row = currentLine.split(";");
                Themes theme = Themes.fromString(row[0]);

                if (theme == pTheme) {
                    Questions question = new TrueFalse(theme, row[1], (row[2].equals("1") ? "True" : "False"));
                    questions.add(question);
                }
            }

            br = new BufferedReader(new InputStreamReader(getAssets().open("image_question.csv")));

            while ((currentLine = br.readLine()) != null) {
                String[] row = currentLine.split(";");
                Themes theme = Themes.fromString(row[0]);

                if (theme == pTheme) {
                    Resources resources = getApplicationContext().getResources();
                    final int resourceId = resources.getIdentifier(row[2].split("\\.")[0], "drawable", getPackageName());
                    Drawable img = ContextCompat.getDrawable(this, resourceId);

                    Questions question = new ImageQuestion(theme, row[1], img, row[3], row[4], row[5], row[6]);
                    questions.add(question);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        Collections.shuffle(questions);
    }

    public void fillAll() {
        BufferedReader br = null;
        questions = new ArrayList<>();
        try {
            String currentLine;
            br = new BufferedReader(new InputStreamReader(getAssets().open("multiple_questions.csv")));

            while ((currentLine = br.readLine()) != null) {
                String[] row = currentLine.split(";");
                Questions question = new MultipleQuestion(Themes.fromString(row[0]), row[1], row[2], row[3], row[4], row[5]);
                questions.add(question);
            }

            br = new BufferedReader(new InputStreamReader(getAssets().open("true_false_questions.csv")));

            while ((currentLine = br.readLine()) != null) {
                String[] row = currentLine.split(";");
                Questions question = new TrueFalse(Themes.fromString(row[0]), row[1], (row[2].equals("1") ? "True" : "False"));
                questions.add(question);
            }

            br = new BufferedReader(new InputStreamReader(getAssets().open("image_question.csv")));

            while ((currentLine = br.readLine()) != null) {
                String[] row = currentLine.split(";");
                Resources resources = getApplicationContext().getResources();
                final int resourceId = resources.getIdentifier(row[2].split("\\.")[0], "drawable", getPackageName());
                Drawable img = ContextCompat.getDrawable(this, resourceId);

                Questions question = new ImageQuestion(Themes.fromString(row[0]), row[1], img, row[3], row[4], row[5], row[6]);
                questions.add(question);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        Collections.shuffle(questions);
    }

    public void play() {
        Intent intent;
        int type = questions.get(0).getType();
        int number = 1;

        if (type == 1) {
            intent = new Intent(MainActivity.this, MultipleQuestionActivity.class);
        } else if (type == 2) {
            intent = new Intent(MainActivity.this, TrueFalseActivity.class);
        } else {
            intent = new Intent(MainActivity.this, ImageQuestionActivity.class);
        }

        intent.putExtra("number", number);
        intent.putParcelableArrayListExtra("questions", questions);

        for (int i = 0; i < questions.size(); i++) {
            Log.d("LIST", "Q" + i + " : " + questions.get(i).toString());
        }

        Log.d("QUEST","size init : " + questions.size());
        startActivity(intent);
    }

    /* I tried to use this but I had the same exception
    @Override
    protected void onSaveInstanceState(Bundle oldInstanceState) {
        super.onSaveInstanceState(oldInstanceState);
        oldInstanceState.clear();
    }
    */

    public void playAll(View view) {
        readFill(Themes.ALL);
        play();
    }

    public void playTech(View view) {
        readFill(Themes.TECH);
        play();
    }

    public void playCulture(View view) {
        readFill(Themes.CULTURE);
        play();
    }

    public void playAnimal(View view) {
        readFill(Themes.ANIMALS);
        play();
    }

    public void playScience(View view) {
        readFill(Themes.SCIENCE);
        play();
    }
}

MultipleQuestionActivity.java都在 onCreate

public class MultipleQuestionActivity extends AppCompatActivity {

    ProgressBar progress;
    MyCountDownTimer countTimer;
    ArrayList<Questions> questions;
    int number;

    @Override
    @SuppressWarnings("ConstantConditions")
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_multiple_question);
        progress = findViewById(R.id.progressbar);

        Intent intent = getIntent();
        number = intent.getExtras().getInt("number");
        Log.d("QUEST", "int : " + number);

        //Bundle b = intent.getExtras();
        questions = intent.getParcelableArrayListExtra("questions");

        for (int i = 0; i < questions.size(); i++) {
            Log.d("LIST", "Q" + i + " : " + questions.get(i).toString());
        }

        Log.d("QUEST", "List size : " + questions.size());

        countTimer = new MyCountDownTimer(15000, 10);
        countTimer.start();
    }

    public class MyCountDownTimer extends CountDownTimer {

        public MyCountDownTimer(long millisInFuture, long countDownInterval) {
            super(millisInFuture, countDownInterval);
        }

        @Override
        public void onTick(long millisUntilFinished) {
            int progressTime = (int) ((millisUntilFinished/100)/1.5);
            progressTime = (int) ((15000/100)/1.5) - progressTime;

            if (progressTime >= 35 && progressTime < 60) {
                progress.setProgressTintList(ColorStateList.valueOf(Color.rgb(255, 224, 53)));
            } else if (progressTime >= 55 && progressTime < 80) {
                progress.setProgressTintList(ColorStateList.valueOf(Color.rgb(249,148,47)));
            } else if (progressTime >= 75) {
                progress.setProgressTintList(ColorStateList.valueOf(Color.rgb(246, 41, 41)));
            }

            progress.setProgress(progress.getMax() - progressTime);
        }

        @Override
        public void onFinish() {
            finish();
        }
    }

}

ImageQuestionActivity.java 和 TrueFalseActivity.java 的代码与 MultipleQuestionActivity.java 相同,所以我不会放在这里。请告诉我是否有其他方法可以传递 ArrayList,或者我是否不必执行此列表。

我的代码中还有一个我没有看到的问题(由于异常,这不是主要问题),无论播放函数调用什么,都只有“科学”主题问题。

这是Logcat:

2019-03-08 11:29:27.623 29975-29975/com.morgane.quizit E/JavaBinder: !!! FAILED BINDER TRANSACTION !!!  (parcel size = 14632360)
2019-03-08 11:29:27.623 29975-29975/com.morgane.quizit D/AndroidRuntime: Shutting down VM
2019-03-08 11:29:27.624 29975-29975/com.morgane.quizit E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.morgane.quizit, PID: 29975
    java.lang.IllegalStateException: Could not execute method for android:onClick
        at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:390)
        at android.view.View.performClick(View.java:6669)
        at android.view.View.performClickInternal(View.java:6638)
        at android.view.View.access$3100(View.java:789)
        at android.view.View$PerformClick.run(View.java:26145)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6863)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
     Caused by: java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Method.invoke(Native Method)
        at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:385)
        at android.view.View.performClick(View.java:6669) 
        at android.view.View.performClickInternal(View.java:6638) 
        at android.view.View.access$3100(View.java:789) 
        at android.view.View$PerformClick.run(View.java:26145) 
        at android.os.Handler.handleCallback(Handler.java:873) 
        at android.os.Handler.dispatchMessage(Handler.java:99) 
        at android.os.Looper.loop(Looper.java:193) 
        at android.app.ActivityThread.main(ActivityThread.java:6863) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 
     Caused by: java.lang.RuntimeException: Failure from system
        at android.app.Instrumentation.execStartActivity(Instrumentation.java:1862)
        at android.app.Activity.startActivityForResult(Activity.java:4599)
        at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:767)
        at android.app.Activity.startActivityForResult(Activity.java:4557)
        at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:754)
        at android.app.Activity.startActivity(Activity.java:4918)
        at android.app.Activity.startActivity(Activity.java:4886)
        at com.morgane.quizit.MainActivity.play(MainActivity.java:151)
        at com.morgane.quizit.MainActivity.playAll(MainActivity.java:164)
        at java.lang.reflect.Method.invoke(Native Method) 
        at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:385) 
        at android.view.View.performClick(View.java:6669) 
        at android.view.View.performClickInternal(View.java:6638) 
        at android.view.View.access$3100(View.java:789) 
        at android.view.View$PerformClick.run(View.java:26145) 
        at android.os.Handler.handleCallback(Handler.java:873) 
        at android.os.Handler.dispatchMessage(Handler.java:99) 
        at android.os.Looper.loop(Looper.java:193) 
        at android.app.ActivityThread.main(ActivityThread.java:6863) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 
     Caused by: android.os.TransactionTooLargeException: data parcel size 14632360 bytes
        at android.os.BinderProxy.transactNative(Native Method)
        at android.os.BinderProxy.transact(Binder.java:1177)
        at android.app.IActivityManager$Stub$Proxy.startActivity(IActivityManager.java:3702)
        at android.app.Instrumentation.execStartActivity(Instrumentation.java:1856)
        at android.app.Activity.startActivityForResult(Activity.java:4599) 
        at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:767) 
        at android.app.Activity.startActivityForResult(Activity.java:4557) 
        at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:754) 
        at android.app.Activity.startActivity(Activity.java:4918) 
        at android.app.Activity.startActivity(Activity.java:4886) 
        at com.morgane.quizit.MainActivity.play(MainActivity.java:151) 
        at com.morgane.quizit.MainActivity.playAll(MainActivity.java:164) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:385) 
        at android.view.View.performClick(View.java:6669) 
        at android.view.View.performClickInternal(View.java:6638) 
        at android.view.View.access$3100(View.java:789) 
        at android.view.View$PerformClick.run(View.java:26145) 
        at android.os.Handler.handleCallback(Handler.java:873) 
        at android.os.Handler.dispatchMessage(Handler.java:99) 
        at android.os.Looper.loop(Looper.java:193) 
        at android.app.ActivityThread.main(ActivityThread.java:6863) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 

更新

如评论中所述,我尝试使用 DataFragment 类,但现在我得到了 NullPointerException。

数据片段.java

public class DataFragment extends Fragment {

    // data object we want to retain
    private ArrayList<Questions> data;

    // this method is only called once for this fragment
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // retain this fragment
        setRetainInstance(true);
    }

    public void setData(ArrayList<Questions> data) {
        this.data = data;
    }

    public ArrayList<Questions> getData() {
        return data;
    }
}

MainActivity.java (play() 方法)

public void play() {
        Intent intent;
        int type = questions.get(0).getType();
        int number = 1;

        if (type == 1) {
            intent = new Intent(MainActivity.this, MultipleQuestionActivity.class);
        } else if (type == 2) {
            intent = new Intent(MainActivity.this, TrueFalseActivity.class);
        } else {
            intent = new Intent(MainActivity.this, ImageQuestionActivity.class);
        }

        intent.putExtra("number", number);
        //intent.putParcelableArrayListExtra("questions", questions);
        FragmentManager fm = getSupportFragmentManager();
        data = (DataFragment) fm.findFragmentByTag("data");

        if (data == null) {
            data = new DataFragment();
            fm.beginTransaction().add(data, "data").commit();
            data.setData(questions);
        }

        for (int i = 0; i < questions.size(); i++) {
            Log.d("LIST", "Q" + i + " : " + questions.get(i).toString());
        }

        Log.d("QUEST","size init : " + questions.size());
        startActivity(intent);
    }

其他活动(onCreate)

@Override
    @SuppressWarnings("ConstantConditions")
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_multiple_question);
        progress = findViewById(R.id.progressbar);

        Intent intent = getIntent();
        number = intent.getExtras().getInt("number");
        Log.d("QUEST", "int : " + number);

        //Bundle b = intent.getExtras();
        DataFragment data = (DataFragment) getSupportFragmentManager().findFragmentByTag("data");
        questions = data.getData();
        //questions = intent.getParcelableArrayListExtra("questions");

        for (int i = 0; i < questions.size(); i++) {
            Log.d("LIST", "Q" + i + " : " + questions.get(i).toString());
        }

        Log.d("QUEST", "List size : " + questions.size());

        countTimer = new MyCountDownTimer(15000, 10);
        countTimer.start();
    }
4

1 回答 1

0

我找到了一种通过新类扩展应用程序传递 ArrayList 的方法:

public class App extends Application {

    public ArrayList<Questions> list;

}

我将它添加到 androidmanifest.xml 中:

<application
   android:name=".App"

这是我使用它的方式:

App app = (App) getApplicationContext();
app.list = questions;

获取列表:

App app = (App) getApplicationContext();
questions = app.list;

所以我删除了 Parcelable 的实现,它可以工作。

于 2019-03-08T14:43:59.313 回答