Подтвердить что ты не робот

NullPointerException на ViewPager с Recyclerview

В нашем приложении есть ViewPager с FragmentPagerAdapter, который содержит три фрагмента. Два из этих фрагментов состоят из Recyclerview для каждого из них.

Первая страница (фрагмент без ViewPager) отображается правильно. Однако, когда ViewPager пытается предварительно загрузить следующую страницу (RecyclerView), приложение вылетает из-за NullPointerException со следующим журналом:

 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)

Здесь, как объявлен ViewPager:

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

И адаптер:

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

Поскольку код из обоих RecyclerView длинный и отличается для каждой страницы, я действительно не знаю, какая часть релевантна, поэтому я не буду давать какой-либо образец. Не стесняйтесь обращаться к определенной части, если считаете, что это может быть полезно для устранения проблемы.

Единственное, что я могу вам сказать, это то, что если я хочу, чтобы он работал, я должен прокомментировать вызов для каждого из setAdapter из обоих RecylerView.

EDIT: здесь код для второй страницы.

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

Любая помощь очень ценится.

4b9b3361

Ответ 1

У меня есть эта ошибка во время одного из моих разработок. Вы проверили, что ваш RecyclerView в ваших XML файлах правильно завернут в другой макет, например FrameLayout?

Если это не так, это приведет к сбою только в представлении, а не в виде одиночного фрагмента.

Ответ 2

Это происходит, когда вы случайно добавляете представления непосредственно в RecyclerView. В моем случае я использовал View.inflate для макета декоратора с RecyclerView в качестве родительского параметра, который автоматически присоединяет его. RecyclerView выполняет итерацию над любыми дочерними элементами, прикрепленными к нему, и ожидает, что все его дети с представлением будут иметь ViewHolders в параметрах макета и будут бросать этот NPE, когда владелец дочернего представления имеет значение null.

Ответ 3

Это происходит, когда вы добавляете элементы непосредственно в listView или RecyclerView в свой 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>

Здесь я добавил TextView внутри RecyclerView, который выкинет меня onLayout error (вызванный NullPointerException). Вы не должны добавлять элементы непосредственно под RecyclerView или listView.

Ответ 4

Добавляя без детей в просмотрщик и настройку attachToRoot, третий параметр метода inflate() false при раздувании настраиваемого макета работал у меня.

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

Разметка:

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

Ответ 5

Изменить:

 private final List<Fragment> mFragmentList = new ArrayList<>();
 private final List<String> mFragmentTitleList = new ArrayList<>();

to:

 // remove final keyword
 private List<Fragment> mFragmentList = new ArrayList<>();
 private List<String> mFragmentTitleList = new ArrayList<>();

Конечная переменная не принимает никаких значений, даже если вы попытаетесь их добавить.