4

Fragments と BroadcastManager に問題があります。私のアプリケーションでは、1 つのメイン アクティビティに切り替えて、新しい NavigationDrawer を使用します。すべてのコンテンツはフラグメントに含まれています。

1 つのフラグメント (ユーザーの検索) には、Android Design Web サイトのナビゲーション パターン「Lateral Navigation」を使用する 2 つのタブ (名前で検索、条件で検索) が含まれています。

|  Main Activity  |    |  Main Activity  |
-------------------    -------------------
| Search Fragment |    | Search Fragment |
| >Tab 1< | Tab 2 |    | Tab 1 | >Tab 2< |
|    View Pager   |    |    View Pager   |
|   Fragment 1    |    |   Fragment 2    |

両方のタブで同じアクション バー (オプション) メニューを使用したい: 検索アクション。ただし、アクティブなフラグメントのみがそれに反応する必要があります。

私はさまざまなアプローチを試しましたが、最も厄介なのは、現在のフラグメントをビューページャーから直接 (API 以外の実装の詳細に依存せずに) 簡単に取得できないことでした。

アプローチ

LocalBroadcast を使用して、検索がクリックされたことをフラグメントに通知します。各フラグメントは、onResume に小さな Wrapper-Receiver を登録し (そして onPause で削除し)、onReceive をフラグメント自体に転送します (以下に示す方法)。setMenuVisibilityFragmentStatePagerAdpater がフラグメントに対して呼び出すコールバックをオーバーライドして、アクティブなフラグメントを認識します。メニュー セットが表示されているフラグメントのみがブロードキャストに反応します。

ActionBar Tab -> ViewPager -> ViewPagerAdapter -> Fragment.setMenuVisibility
ActionBar Menu Action -> Broadcast ->  ... -> BroadcastReceiver -> Fragments

どちらのフラグメントもフラグメント トランザクションをトリガーして、検索結果を表示し、バック スタックに追加します。

問題 フラグメント トランザクションは一般的に機能しますが、デバイスをローテーションしてから検索を押すと、IllegalStateException が発生します (onSaveInstanceState の後でコミットできません)。

@Override
    public void onReceive(Context context, Intent intent) {
        if (m_menuVisible) {
            final String s = ((TextView) getView().findViewById(R.id.search_text)).getText().toString();

            SearchResultsFragment f = new UserSearchResultsFragment();
            Bundle b = new Bundle();
            b.putString(SearchResultsFragment.EXTRA_SEARCHNAME, s);
            f.setArguments(b);

            FragmentTransaction transaction = getSherlockActivity().getSupportFragmentManager().beginTransaction();
            transaction.replace(R.id.fragment_container_frame, f).addToBackStack(null).commit(); // <<< CRASH
        }
    }

コミットを に変更しようとしましたcommitAllowingStateLossが、「アクティビティが破棄されました」という IllegalStateException が発生します。

ここで何がうまくいかないか知っていますか?どうしようか迷ってます…


追加コード:

MainActivities onCreate (NavigationDrawer サンプルに基づく) および selectItem

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
    setContentView(R.layout.fragment_container_layout);

    setupDrawer(); // Sets up the drawer layout, adapter, etc.

    if (savedInstanceState == null) {
        selectItem(0); // Selects and adds the fragment(s) for the position
    }

    // Other setup stuff
}

private void selectItem(int position) {
        // update the main content by replacing fragments

        Fragment f = null;
        switch (position) {
            case 0:
                f = ...
                break;
            case 1:
                ...
            default:
                throw new IllegalArgumentException("Could not find right fragment");
        }
        f.setRetainInstance(true);
        m_drawerList.setItemChecked(position, true);
        setTitle(mDrawerTitles.get(position).titleRes);

        // Hide any progress bar that might be visible in the actionbar
        setProgressBarIndeterminateVisibility(false);

        // When we select something from the navigation drawer, the back stack is discarded
        final FragmentManager fm = getSupportFragmentManager();
        fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);

        fm.beginTransaction().replace(R.id.fragment_container_frame, f).commit();

        // update selected item and title, then close the drawer
        m_drawerLayout.closeDrawer(GravityCompat.START);
    }

誤動作しているタブ付きフラグメントの FragmentStatePagerAdapter:

protected class TabPagerAdapter extends FragmentStatePagerAdapter {

        private List<String> m_fragmentTags = new ArrayList<String>();

        public TabPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        public void addFragments(List<String> list) {
            ViewPager pager = (ViewPager) getView().findViewById(R.id.viewpager);

            startUpdate(pager);
            for (String tag : list) {
                m_fragmentTags.add(tag);
            }
            finishUpdate(pager);
            notifyDataSetChanged();
        }

        public void addFragment(String tag) {
            addFragments(Collections.singletonList(tag));
        }

        @Override
        public Fragment getItem(int pos) {
            // CreateFragmentForTag: Retrieves the classes, instantiates the fragment
            // Does not do retainInstance or any transaction there.
            return createFragmentForTag(m_fragmentTags.get(pos));
        }

        @Override
        public int getCount() {
            return m_fragmentTags.size();
        }

}

必要不可欠なものだけを含むプロジェクトの簡略版は、https://docs.google.com/file/d/0ByjUMh5UybW7cnB4a0NQeUlYM0E/edit?usp=sharingに投稿されています。

次のように再現します。起動すると、2 つのタブが表示されます。どちらかを選択し、ActionBar の Search をクリックします -> Fragment transaction works。戻るキーを使用し、デバイスを回転させて繰り返します -> クラッシュ! [報奨金が終了した後、またはエラーが特定された後、ファイルにアクセスできない場合があります。]

編集:(1)onReceiveがフラグメントのコンテキストに存在することを明確化(2)メインアクティビティのコードを追加

4

2 に答える 2