所以,我有一个问题,破坏(删除)从一个页面ViewPager
屏幕方向改变之后。 我会尽力来形容以下行的问题。
我使用的是FragmentStatePagerAdapter
为的适配器ViewPager
并描述了无尽的观点寻呼机应该如何工作的一个小接口。 它背后的想法是,直到你到达结束时,您可以滚动到右侧ViewPager
。 如果你可以从一个API调用加载更多结果,直到结果出来显示进度页面。
一切都精细到这里,现在问题就来了。 如果在此加载过程中,我旋转屏幕(这不会影响API调用它基本上是一个AsyncTask
),当调用返回时,应用程序崩溃给我这样的例外:
E/AndroidRuntime(13471): java.lang.IllegalStateException: Fragment ProgressFragment{42b08548} is not currently in the FragmentManager
E/AndroidRuntime(13471): at android.support.v4.app.FragmentManagerImpl.saveFragmentInstanceState(FragmentManager.java:573)
E/AndroidRuntime(13471): at android.support.v4.app.FragmentStatePagerAdapter.destroyItem(FragmentStatePagerAdapter.java:136)
E/AndroidRuntime(13471): at mypackage.OutterFragment$PagedSingleDataAdapter.destroyItem(OutterFragment.java:609)
在库中的代码挖了一下之后似乎mIndex
片段的数据字段小于0
在这种情况下,这引发该异常。
这里是寻呼机适配器的代码:
static class PagedSingleDataAdapter extends FragmentStatePagerAdapter implements
IEndlessPagerAdapter {
private WeakReference<OutterFragment> fragment;
private List<DataItem> data;
private SparseArray<WeakReference<Fragment>> currentFragments = new SparseArray<WeakReference<Fragment>>();
private ProgressFragment progressElement;
private boolean isLoadingData;
public PagedSingleDataAdapter(SherlockFragment fragment, List<DataItem> data) {
super(fragment.getChildFragmentManager());
this.fragment = new WeakReference<OutterFragment>(
(OutterFragment) fragment);
this.data = data;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Object item = super.instantiateItem(container, position);
currentFragments.append(position, new WeakReference<Fragment>(
(Fragment) item));
return item;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
currentFragments.put(position, null);
super.destroyItem(container, position, object);
}
@Override
public Fragment getItem(int position) {
if (isPositionOfProgressElement(position)) {
return getProgessElement();
}
WeakReference<Fragment> fragmentRef = currentFragments.get(position);
if (fragmentRef == null) {
return PageFragment.newInstance(args); // here I'm putting some info
// in the args, just deleted
// them now, not important
}
return fragmentRef.get();
}
@Override
public int getCount() {
int size = data.size();
return isLoadingData ? ++size : size;
}
@Override
public int getItemPosition(Object item) {
if (item.equals(progressElement) && !isLoadingData) {
return PagerAdapter.POSITION_NONE;
}
return PagerAdapter.POSITION_UNCHANGED;
}
public void setData(List<DataItem> data) {
this.data = data;
notifyDataSetChanged();
}
@Override
public boolean isPositionOfProgressElement(int position) {
return isLoadingData && position == data.size();
}
@Override
public void setLoadingData(boolean isLoadingData) {
this.isLoadingData = isLoadingData;
}
@Override
public boolean isLoadingData() {
return isLoadingData;
}
@Override
public Fragment getProgessElement() {
if (progressElement == null) {
progressElement = new ProgressFragment();
}
return progressElement;
}
public static class ProgressFragment extends SherlockFragment {
public ProgressFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
TextView progressView = new TextView(container.getContext());
progressView.setGravity(Gravity.CENTER_HORIZONTAL
| Gravity.CENTER_VERTICAL);
progressView.setText(R.string.loading_more_data);
LayoutParams params = new LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.FILL_PARENT);
progressView.setLayoutParams(params);
return progressView;
}
}
}
在onPageSelected()
回调下方,如果需要的话,基本上开始API调用:
@Override
public void onPageSelected(int currentPosition) {
updatePagerIndicator(currentPosition);
activity.invalidateOptionsMenu();
if (requestNextApiPage(currentPosition)) {
pagerAdapter.setLoadingData(true);
requestNextPageController.requestNextPageOfData(this);
}
现在,它也值得说交付后的结果API调用做什么。 这里是回调:
@Override
public boolean onTaskSuccess(Context arg0, List<DataItem> result) {
data = result;
pagerAdapter.setLoadingData(false);
pagerAdapter.setData(result);
activity.invalidateOptionsMenu();
return true;
}
好了,现在因为setData()
方法调用notifiyDataSetChanged()
这将调用getItemPosition()
对于目前中相应的片段currentFragments
阵列。 当然,对于进度元素返回POSITION_NONE
,因为我要删除该页面,所以这基本上调用destroyItem()
从回调PagedSingleDataAdapter
。 如果我不旋转屏幕,一切工作正常,但正如我所说,如果我转动它时,会显示进度元素和API调用尚未完成,该destroyItem()
回调将在活动之后调用重新启动。
也许我也应该说我主持ViewPager
在另一个片段,而不是一个活动,所以OutterFragment
承载ViewPager
。 我实例化pagerAdapter
在onActivityCreated()
的回调OutterFragment
并使用setRetainInstance(true)
,这样,当屏幕旋转pagerAdapter
保持不变(什么都不改变,对吧?),这里的代码:
if (pagerAdapter == null) {
pagerAdapter = new PagedSingleDataAdapter(this, data);
}
pager.setAdapter(pagerAdapter);
if (savedInstanceState == null) {
pager.setOnPageChangeListener(this);
pager.setCurrentItem(currentPosition);
}
现在总结, 问题是:
如果我尝试从删除进度元素ViewPager
它被实例化和活性被破坏和重建后(屏幕方向改变),我得到了上面的异常(该pagerAdapter
保持不变,所以它里面的一切也保持不变,引用等等......因为OutterFragment
它承载pagerAdapter
不被破坏,只能从活动分离,然后重新连接)。 也许这恰好与该片经理的东西,但我真的不知道是什么。
我已经尝试过:
尝试使用即在其他技术去除我的进步片段
onTaskSuccess()
我试图删除片段经理的片段回调,没有工作。我也试图隐藏进度元素而不是从片段经理完全去除它的。 这个工作的50%,因为观点不存在了,但我是有一个空白页面,所以这不是真的是我要找的。
我也试图(重新)附加
progressFragment
屏幕方向改变后的片段经理,这也没有工作。我也试着删除,然后再进步片段添加到片段经理活性重建后,没有工作。
试着拨打
destroyItem()
从手动onTaskSuccess()
回调(这是真的,真的很丑),但没有奏效。
对不起你们这么长的帖子,但我想是最好的,我可以让你们可以了解它说明问题。
任何解决方案,建议是非常赞赏。
谢谢!
更新:发现的解决方案好,所以这花了一段时间。 问题是,destroyItem()回调是在进步片段叫了两次,一次是当屏幕方向改变,然后再次后的API调用完成。 这就是为什么例外。 我找到了解决办法如下:保持跟踪,如果API调用完成或不和销毁进度片段只是在这种情况下,下面的代码。
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
if (object.equals(progressElement) && apiCallFinished == true) {
apiCallFinished = false;
currentFragments.put(position, currentFragments.get(position + 1));
super.destroyItem(container, position, object);
} else if (!(object.equals(progressElement))) {
currentFragments.put(position, null);
super.destroyItem(container, position, object);
}
}
然后这个apiCallFinished被设置在适配器的构造虚假和为true的onTaskSuccess()
回调。 它真的有效!