19

Mamy na naszej aplikacji ViewPager z FragmentPagerAdapter, który zawiera trzy fragmenty. Dwa z tych fragmentów składają się z Recyclerview dla każdego z nich.NullPointerException na ViewPager z widokiem Recycler

Pierwsza strona (fragment bez ViewPagera) jest wyświetlana poprawnie. Jednak, gdy ViewPager próbuje wstępnie załadować następną stronę (a RecyclerView), awarii aplikacji powodu NullPointerException z poniższej 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) 

Oto jak ViewPager deklaruje:

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

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

Jako kod z obu RecyclerView jest długa i inny dla każdej strony i naprawdę nie wiem, która część jest istotne, więc nie da dowolna próbka. Nie wahaj się poprosić o konkretną część, jeśli uważasz, że może to być pomocne w rozwiązaniu problemu.

Jedna rzecz, którą mogę powiedzieć, to to, że jeśli chcę, żeby to zadziałało, muszę skomentować wołanie dla każdego z setAdapter z obu RecylerView.

EDYCJA: Oto kod drugiej strony.

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

Każda pomoc jest doceniana.

+0

jest jakiś recyclerView w trzecim fragmencie? [EDYCJA] przepraszam, jest tam. Możesz opublikować swój kod drugiego fragmentu –

+0

Pierwsza strona to fragment bez żadnego RecyclerView. Jednak tak, drugi i trzeci to fragmenty skomponowane z RecyclerView dla każdego z nich. – Neeeko

+0

Czy możesz opublikować metodę 'onBindViewHolder()' swojego adaptera? – Rami

Odpowiedz

18

Mam ten błąd podczas jednego z moich badań. Czy sprawdziłeś, że Twój RecyclerView w twoich plikach XML jest poprawnie zawinięty w inny układ, taki jak FrameLayout?

Jeśli nie, spowoduje awarię tylko w Viewpager, a nie w widoku pojedynczego fragmentu.

+2

Masz rację! Mój układ składał się tylko z RecyclerView, nie wiedziałem, że musi być zawinięty w inny. Dzięki za pomoc ! – Neeeko

+0

Bingo; wielkie dzięki, Ligol. –

+0

DZIĘKUJEMY SOOOO MUCH !! –

-2

Zmiana:

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

do:

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

zmiennej Końcowego nie podejmie żadnych wartości, nawet jeśli starają się je dodać.

+0

. Wypróbowałem Twoje rozwiązanie i nadal mam NPE. :/ – Neeeko

+0

Oh. Zamiast dodawać jeden po drugim, możesz wysłać pełną listę do adaptera i dodać wszystkie do listy w adapterze. –

+3

To nie działa jak "zmienne końcowe". Możesz dodawać i usuwać elementy z listy, nawet jeśli są one deklarowane jako ostateczne. –

4

Dzieje się tak, gdy przypadkowo dodasz widoki bezpośrednio do RecyclerView. W moim przypadku użyłem View.inflate dla układu dekoratora z RecyclerView jako parametrem nadrzędnym, który automatycznie go dołącza. RecyclerView iteruje nad wszystkimi dziećmi dołączonymi do niego i oczekuje, że wszystkie jego widoki będą miały ViewHolders w parametrach układu, i wyrzuci to NPE, gdy uchwyt widoku dziecka ma wartość NULL.

0

Dzieje się tak, gdy dodasz elementy bezpośrednio pod listView lub RecyclerView w układu xml pliku.

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

Tutaj dodałem TextView wewnątrz RecyclerView która rzuci mi onLayout error (spowodowane przez NullPointerException) .you nie powinno dodawać elementy bezpośrednio pod RecyclerView lub listView.

0

Dodawanie Bez dzieci w widoku recyklera i ustawienie attachToRoot, trzeci parametr inflate() metody false podczas pompowania układ niestandardowy pracował dla mnie.

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

Układ:

<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> 
Powiązane problemy