web-dev-qa-db-fra.com

NullPointerException sur ViewPager avec Recyclerview

Nous avons sur notre application un ViewPager avec un FragmentPagerAdapter qui contient trois fragments. Deux de ces fragments sont composés avec un Recyclerview pour chacun.

La première page (le fragment sans ViewPager) est affichée correctement. Cependant, lorsque ViewPager tente de précharger la page suivante (un RecyclerView), l'application se bloque à cause d'une NullPointerException avec le journal suivant:

 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)

Voici comment le ViewPager est déclaré:

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);

Et l'adaptateur:

    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);
    }
}

Comme le code de RecyclerView est long et différent pour chaque page, je ne sais pas vraiment quelle partie est pertinente, je ne donnerai donc aucun échantillon. N'hésitez pas à demander une pièce spécifique si vous pensez qu'il peut être utile de résoudre le problème.

Une chose que je peux vous dire est que si je veux que cela fonctionne, je dois commenter l'appel pour chacun des setAdapter de RecylerView.

EDIT: Voici le code de la deuxième 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);
            }
        }
    }
}

Toute aide est très appréciée.

21
Neeeko

J'ai cette erreur lors d'un de mes développements. Avez-vous vérifié que votre RecyclerView dans vos fichiers XML est correctement intégré à une autre présentation, comme un FrameLayout?

Sinon, il se bloque uniquement sur un Viewpager et non sur une vue à fragment unique.

20
Ligol

Cela se produit lorsque vous ajoutez accidentellement des vues directement à la RecyclerView. Dans mon cas, j’ai utilisé View.inflate pour une mise en page de décorateur avec RecyclerView comme paramètre parent, qui l’attache automatiquement. RecyclerView itère tous les enfants qui y sont attachés et s'attend à ce que tous les enfants de la vue aient ViewHolders dans les paramètres de disposition, et jetteront cette NPE lorsque le détenteur de la vue d'un enfant est nul.

6
visser4

Ajouter pas d'enfants dans la vue du recycleur et définir attachToRoot, le troisième paramètre de la méthode inflate() dansfalsetout en gonflant la présentation personnalisée a fonctionné pour moi.

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.online_user, parent, false);
    return new RecyclerViewHolder(view.findViewById(R.id.onlineUserView));
}

Disposition:

<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:orientation="vertical">

<Android.support.v7.widget.RecyclerView
    Android:id="@+id/onlineUsersView"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent" />

</LinearLayout>
0
Nabin Bhandari

Cela se produit lorsque vous ajoutez des éléments directement sous listView ou RecyclerViewdans votre fichier présentation xml.

<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>

Ici, j'ai ajouté un TextView à l'intérieur de RecyclerView qui me lancera onLayout error (causé par NullPointerException). Vous ne devriez pas ajouter d'éléments directement sous RecyclerView ou listView.

0
Siva Prakash