Brandon Brandon - 3 months ago 10
Android Question

Using random number generate to display images from an arraylist

Brief Description: The application will display random images to the user based off the words from the word bank file and the user will have to identify the name of the image, (single word). The user can either "pass" by clicking the pass button or identify the image and then the "next" button will pop up directing them to the next image.

Problem:
The application keep crashing right before the new next button appears with different images when I attempt to press the next button.

I think that it has something to do with the random number generator or an issue with the if statements i placed.

I am getting no error message. I am not sure what is causing the issue. When I do run the apk file, when it crash it says "unfortunately, pic has stopped"
"pic" is the app name.

EDIT:
When i ran the app through the emulator I got an runtime error message, shown below

The next button is clicked and changes to the next image which is randomizes by the code:

randomNum = (random.nextInt(UnSpokenList.size()) + 1);


the first element, contains "Word Bank:" which is not an image, it just the name of the list of word, so I did random.nextInt(maxsize)+1 , which I assume avoids 0, the first element, but when it comes down to the arraylist have the size of 2

(it crashes right before this part, but it does crashes when this next button is click as well.)
In the
onActivityResult
function when it size 2 or less then 2, it will stop showing this next button and show a different next button which call the function onlastPair, which produce images based off the last element of the arraylist.

Main Code:
I took some code off functions that aren't related to the issue hopefully it will be easier to find the main problem.

public class Main extends Activity implements AdapterView.OnItemSelectedListener {

private static final int VR_Request = 100;

Button restart;
Button mainMenu;
Button pass;
Button next;
Button last2image;

TextView speechInput;
TextView matchOrNot;
TextView passTitle;
TextView counterDisplay;

String[] wordBank;
ArrayList<String> wordBANK;

Spinner wordList;
Spinner SpokenWords;

ArrayList<String> UnSpokenList;
ArrayList<String> SpokenList;
ArrayAdapter<String> wordList_adapter;
ArrayAdapter<String> SpokenList_adapter;

ImageButton speechBtn;

ImageView image;
Resources res;
int resID;

Random random;
int randomNum;

int previous;
int passCounter;

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

speechInput = (TextView) findViewById(R.id.english_word1);
matchOrNot = (TextView) findViewById(R.id.matchOrNot1);
passTitle = (TextView) findViewById(R.id.passedTitle);
counterDisplay = (TextView) findViewById(R.id.passCounterText);

wordBank = getResources().getStringArray(R.array.Words);

speechBtn = (ImageButton) findViewById(R.id.mic_pic_button1);

wordBANK = new ArrayList<String>(Arrays.asList(wordBank));
image = (ImageView) findViewById(R.id.imageView1);
res = getResources();

restart = (Button) findViewById(R.id.restartButton1);
mainMenu = (Button) findViewById(R.id.mainMenubutton1);
pass = (Button) findViewById(R.id.passButton);
next = (Button) findViewById(R.id.nextButton);
last2image = (Button) findViewById(R.id.last2);

pass.setClickable(true);

wordList = (Spinner) findViewById(R.id.wordsList1);
SpokenWords = (Spinner) findViewById(R.id.spokenWords1);

UnSpokenList = new ArrayList<String>(Arrays.asList(wordBank));
SpokenList = new ArrayList<String>(wordBank.length+1);

UnSpokenList.add(0, "Word Bank:");
SpokenList.add(0,"Spoken Words:");
wordList_adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, UnSpokenList);
wordList_adapter.setDropDownViewResource(android.R.layout.simple_spinner_item);
wordList.setAdapter(wordList_adapter);
SpokenList_adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, SpokenList);
SpokenList_adapter.setDropDownViewResource(android.R.layout.simple_spinner_item);
SpokenWords.setAdapter(SpokenList_adapter);

random = new Random();
randomNum = random.nextInt(UnSpokenList.size()-1);
resID = res.getIdentifier(UnSpokenList.get(randomNum), "drawable", getApplication().getPackageName());
image.setImageResource(resID);

pass.setClickable(true);
pass.setVisibility(View.VISIBLE);
next.setClickable(false);
next.setVisibility(View.INVISIBLE);
speechBtn.setClickable(true);
last2image.setVisibility(View.INVISIBLE);
last2image.setClickable(false);
passCounter = 0;

restart.setVisibility(View.INVISIBLE);
mainMenu.setVisibility(View.INVISIBLE);


passTitle.setVisibility(View.INVISIBLE);
counterDisplay.setVisibility(View.INVISIBLE);
}

@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
parent.getItemAtPosition(position);
}

@Override
public void onNothingSelected(AdapterView<?> parent) {

}



public void onMicButton(View view) {

}

/**
* When the next button is pressed, this function handles the next random image to be displayed
* @param view
*/
public void onNext(View view){
if(view.getId() == R.id.nextButton){
speechInput.setText("");
matchOrNot.setText("");

randomNum = (random.nextInt(UnSpokenList.size()) + 1);

resID = res.getIdentifier(UnSpokenList.get(randomNum), "drawable", getApplication().getPackageName());
image.setImageResource(resID);

next.setClickable(false);
next.setVisibility(View.INVISIBLE);

pass.setClickable(true);
pass.setVisibility(View.VISIBLE);

speechBtn.setClickable(true);
}
}

/**
* This function occurs when the user plays all the way to last 2 image left in game
*/
public void onlastPair(View view){
if(view.getId() == R.id.last2){
if(!UnSpokenList.get(UnSpokenList.size()).contains("Word Bank:")) {
resID = res.getIdentifier(UnSpokenList.get(UnSpokenList.size()), "drawable", getApplication().getPackageName());
image.setImageResource(resID);

speechBtn.setClickable(true);
pass.setClickable(false);
pass.setVisibility(View.INVISIBLE);
}else {
onGameOver();
}
}
}

public void onGameOver(){

}

/**
* when the user clicks the passed button this function is called.
* It takes the previous image and make sure it does not pop-up again for the next image and then reproduce a different image
* @param view
*/
public void onPass(View view){

}

public void onResetPic(View view){

}

public void reset(){

}


public void MainMenu(View view){
}

/**
* shows speech input dialog
*/
public void promptSpeechInput() {
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.ACTION_RECOGNIZE_SPEECH, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault());
intent.putExtra(RecognizerIntent.EXTRA_PROMPT, "Say a Word from our word bank!");

try {
startActivityForResult(intent, VR_Request);
} catch (ActivityNotFoundException a) {
Toast.makeText(Pictionary.this, "Oops, your device doesn't support speech recognition,", Toast.LENGTH_LONG).show();
}
}

/**
* detects and recieve speech input
* @param requestCode
* @param resultCode
* @param intent
*/
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
if(requestCode == VR_Request && resultCode == RESULT_OK) {
ArrayList<String> result = intent.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);

if(wordBANK.contains(result.get(0).toLowerCase()) && UnSpokenList.get(randomNum).contains(result.get(0).toLowerCase())){
speechInput.setText(result.get(0).toUpperCase());
matchOrNot.setTextColor(Color.GREEN);
matchOrNot.setText("CORRECT!");
UnSpokenList.remove(result.get(0).toLowerCase());
SpokenList.add(result.get(0).toLowerCase());
pass.setClickable(false);
pass.setVisibility(View.INVISIBLE);
speechBtn.setClickable(false);

if(UnSpokenList.size() > 3){
next.setClickable(true);
next.setVisibility(View.VISIBLE);
}else{
last2image.setVisibility(View.VISIBLE);
last2image.setClickable(true);
}
}else{
speechInput.setText("");
matchOrNot.setTextColor(Color.RED);
matchOrNot.setText("TRY AGAIN!");
pass.setVisibility(View.VISIBLE);
pass.setClickable(true);
}
}
super.onActivityResult(requestCode, resultCode, intent);
}
}


Any ideas? Thank you in advance!

EDIT: Error Message:
This error message occurs every time I pressed the last2image button, and gives the crash message: "unfortunately, pic has stopped running"

08-17 15:58:31.083 13036-13036/com.example.speechtotext E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.speechtotext, PID: 13036
java.lang.IllegalStateException: Could not execute method for android:onClick
at android.view.View$DeclaredOnClickListener.onClick(View.java:4452)
at android.view.View.performClick(View.java:5198)
at android.view.View$PerformClick.run(View.java:21147)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at android.view.View$DeclaredOnClickListener.onClick(View.java:4447)
at android.view.View.performClick(View.java:5198) 
at android.view.View$PerformClick.run(View.java:21147) 
at android.os.Handler.handleCallback(Handler.java:739) 
at android.os.Handler.dispatchMessage(Handler.java:95) 
at android.os.Looper.loop(Looper.java:148) 
at android.app.ActivityThread.main(ActivityThread.java:5417) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 
Caused by: java.lang.IndexOutOfBoundsException: Invalid index 3, size is 3
at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:255)
at java.util.ArrayList.get(ArrayList.java:308)
at com.example.speechtotext.Main.onlastPair(Main.java:222)
at java.lang.reflect.Method.invoke(Native Method) 
at android.view.View$DeclaredOnClickListener.onClick(View.java:4447) 
at android.view.View.performClick(View.java:5198) 
at android.view.View$PerformClick.run(View.java:21147) 
at android.os.Handler.handleCallback(Handler.java:739) 
at android.os.Handler.dispatchMessage(Handler.java:95) 
at android.os.Looper.loop(Looper.java:148) 
at android.app.ActivityThread.main(ActivityThread.java:5417) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 
08-17 15:58:33.495 13036-13036/com.example.speechtotext I/Process: Sending signal. PID: 13036 SIG: 9
08-17 15:58:33.966 17345-17345/com.example.speechtotext W/System: ClassLoader referenced unknown path: /data/app/com.example.speechtotext-2/lib/x86
08-17 15:58:34.302 17345-17345/com.example.speechtotext W/System: ClassLoader referenced unknown path: /data/app/com.example.speechtotext-2/lib/x86
08-17 15:58:34.673 17345-17345/com.example.speechtotext W/art: Before Android 4.1, method android.graphics.PorterDuffColorFilter android.support.graphics.drawable.VectorDrawableCompat.updateTintFilter(android.graphics.PorterDuffColorFilter, android.content.res.ColorStateList, android.graphics.PorterDuff$Mode) would have incorrectly overridden the package-private method in android.graphics.drawable.Drawable
08-17 15:58:34.748 17345-17386/com.example.speechtotext D/OpenGLRenderer: Use EGL_SWAP_BEHAVIOR_PRESERVED: true

[ 08-17 15:58:34.759 17345:17345 D/ ]
HostConnection::get() New Host Connection established 0xaa23f4c0, tid 17345
08-17 15:58:34.828 17345-17386/com.example.speechtotext I/OpenGLRenderer: Initialized EGL, version 1.4
08-17 15:58:34.885 17345-17386/com.example.speechtotext W/EGL_emulation: eglSurfaceAttrib not implemented
08-17 15:58:34.888 17345-17386/com.example.speechtotext W/OpenGLRenderer: Failed to set EGL_SWAP_BEHAVIOR on surface 0xaa23e540, error=EGL_SUCCESS


The function that runs when the button is click is the onlastPair() function,

public void onlastPair(View view){
if(view.getId() == R.id.last2){
if(!UnSpokenList.get(UnSpokenList.size()).contains("Word Bank:")) {
resID = res.getIdentifier(UnSpokenList.get(UnSpokenList.size()), "drawable", getApplication().getPackageName());
image.setImageResource(resID);

speechBtn.setClickable(true);
pass.setClickable(false);
pass.setVisibility(View.INVISIBLE);
}else {
onGameOver();
}
}
}


originally the error message directed to the two lines below as part an error:

if(!UnSpokenList.get(UnSpokenList.size()).contains("Word Bank:")) {
resID = res.getIdentifier(UnSpokenList.get(UnSpokenList.size()), "drawable", getApplication().getPackageName());


so i decided to change the function shown below,

public void onlastPair(View view){
if(view.getId() == R.id.last2){
if(!UnSpokenList.get(1).isEmpty()) {
int max = UnSpokenList.size();

resID = res.getIdentifier(UnSpokenList.get(max), "drawable", getApplication().getPackageName());
image.setImageResource(resID);

speechBtn.setClickable(true);
pass.setClickable(false);
pass.setVisibility(View.INVISIBLE);
}else {
onGameOver();
}
}
}


but it still give me an error now this time it is pointing to the line:

int max = UnSpokenList.size();


I am not sure what it wrong, maybe it's my logic but it seem to make sense to me or something im missing. Any ideas will be helpful, Thank you!

TWL TWL
Answer

randomNum = (random.nextInt(UnSpokenList.size()) + 1); is incorrect

nextInt(15) gives # 0-14

if your UnSpokenList.size() == 15, that means it has indicies from 0-14

so if you do random.nextInt(UnSpokenList.size()) + 1) you are actually rng'ing for numbers, 15+1, which will be from 0-15

15 will fall outside your indicies of 0-14

to ignore your UnSpokenList(0)...

you need to change it to: random.nextInt(UnSpokenList.size() - 1) + 1

a size 15-1 will rng 0-13, then +1, will make it 1-14 which is perfect for UnSpokenList(1) - UnSpokenList(14)

we can test this for UnSpokenList.size() = 2

random.nextInt(2 - 1) + 1 = only-0 + 1 = only-1

EDIT

if(!UnSpokenList.get(1).isEmpty()) are you trying to check if your list has more than 1 item? if so, yours can throw index-error, it should be if (UnSpokenList.size() > 1)

then once again, int max = UnSpokenList.size() ... UnSpokenList.get(max) you're giving it an index out of bounds, 0-14 but you get(15), it's not going to work