Search code examples
javaandroidarraylistparcelable

Get elements of ArrayList from retrieved ArrayList via intent via Parcelable in 2nd activity


I'm trying to build a simple android that should simulate learning vocabulary with cards. to get to the final app, I'm going to add functionality and complexity step by step, by learning new things, and adding new things.

I'm about to tell you where I'm stuck, but first, here's what my App should do, so far (I'm still far, far way from where I'd like it to go, but you don't have to mind about that.):

At this point the app should do the folowing thing:

  • 1) in the MainActivity:

  • a) Create an Array of 3 instances of an implementation of the Parcelable interface (class VocCard implements Parcelable), VocCard[] voc1, in this case. Since the class VocCard implements Parcelable, a Parcel is obtained for the construction of the 3 instances.

  • b) Create an ArrayList of the type VocCard called vocCardList and add all 3 elements of voc1 to vocCardList.
  • c) Create an instance of a start button which creates an intent for starting a 2nd activity called PracticeActivity when clicked.
  • d) Add the ArrayList vocCardList with Parcelable to the intent.

2) in PracticeActivity

  • a) Get the intent created by MainActivity.
  • b) Retrieve ArrayList vocCardList from intent
  • c) Get any element of vocCardsList and assign a variable of the type VocCard to it.
  • d) Retrieve a value of the assigned Voccard instance by invoking its methods.
  • e) Display that value by setting a TextView to the value's String value.
  • f) Create a Button nextButton which creates an intent for starting the 2nd activity PracticeActivity again, as some kind of recursion.
  • g) Add the ArrayList vocCardList with parcelable to intent.
  • h) repeat 2) a)-g) until App is closed by closing-icon.

I'm currently stuck at 2) c), insofar that the App only works as described above for the index 0. Only VocCard card0 = vocCardList1.get(0); works, vocCardList1.get(1), or vocCardList1.get(2); don't, despite 1 and 2 being within the ArrayList boundries.

Oddly enough, the Runtime Exeption Message for using index 1 and index 2 is not the same:

with vocCardList1.get(1): java.lang.ClassCastException: java.lang.String cannot be cast to com.undiclosed.smartcards.VocCard

with vocCardList1.get(2): java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String com.undisclosed.smartcards.VocCard.returnVocForeign()' on a null object reference

Question:

Why can't I acces the elements of the ArrayList the way I expected? When I searched the web I was probably looking for the wrong stuff.

MainActivity.java:

package com.undisclosed123.smartcards;

import android.content.Intent;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private List<VocCard> vocCardList;
    private String[] voc_f = {"bread","apple","water"};
    private String[] voc_n = {"pain","pomme","eau"};

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

        //Create VocCards and add to List
        vocCardList = new ArrayList<VocCard>();
        VocCard[] voc1 = new VocCard[3];
        Parcel in = Parcel.obtain();

        for(int i = 0; i < 3; i++){

            voc1[i] = new VocCard(in);
            voc1[i].setVocForeign(voc_f[i]);
            voc1[i].setVocNative(voc_n[i]);
            vocCardList.add(voc1[i]);
        }
        // Create Intent and assign the parcelable List for sending to second activity on btn click
        Button startBtn = (Button) findViewById(R.id.button);
        startBtn.setOnClickListener(new View.OnClickListener() {
             @Override
             @SuppressWarnings("unchecked")
             public void onClick(View v) {
                 Intent intent = new Intent(MainActivity.this, PracticeActivity.class);
                 intent.putParcelableArrayListExtra("voc1",(ArrayList)vocCardList);
                 getApplicationContext().startActivity(intent);
             }
        });
    }
}

And below, PracticeActivity.java:

(Sorry for the large sections which are commented out, I figured it could help communicating my further intentions for that class)

package com.undisclosed123.smartcards;

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

import java.util.ArrayList;

public class PracticeActivity extends AppCompatActivity {

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

        // Get the Intent that started this activity and extract the string
        Intent intent = getIntent();
        final ArrayList<VocCard> vocCardList1 = intent.getParcelableArrayListExtra("voc1");     // 

        //Get the Data from the VocCards
        //VocCard card4count = vocCardList1.get(2);
       // card4count.increaseCount(); 
        //int count = card4count.getCount();
       /* if(count >= vocCardList1.size()){
            // TODO
             //Create new intent for EndPracticeActivity

            //makeshift statement
            count--;

        }*/
        VocCard card0 = vocCardList1.get(2); 
       // VocCard card1 = vocCardList1.get(1);
        String test1 = card0.returnVocForeign();
       // card0.increaseCount();
     //   String test1 = "test1";

        //Make a TextView display the transfered String
        TextView textView = findViewById(R.id.textView);
        textView.setText(test1);

        //Create another intent that recalls same activity recursively
        Button nextBtn = (Button) findViewById(R.id.button2);
        nextBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            @SuppressWarnings("unchecked")
            public void onClick(View v) {
                Intent intent = new Intent(PracticeActivity.this, PracticeActivity.class);
                intent.putParcelableArrayListExtra("voc1",(ArrayList)vocCardList1);
                getApplicationContext().startActivity(intent);
            }
        }); /**/

    }
}

And at last, VocCard.java:

package com.undisclosed123.smartcards;

import android.os.Parcel;
import android.os.Parcelable;

public class VocCard implements Parcelable {
    private String voc_foreign;
    private String voc_native;
    private boolean learned;
    private int error_level;
    private static int counter;

    public String returnVocForeign(){
        return voc_foreign;
    }

    public void setVocForeign(String voc_f){
        voc_foreign = voc_f;
    }

    public String returnVocNative(){
        return voc_native;
    }

    public void setVocNative(String voc_n){
        voc_native = voc_n;
    }

    public boolean checkLearned(){
        return learned;
    }

    public int getErrorLevel(){
        return error_level;
    }

    public void makeLearned(){
        learned = true;
    }

    public void increaseErrorLevel(){
        error_level++;
    }

    public int getCount(){
        return counter;
    }

    public void increaseCount(){
        counter++;
    }

    public VocCard(Parcel in) {
        voc_foreign = in.readString();
        voc_native = in.readString();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(voc_foreign);
        dest.writeString(voc_native);
        dest.writeInt((Boolean) learned ? 1 : 0);
        dest.writeInt(error_level);
    }

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

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

        @Override
        public VocCard[] newArray(int size) {
            return new VocCard[size];
        }
    };
}

Solution

  • The problem is with writing data to Parcel and reading data from it.

    public class VocCard implements Parcelable {
    
        ...
        ...
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(voc_foreign);
            dest.writeString(voc_native);
            dest.writeInt(learned ? 1 : 0);
            dest.writeInt(error_level);
        }
    
        /**
        * This constructor is invoked by the method 
        * createFromParcel(Parcel source) of the object CREATOR.
        * 
        * The order and number of writing and reading data to and from   
        * Parcel should be same 
        **/
        private VocCard(Parcel in) {
            voc_foreign = in.readString();
            voc_native = in.readString();
            learned = in.readInt() == 1;
            error_level = in.readInt();
        }
    
        /**
        * A constructor that initializes the VocCard object. 
        **/
        VocCard(String voc_foreign, String voc_native) {
            this.voc_foreign = voc_foreign;
            this.voc_native = voc_native;
        }
    
        ...
        ...
    
    }
    

    Few changes in MainActivity inside onCreate

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    
        ...
        ...
    
        //Create VocCards and add to List
        mVocCardList = new ArrayList<>(3);
    
        for (int i = 0; i < 3; i++) {
            mVocCardList.add(new VocCard(voc_f[i], voc_n[i]));
        }
    
        Button startBtn = (Button) findViewById(R.id.button);
        startBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this, PracticeActivity.class);
                intent.putParcelableArrayListExtra("voc1", (ArrayList<? extends Parcelable>) mVocCardList);
                startActivity(intent);
            }
        });
    }
    

    Now get the VocCard list in PracticeActivity

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    
        ...
        ...
    
        final ArrayList<VocCard> vocCardList = getIntent().getParcelableArrayListExtra("voc1");
    
        final VocCard card = vocCardList.get(2);
        String test = card.getVocForeign();
    
        ... 
        ...
    
    }