简体   繁体   中英

RecyclerView.Adapter return null object reference inside Fragment with viewPager

I am trying to create the page that have view pager with mvvm architecture.

So what i trying to do is showing the Recycler view inside the fragment of the first tab by using observable method.

But when i try to set the content is of the adapter inside my fragment i got

java.lang.NullPointerException: Attempt to invoke virtual method 'void com.xxxx.view.adapter.FoodListAdapter.setFoodList(java.util.List)' on a null object reference 

My FragmentStatePagerAdapter adapter

@Override
    public Fragment getItem(int position) {
        switch (position) {
            case 0:
                Log.d("Food Pager", "get Item");
                FoodListFragment foodListFragment = new FoodListFragment();
                return foodListFragment;
            case 1:
                RestaurantListFragment restaurantListFragment2 = new RestaurantListFragment();
                return restaurantListFragment2;
            case 2:
                RestaurantListFragment restaurantListFragment3 = new RestaurantListFragment();
                return restaurantListFragment3;
            default:
                return null;
        }
    } 

My activity

private void observeViewModel(final CategoryViewModel categoryViewModel) {
        // Observe Category Data
        categoryViewModel.getCategory().observe(this, new Observer<Category>() {
            @Override
            public void onChanged(@Nullable Category category) {
                // Update UI
                if (category != null) {
                    if (category.foods != null) {
                        startFoodListFragment(category.foods);
                    }
                } else {
                    Log.e(TAG, "Category Data is Null");
                }
            }
        });
    }

    private void startFoodListFragment(List<CategoryQuery.Food> foodList) {
        Log.d("startFoodListFragment", "yolo");
        FoodListFragment foodListFragment = new FoodListFragment();
        foodListFragment.setFoodList(foodList);
    }

So inside my Fragment

@Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        Log.d("Food LIst", "On create VIew");
        View rootView = inflater.inflate(R.layout.fragment_food_list, container, false);

        mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerview_food_list);
        // mLoadingIndicator = (ProgressBar) rootView.findViewById(R.id.food_list_loading_indicator);

        Integer gridColumns = 2;

        Float width = getScreenSize().get("width");

        if (width > 600) {
            gridColumns = 3;
        }

        // Create Pinterest Style RecyclerView
        StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(gridColumns, StaggeredGridLayoutManager.VERTICAL);
        mRecyclerView.setLayoutManager(layoutManager);

        // Cool. it's Very easy ,but margin on my LinearLayout didn’t seem to work. So here’s a quick fix.
        SpacesItemDecoration decoration = new SpacesItemDecoration(8);
        mRecyclerView.addItemDecoration(decoration);

        mFoodListAdapter = new FoodListAdapter(foodListAdapterOnClickHandler);
        mRecyclerView.setAdapter(mFoodListAdapter);

        return rootView;
    }

@Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        if (mFoodList != null) {
            mFoodListAdapter.setFoodList(mFoodList);
        }
    }

    public void setFoodList(List<CategoryQuery.Food> foodList) {
        Log.d(TAG, "setFoodlist");
        if (foodList != null) {
            mFoodList = foodList;
            mFoodListAdapter.setFoodList(mFoodList);
            // mFoodListAdapter.notifyDataSetChanged();
        }
    }

This is part of my adapter

public void setFoodList(final List<? extends CategoryQuery.Food> foodList) {
        if (this.foodList == null) {
            this.foodList = foodList;
            notifyItemRangeInserted(0, foodList.size());
        }
    }

This is the related Log that i got

D/Food Pager: get Item
D/Food LIst: On create VIew
D/startFoodListFragment: yolo
D/Food List Fragment: setFoodlist
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main

Then the error goes on

java.lang.NullPointerException: Attempt to invoke virtual method 'void com.xxx.view.adapter.FoodListAdapter.setFoodList(java.util.List)' on a null object reference

So anyone knows why i got null, when calling FoodListAdapter even after i was initialise inside onCreateView ?

Thanks for your help!

What does the log tell us?

D/Food Pager: get Item

The FragmentStatePagerAdapter.getItem(int position) was called to show a FoodListFragment . This usually means the Fragment will be added to the screen, so its onCreateView() should be called next. Which is actually the case:

D/Food LIst: On create VIew

On the other hand, we have an Observer to listen for data changes. Every time it fires, startFoodListFragment() will be called:

D/startFoodListFragment: yolo

In this method, a new instance of FoodListFragment will be created . You try to pass the new data into it by calling setFoodList()

D/Food List Fragment: setFoodlist

But since this new instance is not about to be attached to the screen, its onCreateView() has not been called. So its adapter has not been instantiated. That's why the app crashes:

D/AndroidRuntime: Shutting down VM E/AndroidRuntime: FATAL EXCEPTION: main

What can you do?

You should pass the data into the correct instance of FoodListFragment . Or simply register the Observer inside FoodListFragment . A Fragment is a Lifecycle Owner just like an Activity , so I think this approach would work best for you.

In your activity you have this method startFoodListFragment() which creates a fragment and calls setFoodList() method on it which internally sets data to adapter.

As the adapter instance is being created on onCreateView() lifecycle method, the adapter would not be initialized at the time of object creation. so it will be null at that time that's why it is throwing NullPointerException . Make sure that when you access adapter, it must be after the onCreateView() method is executed so that you will never get NPE again!

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