无法设置 ListView 的适配器,因为 ListView 有一个空对象引用
Posted
技术标签:
【中文标题】无法设置 ListView 的适配器,因为 ListView 有一个空对象引用【英文标题】:Can't set adapter of ListView because the ListView has a null object reference 【发布时间】: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 说它无法在空对象引用上设置适配器。
有人知道我该如何解决这个问题吗?
【问题讨论】:
【参考方案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_ android:layout_>
<android.support.design.widget.TabLayout
android:id="@+id/tablayout"
android:layout_
android:layout_
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_
android:layout_>
</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_
android:layout_
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 中,我制作了一个适配器并将其添加到列表视图中。它似乎根本不起作用..以上是关于无法设置 ListView 的适配器,因为 ListView 有一个空对象引用的主要内容,如果未能解决你的问题,请参考以下文章
Android - 通过 getView 函数在自定义 listView 适配器内设置 ImageView 的源无法正常工作