无法设置 ListView 的适配器,因为 ListView 具有空对象引用

Can't set adapter of ListView because the ListView has a null object reference

我正在尝试使用 ViewPager 创建 TabLayout,但每当我离开应用程序(不要关闭它,只需切换到另一个应用程序并返回)时,片段就会被杀死。我不确定,但片段需要重新创建,然后失败。

创建TabLayout、TabLayoutAdapter和ViewPager的代码:

    TabLayout tabLayout = (TabLayout) findViewById(R.id.tablayout);     
    MyTabLayoutAdapter tabLayoutAdapter;
    ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);

    tabLayout.addTab(tabLayout.newTab().setText("-2"));
    tabLayout.addTab(tabLayout.newTab().setText("-1"));
    tabLayout.addTab(tabLayout.newTab().setText("0"));
    tabLayout.addTab(tabLayout.newTab().setText("1"));
    tabLayout.addTab(tabLayout.newTab().setText("2"));

    tabLayoutAdapter = new MyTabLayoutAdapter(getSupportFragmentManager(), tabLayout.getTabCount());
    viewPager.setAdapter(tabLayoutAdapter);
    viewPager.setOffscreenPageLimit(3);
    tabLayout.setupWithViewPager(viewPager);
    viewPager.setCurrentItem(2);

我的TabLayoutAdapter的代码:

public class MyTabLayoutAdapter extends FragmentStatePagerAdapter {
    private MyFragment fragmentOne;
    private MyFragment fragmentTwo;
    private MyFragment fragmentDefault;
    private MyFragment fragmentFour;
    private MyFragment fragmentFive;
    private int numberOfTabs;

    public MyTabLayoutAdapter(FragmentManager fragmentManager, int numberOfTabs) {
        super(fragmentManager);
        fragmentOne = new MyFragment();
        fragmentTwo = new MyFragment();
        fragmentDefault = new MyFragment();
        fragmentFour = new MyFragment();
        fragmentFive = new MyFragment();
        this.numberOfTabs = numberOfTabs;
    }

    @Override
    public MyFragment getItem(int position) {
        switch (position) {
            case 0:
                return fragmentOne;
            case 1:
                return fragmentTwo;
            case 2:
                return fragmentDefault;
            case 3:
                return fragmentFour;
            case 4:
                return fragmentFive;
            default:
                return fragmentDefault;
        }
    }

    @Override
    public int getCount() {
        return numberOfTabs;
    }
}

MyFragment 片段:

public class MyFragment extends Fragment {
    private View currentView;
    private ListView listView;

    public MyFragment() {}

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        currentView = LayoutInflater.from(getContext()).inflate(R.layout.my_fragment_layout, container, false);
        listView = (ListView) currentView.findViewById(R.id.listview);
        return currentView;
    }

    //This line throws a NullPointer when I resume the app
    public void populateListView(MyListAdapter myListAdapter) {
        listView.setAdapter(myListAdapter);
    }
}

每当我打电话给

MyListAdapter myListAdapter = new MyListAdapter(this, R.layout.my_item, justSomeStringArray);
tabLayoutAdapter.getItem(0).populateListView(myListAdapter);

我收到一个 NullPointerException,指出它无法在空对象引用上设置适配器。

有人知道我该如何解决这个问题吗?

onCreateView() 在将片段添加到 FragmentManager 之前不会在片段上调用。如果在将 fragmentOne 添加到 FragmentManager 之后的某个时间调用,您的最终代码片段最多只能工作。否则,该片段的 ListView 将不存在。

一般来说,让代码 在片段 之外 试图操纵小部件 在片段 之内——就像你在这里所做的那样——是一个坏主意。

这是一种使用 AsyncTask 实现 (GetDataForListViewTask) 让 Fragment 实例驱动数据检索的方法。

但是,按照 Commonsware 的建议,GetDataForListViewTask 仅获取数据。它不会设置用于 ListView 的适配器。相反,设置 ListAdapter 是在 Fragment 本身中完成的。

GetDataForListViewTask 使用 EventBus 将数据传回 Fragment 实例。这使 GetDataForListViewTask 不必维护对 ListView、Activity、上下文或任何有风险的东西的引用。

并且,通过不从外部 "reaching in" 到 Fragment 来更改其小部件甚至传递数据,此代码还尝试(非常广泛地)遵循 CommonsWare 的另一条指南:

In general, having code outside of a fragment attempt to manipulate the widgets inside that fragment — as you are doing here — is a bad idea.

无论如何,如果您使用这种与您原来的方法略有不同的方法,您需要修改 GetDataForListViewTask 以便它从您的应用程序或网络等其他地方获取数据。

GetDataForListViewTask.java

import android.os.AsyncTask;
import org.greenrobot.eventbus.EventBus;
import java.util.ArrayList;


class GetDataForListViewTask extends AsyncTask<Void, Void, ArrayList<String>> {

    @Override
    protected ArrayList<String> doInBackground(Void... ignored) {
        // since this method runs on a worker thread, you may
        // replace this code with a network call or a background computation
        // from another part of the app is ready
        ArrayList<String> listItems = new ArrayList<>();
        for (int i=0; i<10; i++) {
            listItems.add("item " + i);
        }

        return listItems;
    }

    @Override
    protected void onPostExecute(ArrayList<String> result) {
        super.onPostExecute(result);

        // post event so that Fragment can use the data to populate its ListView
        EventBus.getDefault().post(new MyFragment.ListViewDataReadyEvent(result));
    }

}

MainActivity.java

import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TabLayout tabLayout = (TabLayout) findViewById(R.id.tablayout);
        ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);

        MyAdapter tabLayoutAdapter = new MyAdapter(getSupportFragmentManager(), 5);
        viewPager.setAdapter(tabLayoutAdapter);
        viewPager.setOffscreenPageLimit(3);
        tabLayout.setupWithViewPager(viewPager);
        viewPager.setCurrentItem(2);
    }

    class MyAdapter extends FragmentStatePagerAdapter {

        private final int count;

        public MyAdapter(FragmentManager fm, int count) {
            super(fm);
            this.count = count;
        }

        @Override
        public MyFragment getItem(int position) {
            return MyFragment.newInstance();
        }

        @Override
        public int getCount() {
            return this.count;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return "Tab " + position;
        }
    }

}

MyFragment.java

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

import java.util.ArrayList;


public class MyFragment extends Fragment {

        private ListView listView;
        private GetDataForListViewTask getDataForListViewTask;

        public static MyFragment newInstance() {
            return new MyFragment();
        }

        @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setRetainInstance(true); // to support use of GetDataForListViewTask
            EventBus.getDefault().register(this);
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            listView = (ListView) LayoutInflater.from(getContext()).inflate(
                    R.layout.my_fragment_layout, container, false);
            getDataForListViewTask = new GetDataForListViewTask();
            getDataForListViewTask.execute();
            return listView;
        }

        @Override
        public void onDestroy() {
            if (getDataForListViewTask != null) {
                getDataForListViewTask.cancel(false);
            }
            EventBus.getDefault().unregister(this);
            super.onDestroy();
        }

        static class ListViewDataReadyEvent {

            ArrayList<String> data;
            ListViewDataReadyEvent(ArrayList<String> data) {
                this.data = data;
            }
        }

        @SuppressWarnings("unused")
        @Subscribe(threadMode = ThreadMode.MAIN)
        public void onMessageEvent(ListViewDataReadyEvent event) {
            if (listView != null) {
                ArrayAdapter<String> listAdapter = new ArrayAdapter<>(listView.getContext(),
                        android.R.layout.simple_list_item_1, event.data.toArray(new String[]{}));
                listView.setAdapter(listAdapter);
            }
        }
    }

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical" android:padding="4dip"
    android:gravity="center_horizontal"
    android:layout_width="match_parent" android:layout_height="match_parent">

    <android.support.design.widget.TabLayout
        android:id="@+id/tablayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:tabMode="fixed"
        app:tabGravity="fill" />

    <android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </android.support.v4.view.ViewPager>

</LinearLayout>

my_fragment_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<ListView android:id="@+id/listview"
    android:layout_alignParentTop="true"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:android="http://schemas.android.com/apk/res/android">
</ListView>

为build.gradle选择的编译语句

compile 'org.greenrobot:eventbus:3.0.0'
compile 'com.android.support:design:25.3.1'
compile 'com.android.support:appcompat-v7:25.3.1'