19

Abbiamo sulla nostra app uno ViewPager con uno FragmentPagerAdapter che contiene tre frammenti. Due di questi frammenti sono composti con uno Recyclerview per ciascuno.NullPointerException su ViewPager con Recyclerview

La prima pagina (il frammento senza ViewPager) viene visualizzata correttamente. Tuttavia, quando il ViewPager cerca di pre-caricare la pagina successiva (un RecyclerView), l'applicazione si blocca a causa di un NullPointerException con il seguente registro:

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) 

Ecco come il ViewPager è dichiarato:

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

e l'adattatore:

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

come il codice sia del RecyclerView è lungo e diverso per ogni pagina io non so davvero quale parte rilevante così vi frega qualsiasi campione Non esitare a chiedere una parte specifica se pensi che possa essere utile risolvere il problema.

Una cosa che posso dirvi è che se voglio che funzioni, devo commentare la chiamata per ciascuno degli setAdapter da entrambi i RecylerView.

MODIFICA: ecco il codice per la seconda pagina.

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

Qualsiasi aiuto è molto apprezzato.

+0

c'è qualche recyclerView nel terzo frammento? [EDITED] scusate, è lì. Puoi pubblicare il codice del tuo secondo frammento –

+0

La prima pagina è un frammento senza alcuna RecyclerView. Tuttavia, sì, il secondo e il terzo sono frammenti composti con un RecyclerView per ciascuno. – Neeeko

+0

Puoi pubblicare il tuo metodo 'onBindViewHolder()' dell'adattatore? – Rami

risposta

18

Ho questo errore durante uno dei miei sviluppi. Hai controllato che il tuo RecyclerView nei tuoi file XML sia correttamente inserito in un altro layout come FrameLayout?

In caso contrario, si bloccherà solo su un Viewpager e non su una vista a frammento singolo.

+2

Hai ragione! Il mio layout era composto solo da un RecyclerView, non sapevo che doveva essere avvolto in un altro. Grazie per l'aiuto ! – Neeeko

+0

Bingo; grazie mille, Ligol. –

+0

GRAZIE SOOOO MOLTO !! –

-2

Cambio:

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

a:

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

variabile finale non avrà alcun valore, anche se si tenta di aggiungere loro.

+0

Ho provato la soluzione e ho ancora il NPE. :/ – Neeeko

+0

Oh. Quindi, invece di aggiungerne uno alla volta, è possibile inviare l'elenco completo ad Adattatore e aggiungere tutti alla lista in adattatore. –

+3

Non è così che funzionano le "variabili finali". Puoi aggiungere e rimuovere elementi dall'elenco anche se è dichiarato definitivo. –

4

Questo accade quando si aggiungono accidentalmente viste direttamente a RecyclerView. Nel mio caso, ho utilizzato View.inflate per un layout di decoratore con RecyclerView come parametro padre, che lo allega automaticamente. RecyclerView itera su qualsiasi figlio ad esso collegato e si aspetta che tutti i suoi figli vedano avere ViewHolders nei parametri di layout e getterà questo NPE quando il titolare della vista di un bambino è nullo.

0

Questo accade quando si aggiungono elementi direttamente sotto listView o RecyclerView nel vostro layout di XML 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> 

Qui ho aggiunto un TextView dentro RecyclerView che mi getterà onLayout error (causata da NullPointerException) .È non dovrebbe aggiungere elementi direttamente sotto RecyclerView o listView.

0

Aggiunta senza figli nella vista riciclatore e l'impostazione attachToRoot, il terzo parametro di inflate() metodo per false mentre gonfiare il layout personalizzato ha lavorato per me.

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

Disposizione:

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