简体   繁体   中英

Null Pointer Exception in RecyclerView within Fragment with Firestore

I am trying to populate recyclerView with Java objects using Firestore. The RecyclerView is within a fragment. I am using a recyclerViewAdapter which extends the FirestoreRecyclerAdapter class. When I run the code, the app crashes immediately.

Here's the error.

.NullPointerException: Attempt to invoke virtual method 'void androidx.recyclerview.widget.RecyclerView.setLayoutManager(androidx.recyclerview.widget.RecyclerView$LayoutManager)' on a null object reference

Here's some of the fragment Java code:


    public class EventsFragment extends Fragment{
    private FirebaseFirestore db = FirebaseFirestore.getInstance();
    private CollectionReference eventRef = db.collection("Events");

    private EventRecyclerAdapter adapter;

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

        setUpRecyclerView();
        return rootView;
    }

    private void setUpRecyclerView(){
        Query query = eventRef.limit(8);
        FirestoreRecyclerOptions<Event> options = new FirestoreRecyclerOptions.Builder<Event>()
                .setQuery(query, Event.class)
                .build();

        adapter = new EventRecyclerAdapter(options);

        RecyclerView recyclerView = (RecyclerView) getActivity().findViewById(R.id.events_recycler_view);
        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
        recyclerView.setHasFixedSize(true);
        recyclerView.setAdapter(adapter);
    }

 }

Here's the fragment xml:

<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">


    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <com.google.android.material.appbar.MaterialToolbar
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:title="Upcoming Events"
            app:menu="@menu/main_menu"
            android:background="@android:color/white"
            />

    </com.google.android.material.appbar.AppBarLayout>

        <androidx.recyclerview.widget.RecyclerView
            app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:paddingTop="8dp"
            android:clipToPadding="false"
            android:divider="@null"
            android:id="@+id/events_recycler_view"/>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

Here's the RecyclerAdapter code:

public class EventRecyclerAdapter extends FirestoreRecyclerAdapter<Event, EventRecyclerAdapter.EventHolder> {

    public EventRecyclerAdapter(@NonNull FirestoreRecyclerOptions<Event> options) {
        super(options);
    }

    @Override
    protected void onBindViewHolder(@NonNull EventHolder holder, int position, @NonNull Event model) {
        holder.textViewTitle.setText(model.getTitle());
        holder.textViewDescription.setText(model.getDescription());
    }

    @NonNull
    @Override
    public EventHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.events_list_item,
                parent, false);
        return new EventHolder(v);
    }

    class EventHolder extends RecyclerView.ViewHolder {
        TextView textViewTitle;
        TextView textViewDescription;

        public EventHolder(@NonNull View itemView) {
            super(itemView);
            textViewTitle = itemView.findViewById(R.id.title);
            textViewDescription = itemView.findViewById(R.id.description);
        }
    }
}

I believe the problem lies somewhere with the RecyclerView. I've been trying to find the problem for hours now. Is there something I'm overlooking? I just need it to run.

You already noticed that recyclerView is null when you try to set the LayoutManager in setUpRecyclerView()

This happens because you are trying to "find" the RecyclerView in the Activity 's View tree before the Fragment 's View has actually been attached to it:

RecyclerView recyclerView = (RecyclerView) getActivity().findViewById(R.id.events_recycler_view);

The first Fragment lifecycle method where you can put this statement is onResume() (at least in my example app on an emulator running Android 9)

But you can configure the RecyclerView much earlier because you can "find" it in the Fragment 's own View for example in onViewCreated() :

@Override
public void onViewCreated(View view, Bundle savedInstanceState){
    RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.events_recycler_view);
    recyclerView.setLayoutManager(new LinearLayoutManager(view.getContext());
    // etc.
}

Please note that the view parameter in onViewCreated() represents the View which you can get everywhere in the Fragment by calling getView() . But only after onCreateView() has finished, because the purpose of this method is creating and "publishing" the Fragment 's View in the first place.

Last not least, inside of onCreateView() you can "find" the RecyclerView by means of the newly inflated View :

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

    //setUpRecyclerView();
    RecyclerView recyclerView = (RecyclerView) rootView .findViewById(R.id.events_recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(rootView.getContext());
    // etc.

    return rootView;
}

So maybe you'd best pass the root View as parameter into setUpRecyclerView() and use it as follows:

private void setUpRecyclerView(ViewGroup root){
    Query query = eventRef.limit(8);
    FirestoreRecyclerOptions<Event> options = new FirestoreRecyclerOptions.Builder<Event>()
            .setQuery(query, Event.class)
            .build();

    adapter = new EventRecyclerAdapter(options);

    RecyclerView recyclerView = (RecyclerView) root.findViewById(R.id.events_recycler_view);
    recyclerView.setLayoutManager(new LinearLayoutManager(root.getContext()));
    recyclerView.setHasFixedSize(true);
    recyclerView.setAdapter(adapter);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM