LayoutManager.canScrollVertically() 时出现空指针异常

Null pointer exception when LayoutManager.canScrollVertically()

我正在尝试在我的应用程序中包含一个 SwipeRefreshLayout。它可以工作,但是当我关闭我的 WIFI 时(我的应用程序使用它,如果没有 wifi,我想包括警报)。任何人有任何建议为什么我得到这个空指针异常:

NullPointerException: Attempt to invoke virtual method 'boolean android.support.v7.widget.RecyclerView$LayoutManager.canScrollVertically()' on a null object reference
                                                                       at android.support.v7.widget.RecyclerView.computeVerticalScrollOffset(RecyclerView.java:1540)
                                                                       at android.view.View.canScrollVertically(View.java:13757)
                                                                       at android.support.v4.view.ViewCompatICS.canScrollVertically(ViewCompatICS.java:35)
                                                                       at android.support.v4.view.ViewCompat$ICSViewCompatImpl.canScrollVertically(ViewCompat.java:1161)
                                                                       at android.support.v4.view.ViewCompat.canScrollVertically(ViewCompat.java:1575)
                                                                       at android.support.v4.widget.SwipeRefreshLayout.canChildScrollUp(SwipeRefreshLayout.java:643)
                                                                       at android.support.v4.widget.SwipeRefreshLayout.onInterceptTouchEvent(SwipeRefreshLayout.java:657)
                                                                       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2108)
                                                                       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2553)
                                                                       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2197)
                                                                       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2553)
                                                                       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2197)

我必须指出,在我的应用程序中添加 SwipeFredreshLayput 之前,我没有遇到这样的问题。

这是我的XML: Hourly_Fragment:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:tools="http://schemas.android.com/tools"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:paddingLeft="@dimen/activity_horizontal_margin"
            android:paddingRight="@dimen/activity_horizontal_margin"
            android:paddingTop="@dimen/activity_vertical_margin"
            android:paddingBottom="@dimen/activity_vertical_margin"
            android:background="@drawable/bg_gradient"
            tools:context="koemdzhiev.com.stormy.ui.HourlyForecastActivity">

<android.support.v4.widget.SwipeRefreshLayout
    android:id="@+id/hourly_swipe_refresh_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

        <android.support.v7.widget.RecyclerView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_centerHorizontal="true"
            android:id="@+id/recyclerView"/>

</android.support.v4.widget.SwipeRefreshLayout>

</RelativeLayout>

这是我的 Hourly_fragment java 代码:

public class Hourly_forecast_fragment extends Fragment {
private Hour[] mHours;
private MainActivity mActivity;
@InjectView(R.id.hourly_swipe_refresh_layout)
SwipeRefreshLayout mSwipeRefreshLayout;
RecyclerView.LayoutManager layoutManager;
//inject the RecyclerView as member variable
@InjectView(R.id.recyclerView)
RecyclerView mRecyclerView;

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mActivity = ((MainActivity) getActivity());


}

@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View v =inflater.inflate(R.layout.hourly_forecast_fragment,container,false);
    ButterKnife.inject(this, v);
    mSwipeRefreshLayout.setColorSchemeColors(R.color.orange,R.color.green,R.color.blue);
    if (mRecyclerView != null)
        mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                if (mActivity.isNetworkAvailable()) {
                    mActivity.getLocation();
                } else {
                    mActivity.alertForNoInternet();
                    mSwipeRefreshLayout.setRefreshing(false);
                }
            }
        });
    Log.e("Forecast_fragment", "onCreateView");
    return v;
}

public void setUpHourlyFragment(){
    if (mActivity.mForecast != null) {

        Hour[] hourlyForecast = mActivity.mForecast.getHourlyForecast();
        mHours = Arrays.copyOf(hourlyForecast, hourlyForecast.length, Hour[].class);

        HourAdapter adapter = new HourAdapter(mActivity, mHours);
        mRecyclerView.setAdapter(adapter);
        mRecyclerView.setHasFixedSize(true);
        layoutManager = new LinearLayoutManager(mActivity);
        mRecyclerView.setLayoutManager(layoutManager);
        //if dealing with fixed size data, it is recommended to do the following...
        mRecyclerView.setHasFixedSize(true);
    }
}
}

主要activity代码:

public class MainActivity extends AppCompatActivity {
ViewPager pager;
ViewPagerAdapter adapter;
SlidingTabLayout tabs;
CharSequence Titles[]={"Current","Hourly","Daily"};
int Numboftabs =3;
Current_forecast_fragment mCurrent_forecast_fragment;
Hourly_forecast_fragment mHourly_forecast_fragment;
Daily_forecast_fragment mDaily_forecast_fragment;

public static final String TAG = MainActivity.class.getSimpleName();
public static final String LOCATION_KEY = "location_key";
public Forecast mForecast;
public static final String DAILY_FORECAST = "DAILY_FORECAST";
public static final String HOURLY_FORECAST = "HOURLY_FORECAST";
//default coordinates - Gotse Delchev, UK Lati:57.156866 ; Long:
private double latitude = 41.5667;
private double longitude = 23.7333;
private LocationManager locationManager;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //-----------MY CODE STARTS HERE-----------------
    changeWindowTopColor();
    this.mCurrent_forecast_fragment = new Current_forecast_fragment();
    this.mHourly_forecast_fragment = new Hourly_forecast_fragment();
    this.mDaily_forecast_fragment = new Daily_forecast_fragment();
    getLocation();

    // Creating The ViewPagerAdapter and Passing Fragment Manager, Titles fot the Tabs and Number Of Tabs.
    adapter =  new ViewPagerAdapter(getSupportFragmentManager(),Titles,Numboftabs,mCurrent_forecast_fragment,
            mHourly_forecast_fragment,mDaily_forecast_fragment);

    // Assigning ViewPager View and setting the adapter
    pager = (ViewPager) findViewById(R.id.pager);
    pager.setOffscreenPageLimit(3);
    pager.setAdapter(adapter);

    // Assiging the Sliding Tab Layout View
    tabs = (SlidingTabLayout) findViewById(R.id.tabs);
    tabs.setDistributeEvenly(true); // To make the Tabs Fixed set this true, This makes the tabs Space Evenly in Available width

    // Setting Custom Color for the Scroll bar indicator of the Tab View
    tabs.setCustomTabColorizer(new SlidingTabLayout.TabColorizer() {
        @Override
        public int getIndicatorColor(int position) {
            return ContextCompat.getColor(MainActivity.this,R.color.tabsScrollColor);
        }
    });

    // Setting the ViewPager For the SlidingTabsLayout
    tabs.setViewPager(pager);


}
 private void getForecast(double latitude, double longitude) {
if (isNetworkAvailable()) {


        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url(forecast)
                .build();

        Call call = client.newCall(request);

        call.enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mCurrent_forecast_fragment.toggleRefresh();
                        mHourly_forecast_fragment.mSwipeRefreshLayout.setRefreshing(false);
                    }
                });
                alertUserAboutError();
            }

            //when the call to the Okhttp library finishes, than calls this method:
            @Override
            public void onResponse(Response response) throws IOException {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mCurrent_forecast_fragment.toggleRefresh();
                        mHourly_forecast_fragment.mSwipeRefreshLayout.setRefreshing(false);
                    }
                });
                try {
                    String jsonData = response.body().string();
                    //Log.v(TAG, jsonData);
                    if (response.isSuccessful()) {
                        mForecast = parseForecastDetails(jsonData);
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                              mCurrent_forecast_fragment.updateDisplay();
                                mHourly_forecast_fragment.setUpHourlyFragment();
                                mDaily_forecast_fragment.setUpDailyFragment();

                            }
                        });


                    } else {
                        alertUserAboutError();
                    }
                } catch (IOException | JSONException e) {
                    Log.e(TAG, "Exception caught:", e);
                }
            }
        });
    } else {
        mCurrent_forecast_fragment.toggleRefresh();
        mHourly_forecast_fragment.mSwipeRefreshLayout.setRefreshing(false);
      Toast.makeText(this,getString(R.string.network_unavailable_message), Toast.LENGTH_LONG).show();
        alertForNoInternet();
    }
}

public void alertForNoInternet() {
    WIFIDialogFragment dialog = new WIFIDialogFragment();
    dialog.show(getFragmentManager(), getString(R.string.error_dialog_text));
}

输入这些行:

layoutManager = new LinearLayoutManager(mActivity);
mRecyclerView.setLayoutManager(layoutManager);

在 onCreateView 这一行之后:

ButterKnife.inject(this, v);

你通常应该避免在没有附加 LayoutManager 的情况下使用 RecyclerView,它会导致像这样的奇怪异常,因为大多数 RecyclerView 特征,甚至是非绘图 -相关靠LayoutManager。然而,有一个 RecyclerView 没有 Adapter 是完全没问题的(因为你确实已经有了数据或其他什么)。但是您应该在创建 RecyclerView 后尽快附加一个 LayoutManager :-)

如果您使用的是 kotlin,那么最好在执行前检查空值。例如:

val reachBottom = recyclerView?.canScrollVertically(1)
if (reachBottom == false) {
   //scroll view is at bottom
   //your code
}