Frank B.
Frank B.

Reputation: 204

NullPointerExecption on RecyclerView Start

I have a view pager with three fragments (A->B->C):

Fragment A has two edittexts and a save button; Fragment B has a toolbar and a scrollable textview; Fragment C has a RecyclerView with a LinearLayout set to it.

On app launch, I swipe to Fragment B and I get an NPE on the view adapter getItemCount() of my ListFragment (Fragment C). This happens on a physical test device (API 21) but not on any emulator (API 18-23).

What could be the cause?

EDIT: Add HomeActivity.class Interface Method:

@Override
public ArrayList<String> getList() {
    titleSharedPref = getSharedPreferences(TITLE_PREF, Context.MODE_PRIVATE);
    try {
        this.songArrayList = (ArrayList<String>) ObjectSerializer
                .deserialize(titleSharedPref.getString(TITLE_PREF, ObjectSerializer.serialize(new ArrayList<String>())));
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    return this.songArrayList;
}

ListFragment.class:

public class ListFragment extends Fragment {

    private RecyclerView recyclerView;
    private SongRecyclerViewAdapter recyclerViewAdapter;
    private FragmentCommunicator communicator;

    public ListFragment() {
        // Required empty constructor
    }

    public static ListFragment newInstance() {
        return new ListFragment();
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof FragmentCommunicator) {
            communicator = (FragmentCommunicator) context;
        } else {
            throw new RuntimeException(context.toString()
                + " must implement FragmentCommunicator");
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_song_list, container, false);

        // Set the adapter
        if (view instanceof RecyclerView) {
            Context context = view.getContext();
            recyclerView = (RecyclerView) view;
            recyclerView.setLayoutManager(new LinearLayoutManager(context));
            recyclerViewAdapter = new SongRecyclerViewAdapter(communicator.getList(), communicator);
            recyclerView.setAdapter(recyclerViewAdapter);
        }

        ItemTouchHelper.SimpleCallback simpleItemCallBack = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT){
            @Override
            public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
                return false;
            }

            @Override
            public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
                int pos = viewHolder.getAdapterPosition();
                if (communicator.remove(pos)) {
                    recyclerViewAdapter.notifyItemRemoved(pos);
                    recyclerViewAdapter.notifyItemRangeChanged(pos, recyclerViewAdapter.getItemCount());
                }
            }
        };
        ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemCallBack);
        itemTouchHelper.attachToRecyclerView(recyclerView);

        return view;
    }

    /**
     * {@link RecyclerView.Adapter} that can display a {@link ArrayList} and makes a call to the
     * specified {@link FragmentCommunicator}.
     */
    public class SongRecyclerViewAdapter extends RecyclerView.Adapter<SongRecyclerViewAdapter.ViewHolder> {

        private final ArrayList<String> mValues;
        private final FragmentCommunicator communicator;

        public SongRecyclerViewAdapter(ArrayList<String> items, FragmentCommunicator communicator) {
            this.mValues = items;
            this.communicator = communicator;
        }

        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false));
        }

        public class ViewHolder extends RecyclerView.ViewHolder {
            public final View mView;
            public final TextView mContentView;
            public String mItem;

            public ViewHolder(View view) {
                super(view);
                mView = view;
                mContentView = (TextView) view.findViewById(R.id.content);
            }

            @Override
            public String toString() {
                return super.toString() + " '" + mContentView.getText() + "'";
            }
        }

        @Override
        public void onBindViewHolder(final ViewHolder holder, final int position) {
            holder.mItem = mValues.get(position);
            holder.mContentView.setText(holder.mItem);
            holder.mView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (null != communicator) {
                        // Notify the active callbacks interface (the activity, if the
                        // fragment is attached to one) that an item has been selected.
                        communicator.respond(holder.mItem, HomeActivity.MODE_PICK);
                    }
                }
            });
        }

        @Override
        public int getItemCount() {
            return mValues.size();
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        // kill
        communicator = null;
        recyclerViewAdapter = null;
    }
}

EDIT: Removed second question: adding ViewPager listener.

Thank you all!

Upvotes: 0

Views: 163

Answers (2)

Frank B.
Frank B.

Reputation: 204

@rf43 Thanks for help. It was a device specific issue with my HTC One M7 test device. I manually uninstalled the debug apk (install of install over) and installed it again. Now my app works as designed. There must have been some memory issue with the device.

Upvotes: 0

rf43
rf43

Reputation: 4405

First and foremost, you should seriously consider cleaning up your fragment code. I would recommend that you extract the viewholder and adapter out into their own classes. This will help you tremendously when working your code by isolating the code into its specific area of need.

Secondly, think about doing the view work in the viewholder, not in the onBindViewHolder() method of the adapter. You can make a method named populateViews() or something and then pass in your data that will populate the views in the viewholder.

Third, are you sure that data is in fact being passed to the RecyclerView? My guess is that, no, you probably do not have data being passed to it and therefore it is throwing a NPE. Remember, a FragmentPagerAdapter will attempt to fire up at least three fragments - the one that is visible, the one to the left and the one to the right. In your case, when you swipe to fragment B, it is trying to populate the RecyclerView in fragment C but you don't have any data there yet.

As for how to pass the data from fragment A to fragment B without the user having to click something... simply pass the contents of your fragment A's EditTexts up to the Activity and then send that data to fragment B. You can do this in intervals (for example, after each character is entered) or when there is a pause by the user or there is even a callback when the FragmentPagerAdapter begins the swipe that you could utilize.

Upvotes: 2

Related Questions