javaandroidnullpointerexceptionandroid-cards

Android CardsLib - NullPointerException OnLongCardClickListener in MultiChoiceMode


I'm using the Android CardsLib to generate a list of cards in my fragment. I've the list properly done, no problem here.

Now I'm following the tutorial (link to the tutorial) to add MultiChoiceMode to my list, this is selecting multiple cards using the long tap/click to select. But I'm getting a NullPointerException.

Here's my stacktrace:

FATAL EXCEPTION: main
Process: com.mypackage.android.design.appdesgin, PID: 18937
java.lang.NullPointerException
    at it.gmariotti.cardslib.library.internal.multichoice.MultiChoiceAdapterHelperBase.onItemSelectedStateChanged(MultiChoiceAdapterHelperBase.java:293)
    at it.gmariotti.cardslib.library.internal.multichoice.MultiChoiceAdapterHelperBase.onCreateActionMode(MultiChoiceAdapterHelperBase.java:255)
    at it.gmariotti.cardslib.library.internal.CardArrayMultiChoiceAdapter.onCreateActionMode(CardArrayMultiChoiceAdapter.java:131)
    at com.mypackage.android.design.appdesgin.cards.MyCardArrayMultiChoiceAdapter.onCreateActionMode(MyCardArrayMultiChoiceAdapter.java:31)
    at com.android.internal.policy.impl.PhoneWindow$DecorView$ActionModeCallbackWrapper.onCreateActionMode(PhoneWindow.java:3011)
    at com.android.internal.app.ActionBarImpl$ActionModeImpl.dispatchOnCreate(ActionBarImpl.java:909)
    at com.android.internal.app.ActionBarImpl.startActionMode(ActionBarImpl.java:453)
    at android.app.Activity.onWindowStartingActionMode(Activity.java:5005)
    at com.android.internal.policy.impl.PhoneWindow$DecorView.startActionMode(PhoneWindow.java:2636)
    at android.app.Activity.startActionMode(Activity.java:4987)
    at it.gmariotti.cardslib.library.internal.multichoice.MultiChoiceAdapterHelperBase.startActionMode(MultiChoiceAdapterHelperBase.java:239)
    at it.gmariotti.cardslib.library.internal.CardArrayMultiChoiceAdapter.startActionMode(CardArrayMultiChoiceAdapter.java:117)
    at com.mypackage.android.design.appdesgin.fragments.CardLocalBackupsFragment$1.onLongClick(CardLocalBackupsFragment.java:152)
    at it.gmariotti.cardslib.library.view.CardView$5.onLongClick(CardView.java:533)
    at android.view.View.performLongClick(View.java:4481)
    at android.view.View$CheckForLongPress.run(View.java:18425)
    at android.os.Handler.handleCallback(Handler.java:733)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:136)
    at android.app.ActivityThread.main(ActivityThread.java:5081)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:515)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:607)
    at dalvik.system.NativeStart.main(Native Method)

The null pointer is here:

protected void onItemSelectedStateChanged(ActionMode mode) {
    int count = mAdapterView.getCheckedItemCount(); // mAdapterView is NULL

    if (count > 0) {
        Resources res = mAdapterView.getResources();
        String mTitleSelected = res.getQuantityString(R.plurals.card_selected_items, count, count);
        mode.setTitle(mTitleSelected);
    }
}

but this code belongs to MultiChoiceAdapterHelperBase which is part of cardlib's code.

Following the stacktrace it leads to this class (which I copied from the tutorial), MyCardArrayMultiChoiceAdapter which has this code (NPE is on the call to super on onCreateActionMode)

public class MyCardArrayMultiChoiceAdapter extends CardArrayMultiChoiceAdapter {

    private ActionMode mActionMode;

    public MyCardArrayMultiChoiceAdapter(Context context, List<Card> cards) {
        super(context, cards);
    }

    @Override
    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        //It is very important to call the super method
        super.onCreateActionMode(mode, menu); // This is line 31 where it says in my stacktrace

        mActionMode = mode; // to manage mode in your Fragment/Activity

        //If you would like to inflate your menu
        MenuInflater inflater = mode.getMenuInflater();
        inflater.inflate(R.menu.carddemo_multichoice, menu);
        return true;
    }

    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        return false;
    }

    @Override
    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
//        if (item.getItemId() == R.id.menu_share) {
//            Toast.makeText(getContext(), "Share;" + formatCheckedCard(), Toast.LENGTH_SHORT).show();
//            return true;
//        }
        if (item.getItemId() == R.id.menu_discard) {
            discardSelectedItems(mode);
            return true;
        }
        return false;
    }

    @Override
    public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked, CardView cardView, Card card) {
        Toast.makeText(getContext(), "Click;" + position + " - " + checked, Toast.LENGTH_SHORT).show();
    }

    private void discardSelectedItems(ActionMode mode) {
        ArrayList<Card> items = getSelectedCards();
        for (Card item : items) {
            remove(item);
        }
        mode.finish();
    }


    private String formatCheckedCard() {

        SparseBooleanArray checked = mCardListView.getCheckedItemPositions();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < checked.size(); i++) {
            if (checked.valueAt(i) == true) {
                sb.append("\nPosition=" + checked.keyAt(i));
            }
        }
        return sb.toString();
    }
}

Can anyone figure out why this null point exception? Or what am I doing wrong to implement multichoice mode in my cardslist?

If there's the need to put more code here let me know and I'll edit this question.


Solution

  • Found the problem.

    I was still using CardArrayAdapter on my fragment that has the list. I moved from CardArrayAdapter to CardArrayMultiChoiceAdapter and it is working flawlessly :)

    So I had something like (on my fragment):

    private CardArrayAdapter cardArrayAdapter; // comment this one
    private MyCardArrayMultiChoiceAdapter cardArrayAdapter; // use this one!
    

    has in the tutorial:

    If you would like to have a CardList with a MultiChoiceMode built-in feature you can use a CardArrayMultiChoiceAdapter.

    This class extends CardArrayAdapter and preserves all its features.