销毁从ViewPager的适配器项目后,屏幕方向改变 [英] Destroy item from the ViewPager's adapter after screen orientation changed

查看:299
本文介绍了销毁从ViewPager的适配器项目后,屏幕方向改变的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我在遇到破坏(删除)一个来自 ViewPager 页屏幕方向更改后的一个问题。我会尽力说明问题,在下面几行。

So I'm having a problem with destroying (removing) one page from the ViewPager after the screen orientation changed. I'll try to describe the problem in the following lines.

我使用了 FragmentStatePagerAdapter 为的适配器 ViewPager 和一个小的接口,它描述了如何无尽查看传呼机应该工作。它背后的想法是,你可以滚动到右侧,直到你到达 ViewPager 的结束。如果你可以从一个API调用加载更多的结果,将显示一个进度页面,直到结果出来。

I'm using the FragmentStatePagerAdapter for the adapter of the ViewPager and a small interface which describes how an endless view pager should work. The idea behind of it is that you can scroll to the right till you reach the end of the ViewPager. If you can load more results from an API call, a progress page is displayed till the results come.

一切正常,直到这里,现在问题就来了。如果在此加载过程中,我旋转屏幕(这不会影响到API调用这基本上是一个的AsyncTask ),调用返回时,应用程序崩溃给我这个异常

Everything fine till here, now the problem comes. If during this loading process, I rotate the screen (this will not affect the API call which is basically an AsyncTask), when the call returns, the app crashes giving me this exception:

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)

挖库的code位后,它似乎片段的 MINDEX 数据字段小于 0 在这种情况下,这引起了异常。

After digging a bit in the code of the library it seems that the mIndex data field of the fragment is less than 0 in this case, and this raises that exception.

下面是寻呼机适配器的code:

Here is the code of the pager adapter:

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调用:

The onPageSelected() callback below, which basically starts the api call if needed:

 @Override
    public void onPageSelected(int currentPosition) {
        updatePagerIndicator(currentPosition);
        activity.invalidateOptionsMenu();
        if (requestNextApiPage(currentPosition)) {
            pagerAdapter.setLoadingData(true);
            requestNextPageController.requestNextPageOfData(this);
        }

现在,还值得说的交付之后的结果是什么API调用一样。这里是回调:

Now, it is also worth to say what the API call does after delivering the results. Here is the callback:

@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()回调将活动重新启动后调用。

Ok, now because the setData() method invokes the notifiyDataSetChanged(), this will call the getItemPosition() for the fragments that are currently in the currentFragments array. Of course that for the progress element it returns POSITION_NONE since I want to delete this page, so this basically invokes the destroyItem() callback from the PagedSingleDataAdapter. If I don't rotate the screen, everything works OK, but as I said if I'm rotating it when the progress element is displayed and the API call hasn't finished yet, the destroyItem() callback will be invoked after the activity is restarted.

也许我还应该说,我在主持另一个片段,而不是在一个活动的 ViewPager ,所以 OutterFragment 托管 ViewPager 。我实例化 pagerAdapter 中的 onActivityCreated()回调 OutterFragment 和使用 setRetainInstance(真),这样,当屏幕旋转 pagerAdapter 保持不变(无应该改变,右),code在这里?

Maybe I should also say that I'm hosting the ViewPager in another Fragment and not in an activity, so the OutterFragment hosts the ViewPager. I'm instantiating the pagerAdapter in the onActivityCreated() callback of the OutterFragment and using the setRetainInstance(true) so that when the screen rotates the pagerAdapter remains the same (nothing should be changed, right?), code here:

if (pagerAdapter == null) {
    pagerAdapter = new PagedSingleDataAdapter(this, data);
}
pager.setAdapter(pagerAdapter);

if (savedInstanceState == null) {
    pager.setOnPageChangeListener(this);
    pager.setCurrentItem(currentPosition);
}

总结现在,在问题是:

如果我试图从删除的进步元素 ViewPager 它被实例化后的活性被破坏并重新创建(屏幕方向改变)我得到了上面的异常(该 pagerAdapter 是一样的,所以一切内部也保持不变,参考文献等...因为 OutterFragment 它承载 pagerAdapter 不被破坏,仅从活动分离,然后重新连接)。也许它发生一些与片段经理,但我真的不知道是什么。

If I try to remove the progress element from the ViewPager after it was instantiated and the activity was destroyed and recreated (screen orientation changed) I get the above exception (the pagerAdapter remains the same, so everything inside of it also remains the same, references etc… since the OutterFragment which hosts the pagerAdapter is not destroyed is only detached from the activity and then re-attached). Probably it happens something with the fragment manager, but I really don't know what.

我已经尝试过什么:

  1. 尝试使用IE浏览器的 onTaskSuccess()回调,我试图从片段经理删除片段,另一种技术,以消除我的进步片段,也没的工作。

  1. Trying to remove my progress fragment using another technique i.e on the onTaskSuccess() callback I was trying to remove the fragment from the fragment manager, didn't work.

我也试图隐藏完全从片段经理删除它的进步元素代替。这工作50%,因为该观点是不存在了,但我有一个空白页面,所以这不是真的,我在寻找什么。

I also tried to hide the progress element instead of removing it completely from the fragment manager. This worked 50%, because the view was not there anymore, but I was having an empty page, so that's not really what I'm looking for.

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

I also tried to (re)attach the progressFragment to the fragment manager after the screen orientation changes, this also didn't work.

我也试着删除,然后重新添加进度片段的片段管理器中的活性重建后,没有工作。

I also tried to remove and then add again the progress fragment to the fragment manager after the activity was recreated, didn't work.

试着拨打 destroyItem()手动从 onTaskSuccess()回调(这是真的,真难看),但没有奏效。

Tried to call the destroyItem() manually from the onTaskSuccess() callback (which is really, really ugly) but didn't work.

对不起你们这么长的帖子,但我想是最好的,我可以让你们可以把它理解来解释这个问题。

Sorry guys for such a long post, but I was trying to explain the problem as best as I can so that you guys can understand it.

任何解决方案,建议是多AP preciated。

Any solution, recommendation is much appreciated.

谢谢!

更新:溶液中发现的 好了,这花了一段时间。问题是,destroyItem()回调被调用两次的进展片段,一旦当屏幕方向改变了,然后再次经过API调用完成。这就是为什么例外。我找到了解决办法如下: 保持跟踪,如果API调用完成与否和销毁进度片段只是在这种情况下,低于code。

UPDATE: SOLUTION FOUND OK, so this took a while. The problem was that the destroyItem() callback was called twice on the progress fragment, once when the screen orientation changed and then once again after the api call finished. That's why the exception. The solution that I found is the following: Keep tracking if the api call finished or not and destroy the progress fragment just in this case, code below.

@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设置为false在适配器的构造和 onTaskSuccess()回调为true。 它真的有效!

and then this apiCallFinished is set to false in the constructor of the adapter and to true in the onTaskSuccess() callback. And it really works!

推荐答案

更新:溶液中发现的确定,所以这花了一段时间。问题是,destroyItem()回调被调用两次的进展片段,一旦当屏幕方向改变了,然后再次经过API调用完成。这就是为什么例外。我找到了解决办法如下:保持跟踪,如果API调用完成与否和销毁进度片段只是在这种情况下,低于code

UPDATE: SOLUTION FOUND OK, so this took a while. The problem was that the destroyItem() callback was called twice on the progress fragment, once when the screen orientation changed and then once again after the api call finished. That's why the exception. The solution that I found is the following: Keep tracking if the api call finished or not and destroy the progress fragment just in this case, code below.

@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设置为false在适配器的构造和真实的onTaskSuccess()回调。它真的有效!

and then this apiCallFinished is set to false in the constructor of the adapter and to true in the onTaskSuccess() callback. And it really works!

这篇关于销毁从ViewPager的适配器项目后,屏幕方向改变的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆