Вкладки панели действий Android - проблемы с транзакциями внутреннего фрагмента

Я успешно настроил вкладки на панели действий, используя следующий пример из Google по адресу http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app./FragmentTabs.html в качестве основы . Мой код выглядит так:

public class Main extends SherlockFragmentActivity {

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);        

    getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

    ActionBar.Tab tab1 = getSupportActionBar().newTab();
    tab1.setIcon(R.drawable.ic_tab_example_selected);
    tab1.setTabListener(new TabListener<Tab1>(this, "A", Tab1.class));
    getSupportActionBar().addTab(tab1);

    ActionBar.Tab tab2 = getSupportActionBar().newTab();
    tab2.setIcon(R.drawable.ic_tab_example_selected);
    tab2.setTabListener(new TabListener<Tab2>(this, "B", Tab2.class));
    getSupportActionBar().addTab(tab2);

    ActionBar.Tab tab3 = getSupportActionBar().newTab();
    tab3.setIcon(R.drawable.ic_tab_example_selected);
    tab3.setTabListener(new TabListener<Tab3>(this, "C", Tab3.class));
    getSupportActionBar().addTab(tab3);

    ActionBar.Tab tab4 = getSupportActionBar().newTab();
    tab4.setIcon(R.drawable.ic_tab_example_selected);
    tab4.setTabListener(new TabListener<Tab4>(this, "D", Tab4.class));
    getSupportActionBar().addTab(tab4);     
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getSupportMenuInflater();
    inflater.inflate(R.menu.menu, menu);
    return true;
}

public class TabListener<T extends Fragment> implements ActionBar.TabListener {
    private Fragment mFragment;
    private final SherlockFragmentActivity mActivity;
    private final String mTag;
    private final Class<T> mClass;

    /** Constructor used each time a new tab is created.
      * @param activity  The host Activity, used to instantiate the fragment
      * @param tag  The identifier tag for the fragment
      * @param clz  The fragment's Class, used to instantiate the fragment
      */
    public TabListener(SherlockFragmentActivity activity, String tag, Class<T> clz) {
        mActivity = activity;
        mTag = tag;
        mClass = clz;
    }       

    /* The following are each of the ActionBar.TabListener callbacks */

    public void onTabSelected(Tab tab, FragmentTransaction ft) {

        // Check if the fragment is already initialized
        if (mFragment == null) {
            // If not, instantiate and add it to the activity
            mFragment = Fragment.instantiate(mActivity, mClass.getName());
            ft.add(android.R.id.content, mFragment, mTag);
        } else {
            // If it exists, simply attach it in order to show it
            ft.attach(mFragment);
        }
    }

    public void onTabUnselected(Tab tab, FragmentTransaction ft) {

        if (mFragment != null) {
            // Detach the fragment, because another one is being attached
            ft.detach(mFragment);
        }
    }

    public void onTabReselected(Tab tab, FragmentTransaction ft) {
        // User selected the already selected tab. Usually do nothing.
    }
}
}

При этом у меня есть несколько вкладок, которые переключаются между различными фрагментами на каждой вкладке. Однако моя проблема начинается, когда я на вкладке и меняю фрагменты внутри вкладки. Это проблема:

Когда я нахожусь на вкладке 1, я заменяю исходный фрагмент, загруженный на вкладку, новым фрагментом. Затем я перехожу на вкладку 2, где показан начальный фрагмент. Тем не менее, представление фрагмента, замененного на вкладку 1, по-прежнему отображается за фрагментом вкладки 2:

Вкладка 1, фрагмент 2Вкладка 2, Фрагмент 1

Это код, который я сейчас использую для изменения фрагмента из вкладки 1:

// Create new fragment and transaction
    FragmentTransaction transaction = ctx.getFragmentManager().beginTransaction();
    transaction.setCustomAnimations(R.anim.slide_in_left, R.anim.slide_out_right);

    // Replace whatever is in the fragment_container view with this fragment,
    // and add the transaction to the back stack
    transaction.replace(container, fragment, tag);

    if(addToBackStack)
        transaction.addToBackStack(tag);

    // Commit the transaction
    transaction.commit();

Все это достигается с помощью ActionBar Sherlock и библиотеки поддержки Google v4.


person Richard Lewin    schedule 30.03.2012    source источник
comment
Я пытаюсь внедрить вкладки на основе фрагментов, как у вас, но я не могу заставить их работать. я разместил свой вопрос здесь. Пожалуйста, помогите мне понять, что я делаю неправильно вопросы/13761581/   -  person Harsha M V    schedule 07.12.2012


Ответы (1)


Итак, этот ответ предполагает, что вы хотите стирать историю каждой вкладки каждый раз, когда вы меняете вкладки. Я имею в виду, что вкладка 1 начинается с фрагмента 1, затем вы щелкаете и меняете его на фрагмент 2. Если вы выберете вкладку 2, вы отмените историю вкладки 1, и в следующий раз, когда вы нажмете вкладку 1, вы вернетесь к фраг 1.

С учетом сказанного вот решение: замените свой onTabUnselected на приведенный ниже

public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        if (mFragment != null) {
            //this segment removes the back history of everything in the tab you are leaving so when you click on the tab again you go back to a fresh start
            FragmentManager man = mActivity.getFragmentManager();
            if(man.getBackStackEntryCount()>0) //this check is required to prevent null point exceptions when clicking off of a tab with no history
                man.popBackStack(man.getBackStackEntryAt(0).getName(), FragmentManager.POP_BACK_STACK_INCLUSIVE); //this pops the stack back to index 0 so you can then detach and then later attach your initial fragment
            //also it should be noted that if you do popbackstackimmediate here instead of just popbackstack you will see a flash as the gui changes back to the first fragment when the code executes
            //end
            ft.detach(mFragment);
        }
    }
person jp36    schedule 16.04.2012
comment
Спасибо за ответ. Я попробовал это, и теперь он отлично очищает старый вид. jp36, у вас есть идеи, как сохранить историю вкладок, если это необходимо? - person Richard Lewin; 19.04.2012
comment
Если бы от меня требовалось вести историю вкладок, я бы, наверное, отказался от попыток использовать встроенный BackStack и не делал вызовов .addToBackStack(tag). Вместо этого я бы создал свои собственные «бэкстеки» для каждой вкладки, и я бы перехватывал нажатия кнопки «Назад» и вручную выталкивал вещи из моих самоуправляемых стеков и использовал это для изменения текущего фрагмента. - person jp36; 20.04.2012
comment
Тем не менее, предостережение: replace без использования встроенного backstack и backstackpops, как я предложил в ответе, вы столкнетесь с проблемами при переключении вкладок. Потому что вкладки просто attach и detach вкладки, а replace удаляет фрагмент, который вкладка будет пытаться прикрепить. Вам нужно будет исследовать либо использование detach(previous); attach(popped); (что может привести к проблемам с памятью, я не уверен), либо вам нужно будет выполнить какую-то другую работу на вкладке, изменяя код, чтобы указать на текущий фрагмент вместо фрагмента верхнего уровня. - person jp36; 20.04.2012
comment
Я нашел действительно хорошее решение, которое также сохраняет неповрежденным backstack: stackoverflow. com/questions/10502786/ — таким образом вы сможете переключаться между вкладками и при этом сохранять историю внутренних вкладок нетронутой (важно для раскрывающихся списков). - person AgentKnopf; 17.02.2013