Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NullPointerException on ViewPager with Recyclerview

We have on our app a ViewPager with a FragmentPagerAdapter which contains three fragments. Two of these fragments are composed with a Recyclerview for each one.

The first page (the fragment without a ViewPager) is displayed correctly. However, when the ViewPager tries to pre-load the next page (a RecyclerView), the app crashes because of a NullPointerException with the following log :

 java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.support.v7.widget.RecyclerView$ViewHolder.shouldIgnore()' on a null object reference
         at android.support.v7.widget.RecyclerView.findMinMaxChildLayoutPositions(RecyclerView.java:2839)
         at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:2626)
         at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:3011)
         at android.view.View.layout(View.java:15684)
         at android.view.ViewGroup.layout(ViewGroup.java:4981)
         at android.support.v4.view.ViewPager.onLayout(ViewPager.java:1626)
         at android.view.View.layout(View.java:15684)
         at android.view.ViewGroup.layout(ViewGroup.java:4981)
         at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573)
         at android.widget.FrameLayout.onLayout(FrameLayout.java:508)
         at android.view.View.layout(View.java:15684)
         at android.view.ViewGroup.layout(ViewGroup.java:4981)
         at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1703)
         at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1557)
         at android.widget.LinearLayout.onLayout(LinearLayout.java:1466)
         at android.view.View.layout(View.java:15684)
         at android.view.ViewGroup.layout(ViewGroup.java:4981)
         at android.support.design.widget.CoordinatorLayout.layoutChild(CoordinatorLayout.java:1000)
         at android.support.design.widget.CoordinatorLayout.onLayoutChild(CoordinatorLayout.java:710)
         at android.support.design.widget.CoordinatorLayout.onLayout(CoordinatorLayout.java:724)
         at android.view.View.layout(View.java:15684)
         at android.view.ViewGroup.layout(ViewGroup.java:4981)
         at android.support.v4.widget.DrawerLayout.onLayout(DrawerLayout.java:907)
         at android.view.View.layout(View.java:15684)
         at android.view.ViewGroup.layout(ViewGroup.java:4981)
         at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573)
         at android.widget.FrameLayout.onLayout(FrameLayout.java:508)
         at android.view.View.layout(View.java:15684)
         at android.view.ViewGroup.layout(ViewGroup.java:4981)
         at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1703)
         at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1557)
         at android.widget.LinearLayout.onLayout(LinearLayout.java:1466)
         at android.view.View.layout(View.java:15684)
         at android.view.ViewGroup.layout(ViewGroup.java:4981)
         at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573)
         at android.widget.FrameLayout.onLayout(FrameLayout.java:508)
         at android.view.View.layout(View.java:15684)
         at android.view.ViewGroup.layout(ViewGroup.java:4981)
         at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1703)
         at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1557)
         at android.widget.LinearLayout.onLayout(LinearLayout.java:1466)
         at android.view.View.layout(View.java:15684)
         at android.view.ViewGroup.layout(ViewGroup.java:4981)
         at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573)
         at android.widget.FrameLayout.onLayout(FrameLayout.java:508)
         at android.view.View.layout(View.java:15684)
         at android.view.ViewGroup.layout(ViewGroup.java:4981)
         at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2186)
         at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1920)
         at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1106)
         at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6018)
         at android.view.Choreographer$CallbackRecord.run(Choreographer.java:792)
         at android.view.Choreographer.doCallbacks(Choreographer.java:596)
         at android.view.Choreographer.doFrame(Choreographer.java:557)
         at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:778)
         at android.os.Handler.handleCallback(Handler.java:739)
         at android.os.Handler.dispatchMessage(Handler.java:95)
         at android.os.Looper.loop(Looper.java:155)
         at android.app.ActivityThread.main(ActivityThread.java:5696)
         at java.lang.reflect.Method.invoke(Native Method)
         at java.lang.reflect.Method.invoke(Method.java:372)

Here's how the ViewPager is declared :

ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFrag(fragment1, "fragment1");
adapter.addFrag(fragment2, "fragment2");
adapter.addFrag(fragment3, "fragment3");
viewPager.setAdapter(adapter);

And the adapter :

    private class ViewPagerAdapter extends FragmentPagerAdapter {
    private final List<Fragment> mFragmentList = new ArrayList<>();
    private final List<String> mFragmentTitleList = new ArrayList<>();
    public ViewPagerAdapter(FragmentManager manager) {
        super(manager);
    }
    @Override
    public Fragment getItem(int position) {
        return mFragmentList.get(position);
    }
    @Override
    public int getCount() {
        return mFragmentList.size();
    }
    public void addFrag(Fragment fragment, String title) {
        mFragmentList.add(fragment);
        mFragmentTitleList.add(title);
    }
    @Override
    public CharSequence getPageTitle(int position) {
        return mFragmentTitleList.get(position);
    }
}

As the code from both of the RecyclerView is long and different for each page I don't really know which part is relevant so I won't give any sample. Do not hesitate to ask for a specific part if you think it can be helpful to fix the issue.

One thing that I can tell you is that if I want it to work, I have to comment the call for each of the setAdapter from both of the RecylerView.

EDIT : Here's the code for the second page.

public class MyFragment extends Fragment {

    RecyclerView recyclerView;
    GridAdapter gridAdapter;

    public GridAdapter getGridAdapter() {
        return gridAdapter;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        final View v = inflater.inflate(R.layout.our_layout, container, false);
        recyclerView = (RecyclerView) v.findViewById(R.id.recycler_view);
        gridLayoutManager.setSmoothScrollbarEnabled(true);
        recyclerView.setLayoutManager(gridLayoutManager);

        recyclerView.setHasFixedSize(true);

        return v;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        ArrayList<Model> model = getArguments().getParcelableArrayList("extra");
        if (model != null && model.size() != 0) {
            gridAdapter = new GridAdapter(model);
            recyclerView.setAdapter(gridAdapter);
        }
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isVisibleToUser && isResumed()){
            onResume();
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        if (!getUserVisibleHint())
            return;
    }

    public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {

        private int spanCount;
        private int spacingLeft;
        private int spacingRight;
        private int spacingTop;
        private int spacingBottom;
        private boolean includeEdge;

        public GridSpacingItemDecoration(int spanCount, int spacingLeft, int spacingTop, int spacingRight, int spacingBottom, boolean includeEdge) {
            this.spanCount = spanCount;
            this.spacingLeft = spacingLeft;
            this.spacingRight = spacingRight;
            this.spacingTop = spacingTop;
            this.spacingBottom = spacingBottom;
            this.includeEdge = includeEdge;
        }

        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            int position = parent.getChildAdapterPosition(view); // item position
            int column = position % spanCount; // item column

            if (includeEdge) {
                outRect.left = spacingLeft - column * spacingLeft / spanCount; // spacing - column * ((1f / spanCount) * spacing)
                outRect.right = (column + 1) * spacingRight / spanCount; // (column + 1) * ((1f / spanCount) * spacing)

                if (position < spanCount) { // top edge
                    outRect.top = spacingTop;
                }
                outRect.bottom = spacingBottom; // item bottom
            } else {
                outRect.left = column * spacingLeft / spanCount; // column * ((1f / spanCount) * spacing)
                outRect.right = spacingRight - (column + 1) * spacingRight / spanCount; // spacing - (column + 1) * ((1f /    spanCount) * spacing)
                if (position >= spanCount) {
                    outRect.top = spacingTop; // item top
                }
            }
        }
    }

    public class GridAdapter extends RecyclerView.Adapter<GridAdapter.ViewHolder> {

        private ArrayList<Model> model;

        public GridAdapter(ArrayList<Model> offer) {
            super();
            model = offer;
        }

        @Override
        public ViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {
            final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_item, parent, false);
            final ViewHolder holder = new ViewHolder(view);
            return holder;
        }

        @Override
        public void onBindViewHolder(final ViewHolder holder, final int position) {
            final Model currentOffer = model.get(position);

            holder.category.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @SuppressLint("NewApi")
                @SuppressWarnings("deprecation")
                @Override
                public void onGlobalLayout() {
                    int width = holder.category.getWidth();
                    ViewGroup.LayoutParams params = holder.appIcon.getLayoutParams();
                    params.width = width;
                    params.height = width;

                    holder.appIcon.setLayoutParams(params);

                    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN)
                        holder.itemView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                    else
                        holder.itemView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                }
            });

            Picasso.with(getActivity().getApplicationContext()).
                    load(currentOffer.getApp_logo()).fit().centerCrop().into(holder.appIcon);
            holder.appName.setText(currentOffer.getApp_name());
            holder.category.setText(currentOffer.getApp_category());

            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    String marketURL = AndroidTools.getPlayStoreURL(currentOffer.getApp_store_id(), true);

                    UITools.launchUrl(getActivity(), marketURL);

                }
            });

        }

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

        class ViewHolder extends RecyclerView.ViewHolder {
            private ImageView appIcon;
            private TextView appName;
            private TextView category;

            public ViewHolder(View itemView) {
                super(itemView);
                appIcon = (ImageView)itemView.findViewById(R.id.item_icon);
                appName = (TextView)itemView.findViewById(R.id.item_app_name);
                category = (TextView)itemView.findViewById(R.id.item_category);
            }
        }
    }
}

Any help is much appreciated.

like image 437
Neeko Avatar asked Sep 21 '15 09:09

Neeko


3 Answers

I've got this error during one of my development. Have you checked that your RecyclerView in your XML files are correctly wrapped into another layout like a FrameLayout?

If not, it will crash only on a Viewpager and not on single fragment view.

like image 118
Ligol Avatar answered Nov 17 '22 07:11

Ligol


This happens when you accidentally add views directly to the RecyclerView. In my case, I used View.inflate for a decorator layout with the RecyclerView as the parent parameter, which automatically attaches it. RecyclerView iterates over any children attached to it and expects all its view children to have ViewHolders in the layout params, and will throw this NPE when a child's view holder is null.

like image 20
visser4 Avatar answered Nov 17 '22 07:11

visser4


This happens when you add elements directly under listView or RecyclerView in your xml layout file.

<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical">

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content" /> 

 </android.support.v7.widget.RecyclerView>

Here I have added a TextView inside RecyclerView which will throw me onLayout error(caused by NullPointerException).You shouldn't add elements directly under RecyclerView or listView.

like image 1
Siva Prakash Avatar answered Nov 17 '22 09:11

Siva Prakash