Замена ViewPager на фрагмент - затем переход назад

У меня есть действие, в котором изначально находится ViewPager, подключенный к FragmentPagerAdapter.

Когда пользователь щелкает элемент внутри дочернего фрагмента ViewPager, я использую FragmentTransaction для замены пустого представления контейнера новым фрагментом, к которому я хочу перейти.

Если я использую addToBackStack () для транзакции, фиксирую транзакцию и затем возвращаюсь назад, я не возвращаюсь в представления ViewPager (исходный макет).

Если я не использую addToBackStack () для транзакции, зафиксирую транзакцию, а затем вернусь назад, приложение завершит работу.

Кажется очевидным, что ViewPager не добавляется в backstack (что неудивительно, поскольку сам по себе он не является фрагментом). Но я ожидаю, что поведение по умолчанию будет таким, что обратное нажатие вернет меня к первоначальному виду деятельности. Просмотр (ViewPager).

На основании того, что я прочитал, кажется, что, возможно, из-за того, что происходит транзакция фрагмента, ViewPager или PagerAdapter теряет информацию о том, какой фрагмент должен отображаться.

Я действительно смущен этим, но в итоге я создал огромный беспорядок кода, переопределив onBackPress и показывающий и скрывающий представления вьюпейджера. Я бы подумал, что есть более простой способ использовать поведение по умолчанию для выполнения соответствующей навигации.

tl;dr

A - это Viewpager, на котором размещены фрагменты. B - новый Фрагмент.

Когда я заменяю A на B, а затем нажимаю назад, я ожидаю вернуться к A, но этого не происходит.

Любые советы будут высоко ценится.

Код:

Основная деятельность:

  @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);

        headingLayout = (RelativeLayout) findViewById(R.id.headingLayout);
        headingLayout.setVisibility(View.GONE);

        // Set up the ViewPager, attaching the adapter and setting up a listener
        // for when the
        // user swipes between sections.
        mViewPager = (ViewPager) findViewById(R.id.pager);

        mViewPager.setPageMargin(8);

        /** Getting fragment manager */
        FragmentManager fm = getSupportFragmentManager();

        /** Instantiating FragmentPagerAdapter */
        MyFragmentPagerAdapter pagerAdapter = new MyFragmentPagerAdapter(fm);

        /** Setting the pagerAdapter to the pager object */
        mViewPager.setAdapter(pagerAdapter);
.
.
.
}

    public void onListItemClicked(Fragment fragment) {
        fromPlayer = false;
        InitiateTransaction(fragment, true);

    }


    public void InitiateTransaction(Fragment fragment, boolean addToBackStack) {

        invalidateOptionsMenu();

        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();

        ft.replace(R.id.fragmentContainer, fragment).addToBackStack(null)
                .commit();

    }

PagerAdapter:

package another.music.player;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import another.music.player.fragments.AlbumListFragment;
import another.music.player.fragments.ArtistListFragment;
import another.music.player.fragments.SongListFragment;

public class MyFragmentPagerAdapter extends FragmentPagerAdapter {

    final int PAGE_COUNT = 3;

    /** Constructor of the class */
    public MyFragmentPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    /** This method will be invoked when a page is requested to create */
    @Override
    public Fragment getItem(int i) {
        switch (i) {
        case 0:
            ArtistListFragment artistListFragment = new ArtistListFragment();
            Bundle artistData = new Bundle();
            artistData.putInt("current_page", i + 1);
            artistListFragment.setArguments(artistData);
            return artistListFragment;

        case 1:
            AlbumListFragment albumListFragment = new AlbumListFragment();
            Bundle albumData = new Bundle();
            albumData.putInt("current_page", i + 1);
            albumData.putBoolean("showHeader", false);
            albumListFragment.setArguments(albumData);
            return albumListFragment;

        default:

            SongListFragment songListFragment = new SongListFragment();
            Bundle songData = new Bundle();
            songData.putInt("current_page", i + 1);
            songListFragment.setArguments(songData);
            return songListFragment;
        }
    }

    /** Returns the number of pages */
    @Override
    public int getCount() {
        return PAGE_COUNT;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        switch (position) {
        case 0:
            return "Artists";

        case 1:
            return "Albums";

        default:
            return "Songs";
        }
    }
}

основной xml (содержащий fragmentContainer и ViewPager):

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/app_background_ics" >

    <RelativeLayout
        android:id="@+id/headingLayout"
        android:layout_width="match_parent"
        android:layout_height="56dp" >
    </RelativeLayout>

    <FrameLayout
        android:id="@+id/fragmentContainer"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_below="@+id/headingLayout" />

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >

        <android.support.v4.view.PagerTabStrip
            android:id="@+id/pager_title_strip"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="#33b5e5"
            android:paddingBottom="4dp"
            android:paddingTop="4dp"
            android:textColor="#fff" />
    </android.support.v4.view.ViewPager>

</RelativeLayout>

person Tim Malseed    schedule 19.09.2012    source источник


Ответы (5)


У меня тоже была такая же проблема долгое время. Решение оказывается очень простым, и вам не нужно никаких взломов с ViewPager Visibility. Я описан в этом другом вопросе, связанном с SO: Фрагмент в ViewPager не восстанавливается после popBackStack < / а>

Однако для упрощения все, что вам нужно, это использовать getChildFragmentManager () в адаптере ViewPager вместо getSupportFragmentManager (). Итак, вместо этого:

    /** Getting fragment manager */
    FragmentManager fm = getSupportFragmentManager();

    /** Instantiating FragmentPagerAdapter */
    MyFragmentPagerAdapter pagerAdapter = new MyFragmentPagerAdapter(fm);

    /** Setting the pagerAdapter to the pager object */
    mViewPager.setAdapter(pagerAdapter);

Ты делаешь это:

    /** Getting fragment manager */
    FragmentManager fm = getChildFragmentManager();

    /** Instantiating FragmentPagerAdapter */
    MyFragmentPagerAdapter pagerAdapter = new MyFragmentPagerAdapter(fm);

    /** Setting the pagerAdapter to the pager object */
    mViewPager.setAdapter(pagerAdapter);
person Felipe Lima    schedule 27.06.2013
comment
getChildFragmentManager () подходит, если ваш ViewPager размещен внутри фрагмента, но не если ViewPager размещен внутри FragmentActivity. - person Tim Malseed; 28.06.2013

ОБНОВЛЕНИЕ:
Это не "способ Android", и это приводит к плохому взаимодействию с пользователем при просмотре списка. Вместо этого создайте новое занятие.

Для людей, ищущих простое решение этой проблемы, я просто резюмирую то, что я сделал.

Моя архитектура:

  • ViewPager в FragmentActivity (на самом деле ActionBarActivity для поддержки ActionBar. Но ActionBarActivity реализует FragmentActivity).

  • 2 вкладки:

    • FragmentContainer1 that extends Fragment.
    • FragmentContainer2, расширяющий Fragment.

Для каждого FragmentContainer мы вызываем getChildFragmentManager, например, в методе onCreate, и добавляем фрагмент, который хотим показать в этом контейнере:

FragmentToShow fragment = new FragmentToShow();

getChildFragmentManager()
        .beginTransaction()
        .add(R.id.container, fragment)
        .commit();

Мы не хотим, чтобы наш первый фрагмент был добавлен в заднюю часть контейнера фрагмента, потому что мы не хотим отображать контейнер фрагмента, если мы нажмем кнопку возврата.

Затем, если мы хотим заменить FragmentToShow другим фрагментом в нашем классе FragmentToShow (например, со спискомView):

Fragment itemFragment = new ItemFragment();

getFragmentManager()
        .beginTransaction()
        .replace(R.id.container, itemFragment)
        .addToBackStack(null)
        .commit();

Здесь мы получаем диспетчер дочерних фрагментов и добавляем itemFragment в задний стек.

Итак, теперь мы хотим, нажав кнопку «Назад», вернуться к listView (экземпляр FragmentToShow). Наша активность (FragmentActivity) - единственная, которая знает о кнопке возврата, поэтому мы должны переопределить метод onBackPressed () в этом действии:

@Override
public void onBackPressed() {

    // We retrieve the fragment manager of the activity
    FragmentManager frgmtManager = getSupportFragmentManager();

    // We retrieve the fragment container showed right now
    // The viewpager assigns tags to fragment automatically like this
    // mPager is our ViewPager instance
    Fragment fragment = frgmtManager.findFragmentByTag("android:switcher:" + mPager.getId() + ":" + mPager.getCurrentItem());

    // And thanks to the fragment container, we retrieve its child fragment manager
    // holding our fragment in the back stack
    FragmentManager childFragmentManager = fragment.getChildFragmentManager();

    // And here we go, if the back stack is empty, we let the back button doing its job
    // Otherwise, we show the last entry in the back stack (our FragmentToShow) 
    if(childFragmentManager.getBackStackEntryCount() == 0){
        super.onBackPressed();
    } else {
        childFragmentManager.popBackStack();
    }
}

Поскольку мы вызываем getSupportFragmentManager в нашей деятельности, мы просто можем вызвать getFragmentManager в наших дочерних фрагментах. Это вернет экземпляр поддержки FragmentManager.

Вот и все! Я не эксперт, поэтому, если у вас есть предложения или замечания, не стесняйтесь.

person Menerve    schedule 02.08.2013
comment
Эта архитектура работает для меня. Спасибо :) - person Dory; 18.12.2014

Единственный способ добиться этого, который я нашел, - это сделать следующее:

При переходе от viewPager, отправьте viewPager из поля зрения с помощью Visiblity.GONE. Добавьте любые транзакции фрагментов в backstack.

При возврате к экрану viewPager (с помощью обратного нажатия) переопределите onBackPressed. Вы можете проверить, сколько фрагментов находится в backstack. Если viewPager был первым представлением до того, как произошли транзакции фрагментов, вы можете проверить, равно ли счетчик записей backstack фрагментов 0.

fragmentManager.getBackStackEntryCount () == 0, в backstack нет фрагментов.

Если это утверждение верно, просто верните viewPager обратно в поле зрения, используя Visibility.VISIBLE.

person Tim Malseed    schedule 24.09.2012

Если вы используете ViewPager, содержащий Fragments, который может запускать Activities, вот как вы могли бы правильно вернуться к позиции в ViewPager после нажатия или перехода вверх от указанного Activity (Предполагается, что для вашего Activities объявлена ​​навигация вверх).

Во-первых, вам нужно передать текущую позицию ViewPager как Extra в Intent, чтобы начать новый Activity.

Затем вы передадите эту позицию родителю Activity, сделав это:

Intent upIntent = NavUtils.getParentActivityIntent(this);
upIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
upIntent.putExtra(FeedPagerActivity.EXTRA_POSITION, position);
NavUtils.navigateUpTo(this, upIntent);

Поместите этот код в

onOptionsItemSelected (элемент MenuItem)

person Sakiboy    schedule 28.05.2014
comment
Ваш ответ непонятен, не могли бы вы дать несколько идей или ссылок по теме? Спасибо за ваш вклад. - person MohanRaj S; 26.05.2016

Думаю, у меня была очень похожая проблема.

Кажется, что FragmentManagers ведут себя несколько иерархично. Я решил эту проблему с помощью «основного» FragmentManager из действия, в котором размещен контейнер и ViewPager, а не того, который можно получить из фрагментов внутри ViewPager.

Для этого я использовал:

this.getActivity().getSupportFragmentManager()

где this - это экземпляр фрагмента.

Теперь, если я перейду от элемента в ViewPager к какому-либо другому фрагменту с помощью транзакции «заменить», после возврата я смогу увидеть ViewPager в том состоянии, в котором я оставил.

Более полный пример кода выглядит так:

FragmentManager fragmentManager =  MyCurrentFragment.this.getActivity().getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();

transaction.replace(R.id.container, newFragment);
transaction.addToBackStack(null);

transaction.commit();
person Tadas Šubonis    schedule 05.11.2014