之后,屏幕方向改变破坏从ViewPager的适配器项目(Destroy item from the

2019-07-21 09:10发布

所以,我有一个问题,破坏(删除)从一个页面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 。 我实例化pagerAdapteronActivityCreated()的回调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不被破坏,只能从活动分离,然后重新连接)。 也许这恰好与该片经理的东西,但我真的不知道是什么。

我已经尝试过:

  1. 尝试使用即在其他技术去除我的进步片段onTaskSuccess()我试图删除片段经理的片段回调,没有工作。

  2. 我也试图隐藏进度元素而不是从片段经理完全去除它的。 这个工作的50%,因为观点不存在了,但我是有一个空白页面,所以这不是真的是我要找的。

  3. 我也试图(重新)附加progressFragment屏幕方向改变后的片段经理,这也没有工作。

  4. 我也试着删除,然后再进步片段添加到片段经理活性重建后,没有工作。

  5. 试着拨打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()回调。 它真的有效!

Answer 1:

更新:发现的解决方案好,所以这花了一段时间。 问题是,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()回调。 它真的有效!



文章来源: Destroy item from the ViewPager's adapter after screen orientation changed