Java 为什么我在我的 RecyclerView 上得到一个空引用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26702633/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 03:06:34  来源:igfitidea点击:

Why am I getting a null reference on my RecyclerView

javaandroidandroid-fragments

提问by Andrew Butler

I am trying to use the RecyclerView in my fragments. It shows up fine for the first tab, but when I swipe to the second tab and then back to the first one I get the following error:

我正在尝试在我的片段中使用 RecyclerView。第一个选项卡显示正常,但是当我滑动到第二个选项卡然后返回到第一个选项卡时,出现以下错误:

java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v7.widget.RecyclerView$LayoutManager.stopSmoothScroller()' on a null object reference

java.lang.NullPointerException:尝试在空对象引用上调用虚拟方法“void android.support.v7.widget.RecyclerView$LayoutManager.stopSmoothScroller()”

Does anyone know why I would be getting this error? It seems that there is a call that I'm missing before I the fragments switch, but I can't figure it out.

有谁知道为什么我会收到这个错误?似乎在片段切换之前我错过了一个电话,但我无法弄清楚。

Pager Adapter for 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 & FragmentTwo use a boilerplate class I created:

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

}

EDIT

编辑

Error Stack Trace:

错误堆栈跟踪:

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

采纳答案by James Lockhart

Basically, the LayoutManager is being disposed of before your recycler has finished with it.

基本上,LayoutManager 在你的回收者完成之前就被处理掉了。

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

The problem stems from when the stopScroll and it tries to call mLayout.stopSmoothScroller(); without checking if mLayout is null.

问题源于何时 stopScroll 并尝试调用 mLayout.stopSmoothScroller(); 不检查 mLayout 是否为空。

I threw togeather a very hacky hot fix for an app I've been working on, but I wouldn't suggest using this for a long term solution as it's very much a bodge. If like me you have a tight deadline it's best to just catch the null pointer exception and ignore it.

我为我一直在开发的应用程序抛出了一个非常hacky的修补程序,但我不建议将其用作长期解决方案,因为它非常麻烦。如果像我一样你的截止日期很紧,最好只捕获空指针异常并忽略它。

My hot fix was just to create a custom View extending the RecyclerView :

我的热修复只是创建一个扩展 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.
             */
        }
    }
}

Then change all references from the RecyclerView to HotFixRecyclerView. If you do use it please remove it once Android have patched this issue as it's a bit of a hack.

然后将所有引用从 RecyclerView 更改为 HotFixRecyclerView。如果您确实使用它,请在 Android 修补此问题后将其删除,因为它有点像黑客。

  • If you use recylerview inn XML don't forget to change your XML files accordingly to use com.your.package.HotFixRecyclerViewinstead of android.support.v7.widget.RecyclerView
  • 如果您使用 recylerview inn XML,请不要忘记相应地更改您的 XML 文件以使用com.your.package.HotFixRecyclerView而不是android.support.v7.widget.RecyclerView

回答by Felizardo

Reading the bug posted by Isaak, the best solution seems to be assigning the Layout Manager as soon as the view is inflated.

阅读 Isaak 发布的错误,最好的解决方案似乎是在视图膨胀后立即分配布局管理器。

In my case, I only had to assign it on onCreate to fix the problem:

就我而言,我只需将它分配给 onCreate 即可解决问题:

mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);

回答by Juan José Melero Gómez

It seems that If you have a RecyclerView in your layout and you load it in the Activity, it is a must to set a LayoutManager for it, else it will throw this Exception when trying to destroy it. I have just experienced this error and this is the way I've solved it:

似乎如果您的布局中有一个 RecyclerView 并且您将它加载到 Activity 中,则必须为其设置一个 LayoutManager ,否则它会在尝试销毁它时抛出此异常。我刚刚遇到了这个错误,这是我解决它的方法:

My activity showed elements in a pair of TextViews or in a RecyclerView depending on the amount of items: if it was a collection of items, I used the RecyclerView; if there was only one item, it was displayed on the TextViews and I set the RecyclerView's visibility to GONE.

我的活动根据项目的数量在一对 TextViews 或 RecyclerView 中显示元素:如果它是一个项目集合,我使用 RecyclerView;如果只有一项,它会显示在 TextViews 上,我将 RecyclerView 的可见性设置为GONE

Then, in the first case, I called myRecyclerView.setLayoutManager(myLayoutManager), but in the other case I didn't, for I wasn't going to use it. In both cases, the Activity containing the RecyclerView was displayed perfectly. But when closing it, the Exception was thrown in the second case because the RecyclerView, though not used, existed and when trying to dispose of it, it seems it couldn't. So I just assigned the LayoutManager in both cases although I wasn't using it, and this solved the issue.

然后,在第一种情况下,我调用了myRecyclerView.setLayoutManager(myLayoutManager),但在另一种情况下我没有调用,因为我不会使用它。在这两种情况下,包含 RecyclerView 的 Activity 都显示完美。但是当关闭它时,在第二种情况下抛出异常,因为 RecyclerView 虽然没有使用,但存在,当试图处理它时,它似乎不能。所以我只是在两种情况下都分配了 LayoutManager,尽管我没有使用它,这解决了这个问题。

Here's the code from my Activity:

这是我的活动中的代码:

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

回答by huynq0911

Because it has already inflated pref.xml . You should remove this:

因为它已经膨胀了 pref.xml 。你应该删除这个:

    @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    mRootView = inflater.inflate(R.layout.xxx, container, false);
    return mRootView;
}

回答by Mihai

In my case i had notifyDataSetChanged()on the wrong adapter.

就我而言,我notifyDataSetChanged()使用了错误的适配器。