24

画面の向きを変更した後でViewPagerから1ページを破棄(削除)する際に問題があります。私は次のような行で問題を説明しようとします。画面の向きを変更した後、ViewPagerのアダプタから項目を破棄します

私はのアダプターには、ViewPagerと、エンドレスビューページャーがどのように動作するかを記述する小さなインターフェースを使用しています。その背後にあるアイデアは、ViewPagerの終わりに達するまで右にスクロールすることができるということです。 API呼び出しからさらに結果をロードできる場合は、結果が来るまで進行状況ページが表示されます。

ここまでは問題ありませんが、今問題が発生します。このロード・プロセスの間に、私は画面を回転させた場合、(これは基本的にAsyncTaskあるAPIの呼び出しには影響しません)、コールから戻ると、私は、この例外を与えるアプリのクラッシュ:にビットを掘った後

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; 
     } 
    } 
} 

必要であれば、基本的にはAPI呼び出しを開始し、以下のonPageSelected()コールバック、::ここで

はページャアダプタのコードである

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

、また、何を言って価値がありますAPIコールは結果を配信した後に行います。ここでは、コールバックがある:setData()方法はnotifiyDataSetChanged()を呼び出すため

@Override 
public boolean onTaskSuccess(Context arg0, List<DataItem> result) { 
    data = result; 
    pagerAdapter.setLoadingData(false); 
    pagerAdapter.setData(result); 
    activity.invalidateOptionsMenu(); 

    return true; 
} 

[OK]を、今、これはcurrentFragments配列に現在あるフラグメントのためgetItemPosition()を呼び出します。もちろん、進捗要素の場合は、このページを削除したいので、POSITION_NONEを返します。このため、基本的にPagedSingleDataAdapterからdestroyItem()コールバックが呼び出されます。私は画面を回転させない場合、すべて正常に動作しますが、進捗要素が表示され、API呼び出しがまだ完了していないときに回転している場合は、アクティビティが再開された後にdestroyItem()コールバックが呼び出されます。

ViewPagerを別のFragmentでホストしていて、アクティビティではないので、OutterFragmentViewPagerをホストしているとも言わなければなりません。私はOutterFragmentonActivityCreated()コールバックでpagerAdapterをインスタンス化し、画面(右、何も変更すべきではない?)pagerAdapterは同じまま回転すると、ここでは、コードようsetRetainInstance(true)を使用しています:

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

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

は今まとめます、PROBLEMは次のとおりです。

私は上記の例外を取得(画面の向きが変更された)、それがインスタンス化され、活動が破壊され、再作成された後ViewPagerから進捗要素を削除しようとした場合(pagerAdapterは、同じままその中のすべてがまた、pagerAdapterをホストするOutterFragmentが破壊されていないため、アクティビティから切り離されてから再接続されるため)。おそらくそれはフラグメントマネージャーで何か起こりますが、私は本当に何がわかりません。私はすでに試した何

  1. を私はフラグメントマネージャからフラグメントを削除しようとしていたonTaskSuccess()コールバックに、すなわち、別の技術を使用して、私の進捗フラグメントを削除しようとすると、動作しませんでした。

  2. また、フラグメントマネージャから完全に削除するのではなく、進捗要素を隠そうとしました。これは50%働いていました。なぜなら、ビューはもう存在しないからですが、空のページがあるので、それは私が探しているものではありません。

  3. また、画面の向きが変更された後にprogressFragmentをフラグメントマネージャに添付しようとしましたが、これも機能しませんでした。

  4. また、アクティビティが再作成された後、進行状況フラグメントをフラグメントマネージャに再度追加しようとしましたが、機能しませんでした。

  5. onTaskSuccess()コールバックから手動でdestroyItem()をコールしようとしましたが(実際は実際には醜いですが動作しませんでした)

申し訳ありませんが、このような長い記事のためにみんなが、私はあなたたちはそれを理解できるように、として最高の私ができるように問題を説明しようとしていました。

どのような解決策でも、お勧めします。

ありがとうございます!

更新:解決済みの問題 OK、これはしばらく時間がかかりました。問題は、destroyItem()コールバックが進行方向のフラグメントで2回、画面の向きが変更されたときに1回、次に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はonTaskSuccess()コールバックで、アダプタのコンストラクタでtrueにfalseに設定されています。 それは本当に機能します!

+0

愚かな質問かもしれませんが、画面を回転させた後に実際にフラグメントをviewPagerに再追加しますか?オリエンテーションの変更であなたの活動が破壊されます。 –

+0

ああ、いいですね。私はそれらを追加していますが、コードを追加するのを忘れていました:)。これを指摘してくれてありがとう、私は私のポストを少し編集します。乾杯! –

+0

実際にonCreateで.addTab()を使用してタブを再度追加してみてください。問題はprogressFragmentだけですか? –

答えて

4

更新:解決済みの問題 OK、これはしばらく時間がかかりました。問題は、destroyItem()コールバックが進行方向のフラグメントで2回、画面の向きが変更されたときに1回、次に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はonTaskSuccess()コールバックで、アダプタのコンストラクタでtrueにfalseに設定されています。本当にうまくいきます!

関連する問題