我正在尝试使用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,说它无法在空对象引用上设置适配器。
任何人都知道如何解决这个问题?
答案 0 :(得分:2)
这是一种让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'
答案 1 :(得分:1)
onCreateView()
之前,不会对片段调用 FragmentManager
。如果在将fragmentOne
添加到FragmentManager
后的某个时间调用,则最终的代码段最多只会有效。否则,片段的ListView
将不存在。
一般来说,让代码在之外的片段试图操纵里面的小部件那个片段 - 正如你在这里所做的那样 - 是个坏主意。