Почему я получаю нулевую ссылку на мой RecyclerView
Я пытаюсь использовать RecyclerView в своих фрагментах. Он отображается отлично для первой вкладки, но когда я прокручиваю вторую вкладку, а затем возвращаюсь к первой, я получаю следующую ошибку:
java.lang.NullPointerException: попытка вызвать виртуальный метод 'void android.support.v7.widget.RecyclerView $LayoutManager.stopSmoothScroller()" по ссылке нулевого объекта
Кто-нибудь знает, почему я получаю эту ошибку? Кажется, что есть вызов, который мне не хватает, прежде чем я перейду фрагменты, но я не могу понять.
Адаптер пейджера для фрагментов:
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 и FragmentTwo используйте класс, который я создал:
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();
}
ИЗМЕНИТЬ
Трассировка стека ошибок:
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)
Ответы
Ответ 1
В основном, ваш LayoutManager удаляется, прежде чем ваш ресайклир закончит с ним.
Из источника Android:
@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);
}
Проблема заключается в том, что stopScroll пытается вызвать mLayout.stopSmoothScroller(); без проверки, если значение mlayout равно нулю. Я бросил togeather очень взломанное горячее исправление для приложения, над которым я работал, но НЕ должен использоваться как долгосрочное решение, поскольку это очень хак, но у меня был ограниченный срок, поэтому мне просто пришлось поймать исключение нулевого указателя, Я вырезал обработчик, потому что он был специфичным для приложения.
Мое горячее решение состояло только в том, чтобы создать пользовательский вид, расширяющий 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.
*/
}
}
}
Затем измените все ссылки на RecyclerView на мой собственный вид. Если вы его используете, удалите его, как только Android запустит эту проблему, поскольку это немного взломает.
- Если вы используете recylerview на XML, не забудьте изменить XML файл в соответствии с
com.your.package.HotFixRecyclerView
вместо android.support.v7.widget.RecyclerView
Ответ 2
Кажется, что если у вас есть RecyclerView в вашем макете и вы загружаете его в Activity, необходимо установить для него LayoutManager, иначе он выкинет это Exception при попытке его уничтожить. Я только что испытал эту ошибку, и именно так я ее решил:
Моя активность показала элементы в паре TextView или в RecyclerView в зависимости от количества элементов: если это был набор элементов, я использовал RecyclerView; если был только один элемент, он был отображен в TextViews, и я установил видимость RecyclerView на GONE
.
Тогда в первом случае я назвал myRecyclerView.setLayoutManager(myLayoutManager)
, но в другом случае я этого не сделал, потому что я не собирался его использовать. В обоих случаях активность, содержащая RecyclerView, была отлично отображена. Но при его закрытии исключение было выбрано во втором случае, потому что RecyclerView, хотя и не использовался, существовал и, пытаясь избавиться от него, похоже, не может. Поэтому я просто назначил LayoutManager в обоих случаях, хотя я не использовал его, и это решило проблему.
Здесь код из моей деятельности:
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);
}
Ответ 3
Потому что он уже раздул pref.xml. Вы должны удалить это:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mRootView = inflater.inflate(R.layout.xxx, container, false);
return mRootView;
}
Ответ 4
Считая ошибку, опубликованную Исааком, лучшим решением, похоже, является назначение Менеджера макетов, как только изображение будет завышено.
В моем случае мне пришлось назначить его onCreate для устранения проблемы:
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);