我正在开发一个 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();
}