【问题标题】:Can't set adapter of ListView because the ListView has a null object reference无法设置 ListView 的适配器,因为 ListView 有一个空对象引用
【发布时间】:2017-11-05 10:53:32
【问题描述】:

我正在尝试使用 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 说它无法在空对象引用上设置适配器。

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

【问题讨论】:

    标签: android listview android-fragments android-listfragment onresume


    【解决方案1】:

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

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

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

    而且,通过不从外部“进入”片段来更改其小部件甚至传递数据,此代码还尝试(非常广泛地)遵循 CommonsWare 的另一条指导:

    一般来说,让片段外部的代码尝试操作 该片段中的小部件——正如你在这里所做的那样——是一个坏的 想法。

    无论如何,如果您使用这种方法,这与您原来的方法有点不同,您需要修改 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'
    

    【讨论】:

      【解决方案2】:

      在将片段添加到FragmentManager 之前,不会在片段上调用onCreateView()。您的最终代码 sn-p 充其量只能在将 fragmentOne 添加到 FragmentManager 之后的某个时间调用时才有效。否则,fragment 的ListView 将不存在。

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

      【讨论】:

      • 嗯.. 我在后台有一个 http 请求,当我得到响应时,我尝试创建 ListAdapter 并添加它。奇怪的是,我在 TabLayout 之后调用了 http 请求,一切都设置好了。是否有可能检查片段是否已添加到 FragmentManager?
      • @Jason:“我在后台有一个 http 请求,当我得到响应时,我尝试创建 ListAdapter 并添加它”——这不是网络代码的工作设置ListAdapter。获取数据并将其提供给 UI 是网络代码的工作。在您的情况下,UI 尚未准备好接收数据,但 UI 可以安排保留该数据,直到使用 onCreateView() 调用它以使用数据。
      • 那么你是在告诉我在fragment的构造函数中创建ListAdapter,在onCreate中将adapter添加到listView中?
      • @Jason:“所以你告诉我在片段的构造函数中创建 ListAdapter”——不,因为你还没有访问活动的权限,所以你不能创建一个 @ 987654328@。我告诉您将数据提供给片段(例如,tabLayoutAdapter.getItem(0).pleaseHaveSomeData(whatever);),并让片段在适当的时候创建ListAdapter。这可能是立即的(如果ListView 已经创建),也可能是在onViewCreated() 中(如果ListView 尚未创建)。
      • 每当 http 请求准备好时,我都会调用一个方法来保存内容和上下文。在 onCreateView 中,我制作了一个适配器并将其添加到列表视图中。它似乎根本不起作用..
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-22
      • 2015-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-15
      相关资源
      最近更新 更多