J'essaie d'utiliser le RecyclerView dans mes fragments. Il apparaît bien pour le premier onglet, mais lorsque je glisse vers le deuxième onglet, puis reviens au premier, j'obtiens l'erreur suivante:
Java.lang.NullPointerException: tentative d'appeler la méthode virtuelle 'void Android.support.v7.widget.RecyclerView $ LayoutManager.stopSmoothScroller ()' sur une référence d'objet null
Est-ce que quelqu'un sait pourquoi j'obtiendrais cette erreur? Il semble qu'il y ait un appel qui me manque avant que les fragments ne changent, mais je ne peux pas le comprendre.
Adaptateur de téléavertisseur pour fragments:
public class PagerAdapter extends FragmentPagerAdapter {
private final String[] TITLES = {"Header 0", "Header 1" };
public PagerAdapter(FragmentManager fm){
super(fm);
}
@Override
public CharSequence getPageTitle(int position){
return TITLES[position];
}
@Override
public int getCount() {
return TITLES.length;
}
@Override
public Fragment getItem(int position){
switch(position){
case 0:
return new FragmentOne();
case 1:
return new FragmentTwo();
default:
return new FragmentOne();
}
}
}
FragmentOne et FragmentTwo utilisent une classe passe-partout que j'ai créée:
public class FragmentOne extends Base {
... code for displaying content in the RecyclerView. Just contains my custom Adapter for the RecyclerView ...
}
Base:
public abstract class Base extends Fragment {
public View mView;
public RecyclerView mDataView;
public ProgressBar mProgressBar;
public Context mContext;
private RecyclerView.LayoutManager mLayoutManager;
public Base() { }
@Override
public View onCreateView(LayoutInflater _i, ViewGroup _vg, Bundle savedInstanceBundle){
mView = _i.inflate(R.layout.f_base, null);
mDataView = (RecyclerView) mView.findViewById(R.id.data);
mProgressBar = (ProgressBar)mView.findViewById(R.id.loading);
mLayoutManager = new LinearLayoutManager(mContext);
mDataView.setLayoutManager(mLayoutManager);
mContext = this.getActivity();
return mView;
}
@Override
public void onStart() {
super.onStart();
init();
doWork();
}
public abstract void init();
public abstract void doWork();
}
MODIFIER
Trace de pile d'erreur:
11-02 12:49:44.171 3708-3708/com.nitrox.digitune E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.nitrox.digitune, PID: 3708
Java.lang.NullPointerException: Attempt to invoke virtual method 'void Android.support.v7.widget.RecyclerView$LayoutManager.stopSmoothScroller()' on a null object reference
at Android.support.v7.widget.RecyclerView.stopScrollersInternal(RecyclerView.Java:1159)
at Android.support.v7.widget.RecyclerView.stopScroll(RecyclerView.Java:1151)
at Android.support.v7.widget.RecyclerView.onDetachedFromWindow(RecyclerView.Java:1356)
at Android.view.View.dispatchDetachedFromWindow(View.Java:13441)
at Android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.Java:2837)
at Android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.Java:2834)
at Android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.Java:2834)
at Android.view.ViewGroup.removeViewInternal(ViewGroup.Java:4163)
at Android.view.ViewGroup.removeViewInternal(ViewGroup.Java:4136)
at Android.view.ViewGroup.removeView(ViewGroup.Java:4068)
at Android.support.v4.view.ViewPager.removeView(ViewPager.Java:1326)
at Android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.Java:1045)
at Android.support.v4.app.FragmentManagerImpl.detachFragment(FragmentManager.Java:1280)
at Android.support.v4.app.BackStackRecord.run(BackStackRecord.Java:724)
at Android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.Java:1489)
at Android.support.v4.app.FragmentManagerImpl.executePendingTransactions(FragmentManager.Java:486)
at Android.support.v4.app.FragmentPagerAdapter.finishUpdate(FragmentPagerAdapter.Java:141)
at Android.support.v4.view.ViewPager.populate(ViewPager.Java:1073)
at Android.support.v4.view.ViewPager.populate(ViewPager.Java:919)
at Android.support.v4.view.ViewPager$3.run(ViewPager.Java:249)
at Android.view.Choreographer$CallbackRecord.run(Choreographer.Java:767)
at Android.view.Choreographer.doCallbacks(Choreographer.Java:580)
at Android.view.Choreographer.doFrame(Choreographer.Java:549)
at Android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.Java:753)
at Android.os.Handler.handleCallback(Handler.Java:739)
at Android.os.Handler.dispatchMessage(Handler.Java:95)
at Android.os.Looper.loop(Looper.Java:135)
at Android.app.ActivityThread.main(ActivityThread.Java:5221)
at Java.lang.reflect.Method.invoke(Native Method)
at Java.lang.reflect.Method.invoke(Method.Java:372)
at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:899)
at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:694)
Fondamentalement, votre LayoutManager est éliminé avant que votre recycleur n'en ait fini avec lui.
Depuis la source Android source:
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mItemAnimator != null) {
mItemAnimator.endAnimations();
}
mFirstLayoutComplete = false;
stopScroll();
mIsAttached = false;
if (mLayout != null) {
mLayout.onDetachedFromWindow(this, mRecycler);
}
removeCallbacks(mItemAnimatorRunner);
}
Le problème est que stopScroll essaie d'appeler mLayout.stopSmoothScroller (); sans vérifier si mLayout est nul. J'ai lancé un correctif très hacky pour une application sur laquelle je travaille, mais ne devrait PAS être utilisé comme une solution à long terme, car il s'agit en fait d'un hack, mais j'avais un délai serré, j'ai donc dû attraper l'exception de pointeur nul . J'ai supprimé mon gestionnaire car il était spécifique à l'application.
Mon correctif consistait simplement à créer une vue personnalisée étendant RecyclerView:
import Android.content.Context;
import Android.support.v7.widget.RecyclerView;
import Android.util.AttributeSet;
public class HotFixRecyclerView extends RecyclerView
{
public HotFixRecyclerView( Context context )
{
super( context );
}
public HotFixRecyclerView( Context context, AttributeSet attrs )
{
super( context, attrs );
}
public HotFixRecyclerView( Context context, AttributeSet attrs, int defStyle )
{
super( context, attrs, defStyle );
}
@Override
public void stopScroll()
{
try
{
super.stopScroll();
}
catch( NullPointerException exception )
{
/**
* The mLayout has been disposed of before the
* RecyclerView and this stops the application
* from crashing.
*/
}
}
}
Ensuite, changez toutes les références à RecyclerView à ma propre vue. Si vous l'utilisez, veuillez le supprimer une fois Android a corrigé ce problème car c'est un peu un hack.
com.your.package.HotFixRecyclerView
au lieu de Android.support.v7.widget.RecyclerView
Il semble que si vous avez un RecyclerView dans votre mise en page et que vous le chargez dans l'activité, il est indispensable de définir un LayoutManager pour cela, sinon il lèvera cette exception en essayant de le détruire. Je viens de rencontrer cette erreur et voici comment je l'ai résolue:
Mon activité a montré des éléments dans une paire de TextViews ou dans un RecyclerView en fonction de la quantité d'articles: s'il s'agissait d'une collection d'articles, j'ai utilisé le RecyclerView; s'il n'y avait qu'un seul élément, il était affiché sur les TextViews et j'ai défini la visibilité de RecyclerView sur GONE
.
Ensuite, dans le premier cas, j'ai appelé myRecyclerView.setLayoutManager(myLayoutManager)
, mais dans l'autre cas je ne l'ai pas fait, car je n'allais pas l'utiliser. Dans les deux cas, l'activité contenant le RecyclerView était parfaitement affichée. Mais lors de sa fermeture, l'exception a été levée dans le deuxième cas parce que le RecyclerView, bien que non utilisé, existait et lors de la tentative d'élimination, il semble qu'il ne pouvait pas. Je viens donc d'affecter le LayoutManager dans les deux cas, même si je ne l'utilisais pas, et cela a résolu le problème.
Voici le code de mon activité:
ItemsAdapter adapter;
RecyclerView myRecyclerView = findViewById(R.id.my_recycler_view);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);
if (items.size() > 1){
adapter = new ItemsAdapter(this, R.layout.item_layout, items);
myRecyclerView.setLayoutManager(layoutManager);
myRecyclerView.setAdapter(adapter);
} else {
tv_field1.setText(items.get(0).getField1());
tv_field2.setText(items,get(0).getField2());
myRecyclerView.setVisibility(View.GONE);
//This is what I had to add
myRecyclerView.setLayoutManager(layoutManager);
}
Parce qu'il a déjà gonflé pref.xml. Vous devez supprimer ceci:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mRootView = inflater.inflate(R.layout.xxx, container, false);
return mRootView;
}
En lisant le bug signalé par Isaak, la meilleure solution semble être d'assigner le Layout Manager dès que la vue est gonflée.
Dans mon cas, je n'avais qu'à l'assigner sur onCreate pour résoudre le problème:
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);