I am writing an Android app that helps little kids learn maths. Users can select some question options and answer the questions. If he/she answers all of them correctly, he/she can get a prize. And there is a different prize for every question option. And there are 22 different question options. I found 22 images online and put them in my drawable folder. Then I wrote a class full of maps, called QuestionOptionMaps
. Here it is, hope you know what I want to do here:
package com.smartkidslovemaths.util;
import com.smartkidslovemaths.QuestionOptions;
import com.smartkidslovemaths.R;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
public class QuestionOptionMaps {
private QuestionOptionMaps () {}
public static ArrayList<QuestionOptions> getOptionsList() {
return optionsList;
}
public static HashMap<QuestionOptions, Integer> getOptionsDrawableMap() {
return optionsDrawableMap;
}
public static HashMap<QuestionOptions, String> getOptionsKeysMap() {
return optionsKeysMap;
}
public static HashMap<QuestionOptions, Integer> getOptionsTimerMap() {
return optionsTimerMap;
}
private static ArrayList<QuestionOptions> optionsList;
private static HashMap<QuestionOptions, Integer> optionsDrawableMap;
private static HashMap<QuestionOptions, String> optionsKeysMap;
private static HashMap<QuestionOptions, Integer> optionsTimerMap;
static {
optionsList = new ArrayList<> ();
optionsList.add (new QuestionOptions (QuestionOptions.OperationType.ADDITION, 1, false));
optionsList.add (new QuestionOptions (QuestionOptions.OperationType.ADDITION, 2, false));
optionsList.add (new QuestionOptions (QuestionOptions.OperationType.ADDITION, 3, false));
optionsList.add (new QuestionOptions (QuestionOptions.OperationType.ADDITION, 1, true));
optionsList.add (new QuestionOptions (QuestionOptions.OperationType.ADDITION, 2, true));
optionsList.add (new QuestionOptions (QuestionOptions.OperationType.ADDITION, 3, true));
optionsList.add (new QuestionOptions (QuestionOptions.OperationType.SUBTRACTION, 1, false));
optionsList.add (new QuestionOptions (QuestionOptions.OperationType.SUBTRACTION, 2, false));
optionsList.add (new QuestionOptions (QuestionOptions.OperationType.SUBTRACTION, 3, false));
optionsList.add (new QuestionOptions (QuestionOptions.OperationType.SUBTRACTION, 1, true));
optionsList.add (new QuestionOptions (QuestionOptions.OperationType.SUBTRACTION, 2, true));
optionsList.add (new QuestionOptions (QuestionOptions.OperationType.SUBTRACTION, 3, true));
optionsList.add (new QuestionOptions (QuestionOptions.OperationType.ADD_AND_SUB, 1, false));
optionsList.add (new QuestionOptions (QuestionOptions.OperationType.ADD_AND_SUB, 2, false));
optionsList.add (new QuestionOptions (QuestionOptions.OperationType.ADD_AND_SUB, 3, false));
optionsList.add (new QuestionOptions (QuestionOptions.OperationType.ADD_AND_SUB, 1, true));
optionsList.add (new QuestionOptions (QuestionOptions.OperationType.ADD_AND_SUB, 2, true));
optionsList.add (new QuestionOptions (QuestionOptions.OperationType.ADD_AND_SUB, 3, true));
optionsList.add (new QuestionOptions (QuestionOptions.OperationType.MULTIPLICATION, 1, false));
optionsList.add (new QuestionOptions (QuestionOptions.OperationType.MULTIPLICATION, 2, false));
optionsList.add (new QuestionOptions (QuestionOptions.OperationType.MULTIPLICATION, 1, true));
optionsList.add (new QuestionOptions (QuestionOptions.OperationType.MULTIPLICATION, 2, true));
ArrayList<String> prefKeyArray = new ArrayList<> ();
prefKeyArray.add ("p110");
prefKeyArray.add ("p120");
prefKeyArray.add ("p130");
prefKeyArray.add ("p111");
prefKeyArray.add ("p121");
prefKeyArray.add ("p131");
prefKeyArray.add ("p210");
prefKeyArray.add ("p220");
prefKeyArray.add ("p230");
prefKeyArray.add ("p211");
prefKeyArray.add ("p221");
prefKeyArray.add ("p231");
prefKeyArray.add ("p310");
prefKeyArray.add ("p320");
prefKeyArray.add ("p330");
prefKeyArray.add ("p311");
prefKeyArray.add ("p321");
prefKeyArray.add ("p331");
prefKeyArray.add ("p410");
prefKeyArray.add ("p420");
prefKeyArray.add ("p411");
prefKeyArray.add ("p421");
optionsKeysMap = getHashMapFromCollections (optionsList, prefKeyArray);
ArrayList<Integer> idArray = new ArrayList<> ();
idArray.add (R.drawable.p110);
idArray.add (R.drawable.p120);
idArray.add (R.drawable.p130);
idArray.add (R.drawable.p111);
idArray.add (R.drawable.p121);
idArray.add (R.drawable.p131);
idArray.add (R.drawable.p210);
idArray.add (R.drawable.p220);
idArray.add (R.drawable.p230);
idArray.add (R.drawable.p211);
idArray.add (R.drawable.p221);
idArray.add (R.drawable.p231);
idArray.add (R.drawable.p310);
idArray.add (R.drawable.p320);
idArray.add (R.drawable.p330);
idArray.add (R.drawable.p311);
idArray.add (R.drawable.p321);
idArray.add (R.drawable.p331);
idArray.add (R.drawable.p410);
idArray.add (R.drawable.p420);
idArray.add (R.drawable.p411);
idArray.add (R.drawable.p421);
optionsDrawableMap = getHashMapFromCollections (optionsList, idArray);
//TODO initialize the collections
}
private static <K, V> HashMap<K, V> getHashMapFromCollections (Collection<K> keys, Collection<V> values) {
if (keys.size () != values.size ())
throw new AssertionError ();
HashMap<K, V> map = new HashMap<> ();
K[] keyArray = (K[])keys.toArray ();
V[] valueArray = (V[])values.toArray ();
for (int i = 0 ; i < keys.size () ; i++) {
map.put (keyArray[i], valueArray[i]);
}
return map;
}
}
Most of it is just initializing the maps. I didn't initialize the optionsTimer
map because that is a todo. Now I created a PrizeActivity
which displays all the prizes that the user got. It basically shows all the prizes and the amount that you have. Because there are a lot of prizes, I decided to dynamically add views to a ScrollView
. Here is the layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="com.smartkidslovemaths.PrizeActivity">
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="@+id/trophy_content"
android:orientation="horizontal">
</LinearLayout>
</HorizontalScrollView>
</RelativeLayout>
And here is how I add the views:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate (savedInstanceState);
setContentView (R.layout.activity_prize);
for (QuestionOptions option : QuestionOptionMaps.getOptionsList ()) {
displayAPrize (option);
}
}
private void displayAPrize (QuestionOptions options) {
Resources res = getResources ();
int parentMargin = (int)res.getDimension (R.dimen.prize_display_margin);
LinearLayout.LayoutParams parentParams =
new LinearLayout.LayoutParams (ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT);
parentParams.setMargins (parentMargin, parentMargin, parentMargin, parentMargin);
LinearLayout.LayoutParams imageParams =
new LinearLayout.LayoutParams (
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT
);
LinearLayout parent = new LinearLayout (this);
parent.setLayoutParams (parentParams);
parent.setOrientation (LinearLayout.VERTICAL);
ImageView image = new ImageView (this);
image.setLayoutParams (imageParams);
int imageId = QuestionOptionMaps.getOptionsDrawableMap ().get (options);
image.setImageResource (imageId);
parent.addView (image);
TextView text = new TextView (this);
text.setLayoutParams (imageParams);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences (this);
String key = QuestionOptionMaps.getOptionsKeysMap ().get (options);
int prizeCount = prefs.getInt (key, 0);
text.setText ("x" + prizeCount);
parent.addView (text);
((LinearLayout)findViewById (R.id.trophy_content)).addView (parent);
}
And when I run the app, it crashed with an OutOfMemoryError
! Here is the call stack:
java.lang.OutOfMemoryError
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:503)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:356)
at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:816)
at android.content.res.Resources.loadDrawable(Resources.java:2117)
at android.content.res.Resources.getDrawable(Resources.java:702)
at android.widget.ImageView.resolveUri(ImageView.java:636)
at android.widget.ImageView.setImageResource(ImageView.java:365)
at com.smartkidslovemaths.PrizeActivity.displayAPrize(PrizeActivity.java:46)
at com.smartkidslovemaths.PrizeActivity.onCreate(PrizeActivity.java:22)
at android.app.Activity.performCreate(Activity.java:5133)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2230)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2316)
at android.app.ActivityThread.access$600(ActivityThread.java:150)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1298)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:213)
at android.app.ActivityThread.main(ActivityThread.java:5225)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:741)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:557)
at dalvik.system.NativeStart.main(Native Method)
Line 46 is this line:
image.setImageResource (imageId);
I really don't understand why this happens. Other apps have lots more images, why they don't crash? is there any way to fix this?