ViewPager 내부의 조각 교체


268

를 사용하여 Fragment를 사용하려고 ViewPager합니다 FragmentPagerAdapter. 내가 달성하고자하는 것은의 첫 페이지에있는 조각을 다른 것으로 교체하는 ViewPager것입니다.

호출기는 두 페이지로 구성됩니다. 첫 번째 FirstPagerFragmentSecondPagerFragment입니다. 두 번째는 입니다. 첫 페이지의 버튼을 클릭하십시오. 를 FirstPagerFragmentNextFragment 로 바꾸고 싶습니다 .

아래 코드가 있습니다.

public class FragmentPagerActivity extends FragmentActivity {

    static final int NUM_ITEMS = 2;

    MyAdapter mAdapter;
    ViewPager mPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.fragment_pager);

        mAdapter = new MyAdapter(getSupportFragmentManager());

        mPager = (ViewPager) findViewById(R.id.pager);
        mPager.setAdapter(mAdapter);

    }


    /**
     * Pager Adapter
     */
    public static class MyAdapter extends FragmentPagerAdapter {
        public MyAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public int getCount() {
            return NUM_ITEMS;
        }

        @Override
        public Fragment getItem(int position) {

            if(position == 0) {
                return FirstPageFragment.newInstance();
            } else {
                return SecondPageFragment.newInstance();
            }

        }
    }


    /**
     * Second Page FRAGMENT
     */
    public static class SecondPageFragment extends Fragment {

        public static SecondPageFragment newInstance() {
            SecondPageFragment f = new SecondPageFragment();
            return f;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            //Log.d("DEBUG", "onCreateView");
            return inflater.inflate(R.layout.second, container, false);

        }
    }

    /**
     * FIRST PAGE FRAGMENT
     */
    public static class FirstPageFragment extends Fragment {

        Button button;

        public static FirstPageFragment newInstance() {
            FirstPageFragment f = new FirstPageFragment();
            return f;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            //Log.d("DEBUG", "onCreateView");
            View root = inflater.inflate(R.layout.first, container, false);
            button = (Button) root.findViewById(R.id.button);
            button.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    FragmentTransaction trans = getFragmentManager().beginTransaction();
                                    trans.replace(R.id.first_fragment_root_id, NextFragment.newInstance());
                    trans.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
                    trans.addToBackStack(null);
                    trans.commit();

                }

            });

            return root;
        }

        /**
     * Next Page FRAGMENT in the First Page
     */
    public static class NextFragment extends Fragment {

        public static NextFragment newInstance() {
            NextFragment f = new NextFragment();
            return f;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            //Log.d("DEBUG", "onCreateView");
            return inflater.inflate(R.layout.next, container, false);

        }
    }
}

... 그리고 여기에 xml 파일

fragment_pager.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" android:padding="4dip"
        android:gravity="center_horizontal"
        android:layout_width="match_parent" android:layout_height="match_parent">

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

</LinearLayout>

first.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/first_fragment_root_id"
  android:orientation="vertical"
  android:layout_width="match_parent"
  android:layout_height="match_parent">

  <Button android:id="@+id/button"
     android:layout_width="wrap_content" android:layout_height="wrap_content"
     android:text="to next"/>

</LinearLayout>

이제 문제는 ... 어떤 ID를 사용해야합니까?

trans.replace(R.id.first_fragment_root_id, NextFragment.newInstance());

?

를 사용 R.id.first_fragment_root_id하면 교체가 작동하지만 계층 뷰어는 아래와 같이 이상한 동작을 보여줍니다.

처음에는 상황이

교체 후 상황은

보시다시피 문제가 있음을 알 수 있듯이 조각을 교체 한 후 첫 번째 그림과 동일한 상태를 찾을 것으로 예상됩니다.


어떻게 레이아웃 다이어그램을 생성 했습니까? 아니면 수동으로 했습니까?
Jeroen Vannevel

@ 국수 : 나는 똑같이해야한다. 귀하의 질문에 여러 가지 답변이있는 긴 스레드가 생성되었습니다. 결국 무엇이 가장 효과가 있었습니까? 많은 감사합니다!
narb

1
@JeroenVannevel 당신은 이미 알아 차렸을 수도 있지만 레이아웃 다이어그램은 developer.android.com/tools/performance/hierarchy-viewer/…
Ankit Popli

문제가
albert

답변:


162

거기의 수정 소스 코드를 필요로하지 않는 다른 솔루션 ViewPagerFragmentStatePagerAdapter, 그리고 그것은 작동 FragmentPagerAdapter저자가 사용하는 기본 클래스.

어떤 ID를 사용해야하는지 저자의 질문에 답하고 싶습니다. 컨테이너의 ID, 즉 뷰 호출기 자체의 ID입니다. 그러나 자신도 알겠지만 코드에서 해당 ID를 사용하면 아무 일도 일어나지 않습니다. 이유를 설명하겠습니다.

우선 ViewPager, 페이지를 다시 채우 notifyDataSetChanged()려면 어댑터의 기본 클래스에 있는 페이지를 호출 해야합니다.

둘째, 추상 방법을 ViewPager사용하여 getItemPosition()어떤 페이지를 삭제하고 유지해야하는지 확인합니다. 항상이 기능 반환의 기본 구현 POSITION_UNCHANGED발생, ViewPager현재의 모든 페이지를 유지, 따라서 새 페이지를 부착하지. 따라서 프래그먼트 교체 작업을 수행 getItemPosition()하려면 어댑터에서 대체해야 POSITION_NONE하며, 구식으로 숨겨 질 때 숨겨져있는 프래그먼트를 인수로 반환해야합니다 .

이것은 또한 어댑터는 항상 위치 0에 표시되어야하는 단편을 인식 할 필요가 있다는 것을 의미 FirstPageFragmentNextFragment. 이를 수행하는 한 가지 방법은을 만들 때 리스너를 제공하는 FirstPageFragment것으로 조각을 전환 할 때 호출됩니다. 나는 당신의 조각 어댑터 핸들을 수 있도록 모든 단편 스위치 및 호출하지만 이것이 좋은 일이라고 생각 ViewPager하고 FragmentManager.

셋째, FragmentPagerAdapter위치에서 파생 된 이름으로 사용 된 조각을 캐시하므로 위치 0에 조각이 있으면 클래스가 새로운 경우에도 대체되지 않습니다. 두 가지 해결책이 있지만 가장 간단한 방법은의 remove()기능 을 사용하여 FragmentTransaction태그를 제거하는 것입니다.

그것은 많은 텍스트였습니다, 다음은 귀하의 경우에 작동 해야하는 코드입니다 :

public class MyAdapter extends FragmentPagerAdapter
{
    static final int NUM_ITEMS = 2;
    private final FragmentManager mFragmentManager;
    private Fragment mFragmentAtPos0;

    public MyAdapter(FragmentManager fm)
    {
        super(fm);
        mFragmentManager = fm;
    }

    @Override
    public Fragment getItem(int position)
    {
        if (position == 0)
        {
            if (mFragmentAtPos0 == null)
            {
                mFragmentAtPos0 = FirstPageFragment.newInstance(new FirstPageFragmentListener()
                {
                    public void onSwitchToNextFragment()
                    {
                        mFragmentManager.beginTransaction().remove(mFragmentAtPos0).commit();
                        mFragmentAtPos0 = NextFragment.newInstance();
                        notifyDataSetChanged();
                    }
                });
            }
            return mFragmentAtPos0;
        }
        else
            return SecondPageFragment.newInstance();
    }

    @Override
    public int getCount()
    {
        return NUM_ITEMS;
    }

    @Override
    public int getItemPosition(Object object)
    {
        if (object instanceof FirstPageFragment && mFragmentAtPos0 instanceof NextFragment)
            return POSITION_NONE;
        return POSITION_UNCHANGED;
    }
}

public interface FirstPageFragmentListener
{
    void onSwitchToNextFragment();
}

이것이 누군가를 돕기를 바랍니다!


24
나에게도 효과가 있지만 첫 번째 조각을 표시하기 위해 뒤로 버튼을 구현하는 방법은 무엇입니까?
user1159819

6
훌륭한 설명이지만 이것의 전체 소스 코드, 특히 FirstPageFragment 및 SecondPageFragment 클래스를 게시 할 수 있습니다.
georgiecasey

6
newInstance ()의 번들에 FirstPageFragmentListener를 어떻게 추가합니까?
미구엘

4
FirstPageFragment.newInstance()리스너 매개 변수로 처리하는 코드를 추가 할 수 있습니까 ?
MiguelHincapieC

6
이것은 조각을 제거 할 때 commit () 대신 commitNow ()를 호출하는 경우에만 작동합니다. 내 유스 케이스가 왜 다른지 잘 모르겠지만 희망적으로 이것은 누군가를 절약 할 수 있기를 바랍니다.
Ian Lovejoy

37

2012 년 11 월 13 일부터 ViewPager에서 조각을 수정하는 것이 훨씬 쉬워졌습니다. Google은 중첩 조각을 지원하는 Android 4.2를 출시했으며 새로운 Android 지원 라이브러리 v11 에서도 지원 되므로 1.6까지 계속 작동합니다.

getChildFragmentManager를 사용하는 것을 제외하고는 조각을 대체하는 일반적인 방법과 매우 유사합니다. 사용자가 뒤로 버튼을 클릭 할 때 중첩 된 조각 백 스택이 팝업되지 않는 것을 제외하고 작동하는 것 같습니다 . 연결된 질문의 솔루션에 따라 조각의 하위 관리자에서 popBackStackImmediate ()를 수동으로 호출해야합니다. 따라서 ViewPager의 현재 조각을 가져오고 getChildFragmentManager (). popBackStackImmediate ()를 호출하는 ViewPager 활동의 onBackPressed ()를 재정의해야합니다.

현재 표시되는 조각을 얻는 것도 약간 해킹입니다.더러운 "android : switcher : VIEWPAGER_ID : INDEX"솔루션을 사용 했지만 이 페이지 의 두 번째 솔루션 설명 된대로 ViewPager의 모든 조각을 직접 추적 할 수도 있습니다 .

여기 사용자가 행을 클릭하고 뒤로 버튼이 작동 할 때 ViewPager에 상세보기가 표시된 4 개의 ListView가있는 ViewPager에 대한 코드가 있습니다. 간결성을 위해 관련 코드 만 포함 시키려고 했으므로 전체 앱을 GitHub에 업로드하려면 의견을 남기십시오.

HomeActivity.java

 public class HomeActivity extends SherlockFragmentActivity {
FragmentAdapter mAdapter;
ViewPager mPager;
TabPageIndicator mIndicator;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    mAdapter = new FragmentAdapter(getSupportFragmentManager());
    mPager = (ViewPager)findViewById(R.id.pager);
    mPager.setAdapter(mAdapter);
    mIndicator = (TabPageIndicator)findViewById(R.id.indicator);
    mIndicator.setViewPager(mPager);
}

// This the important bit to make sure the back button works when you're nesting fragments. Very hacky, all it takes is some Google engineer to change that ViewPager view tag to break this in a future Android update.
@Override
public void onBackPressed() {
    Fragment fragment = (Fragment) getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":"+mPager.getCurrentItem());
    if (fragment != null) // could be null if not instantiated yet
    {
        if (fragment.getView() != null) {
            // Pop the backstack on the ChildManager if there is any. If not, close this activity as normal.
            if (!fragment.getChildFragmentManager().popBackStackImmediate()) {
                finish();
            }
        }
    }
}

class FragmentAdapter extends FragmentPagerAdapter {        
    public FragmentAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int position) {
        switch (position) {
        case 0:
            return ListProductsFragment.newInstance();
        case 1:
            return ListActiveSubstancesFragment.newInstance();
        case 2:
            return ListProductFunctionsFragment.newInstance();
        case 3:
            return ListCropsFragment.newInstance();
        default:
            return null;
        }
    }

    @Override
    public int getCount() {
        return 4;
    }

 }
}

ListProductsFragment.java

public class ListProductsFragment extends SherlockFragment {
private ListView list;

public static ListProductsFragment newInstance() {
    ListProductsFragment f = new ListProductsFragment();
    return f;
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View V = inflater.inflate(R.layout.list, container, false);
    list = (ListView)V.findViewById(android.R.id.list);
    list.setOnItemClickListener(new OnItemClickListener() {
        public void onItemClick(AdapterView<?> parent, View view,
            int position, long id) {
          // This is important bit
          Fragment productDetailFragment = FragmentProductDetail.newInstance();
          FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
          transaction.addToBackStack(null);
          transaction.replace(R.id.products_list_linear, productDetailFragment).commit();
        }
      });       
    return V;
}
}

7
코드를 github에 업로드 할 수 있습니까? 감사.
Gautham

4
@georgiecasey : API 17, Android 4.2를 다운로드하여 getChildFragmentManager () 사용을 시작할 수있었습니다. 그러나 이제 화면에 오래된 + 새 조각이 겹치므로 솔루션에서 무언가를 놓치고 있어야합니다. 참고 : developer.android.com/reference/android/support/v4/view/… 의 Google 예제 TabsAdapter 코드와 함께 솔루션을 사용하려고합니다 . 제안이나 참조 코드에 감사드립니다.
gcl1

27
R.id.products_list_linear는 무엇을 의미합니까? 제공된 코드에 연결하십시오.
howettl

1
@howettl ID는 외부 조각 멤버에 대한 참조입니다. 이 문제를 해결하기위한 핵심은 중첩 된 프래그먼트를 사용하는 것입니다 (뷰 페이저 어댑터를 조작 할 필요가없는 방식).
Indrek Kõue 2016 년

2
이것이 github에 만든 적이 있습니까?
Baruch

32

도움이되고 우아하다고 생각한 @wize의 답변을 바탕으로 부분적으로 원하는 것을 달성 할 수 있었기 때문에 일단 교체 된 첫 번째 조각으로 가능성을 되돌릴 수있었습니다. 나는 그의 코드를 조금 수정하는 것을 달성했다.

이것은 FragmentPagerAdapter입니다.

public static class MyAdapter extends FragmentPagerAdapter {
    private final class CalendarPageListener implements
            CalendarPageFragmentListener {
        public void onSwitchToNextFragment() {
            mFragmentManager.beginTransaction().remove(mFragmentAtPos0)
                    .commit();
            if (mFragmentAtPos0 instanceof FirstFragment){
                mFragmentAtPos0 = NextFragment.newInstance(listener);
            }else{ // Instance of NextFragment
                mFragmentAtPos0 = FirstFragment.newInstance(listener);
            }
            notifyDataSetChanged();
        }
    }

    CalendarPageListener listener = new CalendarPageListener();;
    private Fragment mFragmentAtPos0;
    private FragmentManager mFragmentManager;

    public MyAdapter(FragmentManager fm) {
        super(fm);
        mFragmentManager = fm;
    }

    @Override
    public int getCount() {
        return NUM_ITEMS;
    }

    @Override
    public int getItemPosition(Object object) {
        if (object instanceof FirstFragment && mFragmentAtPos0 instanceof NextFragment)
            return POSITION_NONE;
        if (object instanceof NextFragment && mFragmentAtPos0 instanceof FirstFragment)
            return POSITION_NONE;
        return POSITION_UNCHANGED;
    }

    @Override
    public Fragment getItem(int position) {
        if (position == 0)
            return Portada.newInstance();
        if (position == 1) { // Position where you want to replace fragments
            if (mFragmentAtPos0 == null) {
                mFragmentAtPos0 = FirstFragment.newInstance(listener);
            }
            return mFragmentAtPos0;
        }
        if (position == 2)
            return Clasificacion.newInstance();
        if (position == 3)
            return Informacion.newInstance();

        return null;
    }
}

public interface CalendarPageFragmentListener {
    void onSwitchToNextFragment();
}

대체를 수행하려면, 유형의 정적 필드를 정의 하고 해당 프래그먼트 CalendarPageFragmentListenernewInstance메소드를 통해 초기화 하고 호출 FirstFragment.pageListener.onSwitchToNextFragment()하거나 NextFragment.pageListener.onSwitchToNextFragment()눈에 띄게 호출 하십시오.


교체는 어떻게 수행합니까? onSwitchToNext 메소드를 대체하지 않고 리스너를 초기화 할 수 없습니다.
Leandros

1
장치를 회전해도 mFragmentAtPos0에 저장된 상태를 잃지 않습니까?
seb

@ seb, mFragmentAtPos0활동 상태 를 저장할 때 참조 를 저장하기 위해 실제로이 솔루션을 개선했습니다 . 가장 우아한 솔루션은 아니지만 작동합니다.
mdelolmo

내 대답을 볼 수 있습니다 . 조각을 대체하고 첫 번째 조각으로 돌아갑니다.
Lyd

@mdelolmo ViewPager에 대한이 질문을 볼 수 있습니까? 감사합니다 stackoverflow.com/questions/27937250/…
Hoa Vu

21

나는 다음에 대한 솔루션을 구현했다.

  • 탭 내부의 동적 조각 교체
  • 탭당 히스토리 유지 보수
  • 방향 변경 작업

이를 달성하기위한 요령은 다음과 같습니다.

  • notifyDataSetChanged () 메소드를 사용하여 단편 대체를 적용하십시오.
  • 조각 모음 관리자는 백 스테이지에만 사용하고 조각 모음 교체에는 사용하지 마십시오.
  • 메멘토 패턴 (스택)을 사용하여 기록 유지

어댑터 코드는 다음과 같습니다.

public class TabsAdapter extends FragmentStatePagerAdapter implements ActionBar.TabListener, ViewPager.OnPageChangeListener {

/** The sherlock fragment activity. */
private final SherlockFragmentActivity mActivity;

/** The action bar. */
private final ActionBar mActionBar;

/** The pager. */
private final ViewPager mPager;

/** The tabs. */
private List<TabInfo> mTabs = new LinkedList<TabInfo>();

/** The total number of tabs. */
private int TOTAL_TABS;

private Map<Integer, Stack<TabInfo>> history = new HashMap<Integer, Stack<TabInfo>>();

/**
 * Creates a new instance.
 *
 * @param activity the activity
 * @param pager    the pager
 */
public TabsAdapter(SherlockFragmentActivity activity, ViewPager pager) {
    super(activity.getSupportFragmentManager());
    activity.getSupportFragmentManager();
    this.mActivity = activity;
    this.mActionBar = activity.getSupportActionBar();
    this.mPager = pager;
    mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
}

/**
 * Adds the tab.
 *
 * @param image         the image
 * @param fragmentClass the class
 * @param args          the arguments
 */
public void addTab(final Drawable image, final Class fragmentClass, final Bundle args) {
    final TabInfo tabInfo = new TabInfo(fragmentClass, args);
    final ActionBar.Tab tab = mActionBar.newTab();
    tab.setTabListener(this);
    tab.setTag(tabInfo);
    tab.setIcon(image);

    mTabs.add(tabInfo);
    mActionBar.addTab(tab);

    notifyDataSetChanged();
}

@Override
public Fragment getItem(final int position) {
    final TabInfo tabInfo = mTabs.get(position);
    return Fragment.instantiate(mActivity, tabInfo.fragmentClass.getName(), tabInfo.args);
}

@Override
public int getItemPosition(final Object object) {
    /* Get the current position. */
    int position = mActionBar.getSelectedTab().getPosition();

    /* The default value. */
    int pos = POSITION_NONE;
    if (history.get(position).isEmpty()) {
        return POSITION_NONE;
    }

    /* Checks if the object exists in current history. */
    for (Stack<TabInfo> stack : history.values()) {
        TabInfo c = stack.peek();
        if (c.fragmentClass.getName().equals(object.getClass().getName())) {
            pos = POSITION_UNCHANGED;
            break;
        }
    }
    return pos;
}

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

@Override
public void onPageScrollStateChanged(int arg0) {
}

@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}

@Override
public void onPageSelected(int position) {
    mActionBar.setSelectedNavigationItem(position);
}

@Override
public void onTabSelected(final ActionBar.Tab tab, final FragmentTransaction ft) {
    TabInfo tabInfo = (TabInfo) tab.getTag();
    for (int i = 0; i < mTabs.size(); i++) {
        if (mTabs.get(i).equals(tabInfo)) {
            mPager.setCurrentItem(i);
        }
    }
}

@Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
}

@Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
}

public void replace(final int position, final Class fragmentClass, final Bundle args) {
    /* Save the fragment to the history. */
    mActivity.getSupportFragmentManager().beginTransaction().addToBackStack(null).commit();

    /* Update the tabs. */
    updateTabs(new TabInfo(fragmentClass, args), position);

    /* Updates the history. */
    history.get(position).push(new TabInfo(mTabs.get(position).fragmentClass, mTabs.get(position).args));

    notifyDataSetChanged();
}

/**
 * Updates the tabs.
 *
 * @param tabInfo
 *          the new tab info
 * @param position
 *          the position
 */
private void updateTabs(final TabInfo tabInfo, final int position) {
    mTabs.remove(position);
    mTabs.add(position, tabInfo);
    mActionBar.getTabAt(position).setTag(tabInfo);
}

/**
 * Creates the history using the current state.
 */
public void createHistory() {
    int position = 0;
    TOTAL_TABS = mTabs.size();
    for (TabInfo mTab : mTabs) {
        if (history.get(position) == null) {
            history.put(position, new Stack<TabInfo>());
        }
        history.get(position).push(new TabInfo(mTab.fragmentClass, mTab.args));
        position++;
    }
}

/**
 * Called on back
 */
public void back() {
    int position = mActionBar.getSelectedTab().getPosition();
    if (!historyIsEmpty(position)) {
        /* In case there is not any other item in the history, then finalize the activity. */
        if (isLastItemInHistory(position)) {
            mActivity.finish();
        }
        final TabInfo currentTabInfo = getPrevious(position);
        mTabs.clear();
        for (int i = 0; i < TOTAL_TABS; i++) {
            if (i == position) {
                mTabs.add(new TabInfo(currentTabInfo.fragmentClass, currentTabInfo.args));
            } else {
                TabInfo otherTabInfo = history.get(i).peek();
                mTabs.add(new TabInfo(otherTabInfo.fragmentClass, otherTabInfo.args));
            }
        }
    }
    mActionBar.selectTab(mActionBar.getTabAt(position));
    notifyDataSetChanged();
}

/**
 * Returns if the history is empty.
 *
 * @param position
 *          the position
 * @return  the flag if empty
 */
private boolean historyIsEmpty(final int position) {
    return history == null || history.isEmpty() || history.get(position).isEmpty();
}

private boolean isLastItemInHistory(final int position) {
    return history.get(position).size() == 1;
}

/**
 * Returns the previous state by the position provided.
 *
 * @param position
 *          the position
 * @return  the tab info
 */
private TabInfo getPrevious(final int position) {
    TabInfo currentTabInfo = history.get(position).pop();
    if (!history.get(position).isEmpty()) {
        currentTabInfo = history.get(position).peek();
    }
    return currentTabInfo;
}

/** The tab info class */
private static class TabInfo {

    /** The fragment class. */
    public Class fragmentClass;

    /** The args.*/
    public Bundle args;

    /**
     * Creates a new instance.
     *
     * @param fragmentClass
     *          the fragment class
     * @param args
     *          the args
     */
    public TabInfo(Class fragmentClass, Bundle args) {
        this.fragmentClass = fragmentClass;
        this.args = args;
    }

    @Override
    public boolean equals(final Object o) {
        return this.fragmentClass.getName().equals(o.getClass().getName());
    }

    @Override
    public int hashCode() {
        return fragmentClass.getName() != null ? fragmentClass.getName().hashCode() : 0;
    }

    @Override
    public String toString() {
        return "TabInfo{" +
                "fragmentClass=" + fragmentClass +
                '}';
    }
}

모든 탭을 처음 추가 할 때 createHistory () 메소드를 호출하여 초기 히스토리를 작성해야합니다.

public void createHistory() {
    int position = 0;
    TOTAL_TABS = mTabs.size();
    for (TabInfo mTab : mTabs) {
        if (history.get(position) == null) {
            history.put(position, new Stack<TabInfo>());
        }
        history.get(position).push(new TabInfo(mTab.fragmentClass, mTab.args));
        position++;
    }
}

조각을 특정 탭으로 바꿀 때마다 replace (final int position, final Class fragmentClass, final Bundle args)

/* Save the fragment to the history. */
    mActivity.getSupportFragmentManager().beginTransaction().addToBackStack(null).commit();

    /* Update the tabs. */
    updateTabs(new TabInfo(fragmentClass, args), position);

    /* Updates the history. */
    history.get(position).push(new TabInfo(mTabs.get(position).fragmentClass, mTabs.get(position).args));

    notifyDataSetChanged();

다시 누르면 back () 메소드를 호출해야합니다.

public void back() {
    int position = mActionBar.getSelectedTab().getPosition();
    if (!historyIsEmpty(position)) {
        /* In case there is not any other item in the history, then finalize the activity. */
        if (isLastItemInHistory(position)) {
            mActivity.finish();
        }
        final TabInfo currentTabInfo = getPrevious(position);
        mTabs.clear();
        for (int i = 0; i < TOTAL_TABS; i++) {
            if (i == position) {
                mTabs.add(new TabInfo(currentTabInfo.fragmentClass, currentTabInfo.args));
            } else {
                TabInfo otherTabInfo = history.get(i).peek();
                mTabs.add(new TabInfo(otherTabInfo.fragmentClass, otherTabInfo.args));
            }
        }
    }
    mActionBar.selectTab(mActionBar.getTabAt(position));
    notifyDataSetChanged();
}

이 솔루션은 셜록 작업 표시 줄 및 슬쩍 제스처와 함께 작동합니다.


대단해! github에 넣을 수 있습니까?
Nicramus

이 구현은 여기에 제공된 모든 것 중 가장 깨끗하며 모든 단일 문제를 해결합니다. 허용되는 답변이어야합니다.
dazedviper

이 구현에 문제가있는 사람이 있습니까? 나는 그들 중 2가 있습니다. 1. 첫 번째 백이 등록되지 않습니다-다시 등록하려면 두 번 클릭해야합니다. 2. 탭 a에서 뒤로 (두 번)를 클릭하고 탭 b로 이동하면 탭 a로 돌아 가면 탭 b의 내용 만 표시됩니다.
yaronbic

7

tl; dr : 호스팅 된 컨텐츠를 교체하고 브라우저에서와 같이 뒤로 탐색 히스토리를 추적하는 호스트 단편을 사용하십시오.

유스 케이스가 고정 된 양의 탭으로 구성되어 있으므로 솔루션이 잘 작동합니다 .ViewPager를 사용자 정의 클래스의 인스턴스로 채우는 것이 HostFragment호스팅 된 컨텐츠를 대체하고 자체 탐색 기록을 유지할 수 있습니다. 호스팅 된 조각을 바꾸려면 메소드를 호출하십시오 hostfragment.replaceFragment().

public void replaceFragment(Fragment fragment, boolean addToBackstack) {
    if (addToBackstack) {
        getChildFragmentManager().beginTransaction().replace(R.id.hosted_fragment, fragment).addToBackStack(null).commit();
    } else {
        getChildFragmentManager().beginTransaction().replace(R.id.hosted_fragment, fragment).commit();
    }
}

그 방법은 프레임 레이아웃을 id로 바꾸고 그 방법으로 R.id.hosted_fragment제공된 조각을 사용하는 것입니다.

자세한 내용과 GitHub에 대한 완전한 작업 예제는 이 주제에 대한 튜토리얼을 확인하십시오 !


이것이 더 복잡해 보이지만 실제로는 구조와 유지 관리가 쉬운 가장 좋은 방법이라고 생각합니다.
시라 람

나는 NPE를 끝내고 있습니다. : / 교체 될보기를 찾을 수 없음
Fariz Fakkel

6

제시된 솔루션 중 일부는 문제를 부분적으로 해결하는 데 많은 도움이되었지만 여전히 일부 경우 단편 콘텐츠 대신 예기치 않은 예외와 블랙 페이지 콘텐츠를 생성 한 솔루션에서 누락 된 중요한 사항이 하나 있습니다.

문제는이다 FragmentPagerAdapter의 클래스에 캐시 조각을 저장하는 항목 ID를 사용 FragmentManager . 이러한 이유로, 당신은 또한 오버라이드 (override) 할 필요가 주는 getItemId (INT 위치) 은 예를 들어 반환하도록 방법을 위치 최상위 페이지와에 대한 100 + 위치 정보 페이지를. 그렇지 않으면 이전에 생성 된 최상위 조각이 세부 수준 조각 대신 캐시에서 반환됩니다.

또한 ViewPager를 사용하여 Fragment 페이지로 탭과 같은 활동을 구현하는 방법 과 RadioGroup 을 사용하여 탭 페이지를 세부 페이지로 대체하고 뒤로 버튼을 지원 하는 탭 예제를 공유 합니다. 이 구현은 한 수준의 백 스태킹 (항목 목록-항목 세부 사항) 만 지원하지만 다중 레벨 백 스태킹 구현은 간단합니다. 이 예제는 예를 들어 두 번째 페이지로 전환하고 첫 번째 페이지의 조각을 변경하고 (보이지 않는 동안) 첫 번째 페이지로 돌아 오는 경우를 제외하고는 NullPointerException 을 발생 시키는 것을 제외하고는 정상적인 경우에 아주 잘 작동 합니다. 알아 내면이 문제에 대한 해결책을 게시 할 것입니다.

public class TabsActivity extends FragmentActivity {

  public static final int PAGE_COUNT = 3;
  public static final int FIRST_PAGE = 0;
  public static final int SECOND_PAGE = 1;
  public static final int THIRD_PAGE = 2;

  /**
   * Opens a new inferior page at specified tab position and adds the current page into back
   * stack.
   */
  public void startPage(int position, Fragment content) {
    // Replace page adapter fragment at position.
    mPagerAdapter.start(position, content);
  }

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Initialize basic layout.
    this.setContentView(R.layout.tabs_activity);

    // Add tab fragments to view pager.
    {
      // Create fragments adapter.
      mPagerAdapter = new PagerAdapter(pager);
      ViewPager pager = (ViewPager) super.findViewById(R.id.tabs_view_pager);
      pager.setAdapter(mPagerAdapter);

      // Update active tab in tab bar when page changes.
      pager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int index, float value, int nextIndex) {
          // Not used.
        }

        @Override
        public void onPageSelected(int index) {
          RadioGroup tabs_radio_group = (RadioGroup) TabsActivity.this.findViewById(
            R.id.tabs_radio_group);
          switch (index) {
            case 0: {
              tabs_radio_group.check(R.id.first_radio_button);
            }
            break;
            case 1: {
              tabs_radio_group.check(R.id.second_radio_button);
            }
            break;
            case 2: {
              tabs_radio_group.check(R.id.third_radio_button);
            }
            break;
          }
        }

        @Override
        public void onPageScrollStateChanged(int index) {
          // Not used.
        }
      });
    }

    // Set "tabs" radio group on checked change listener that changes the displayed page.
    RadioGroup radio_group = (RadioGroup) this.findViewById(R.id.tabs_radio_group);
    radio_group.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
      @Override
      public void onCheckedChanged(RadioGroup radioGroup, int id) {
        // Get view pager representing tabs.
        ViewPager view_pager = (ViewPager) TabsActivity.this.findViewById(R.id.tabs_view_pager);
        if (view_pager == null) {
          return;
        }

        // Change the active page.
        switch (id) {
          case R.id.first_radio_button: {
            view_pager.setCurrentItem(FIRST_PAGE);
          }
          break;
          case R.id.second_radio_button: {
            view_pager.setCurrentItem(SECOND_PAGE);
          }
          break;
          case R.id.third_radio_button: {
            view_pager.setCurrentItem(THIRD_PAGE);
          }
          break;
        }
      });
    }
  }

  @Override
  public void onBackPressed() {
    if (!mPagerAdapter.back()) {
      super.onBackPressed();
    }
  }

  /**
   * Serves the fragments when paging.
   */
  private class PagerAdapter extends FragmentPagerAdapter {

    public PagerAdapter(ViewPager container) {
      super(TabsActivity.this.getSupportFragmentManager());

      mContainer = container;
      mFragmentManager = TabsActivity.this.getSupportFragmentManager();

      // Prepare "empty" list of fragments.
      mFragments = new ArrayList<Fragment>(){};
      mBackFragments = new ArrayList<Fragment>(){};
      for (int i = 0; i < PAGE_COUNT; i++) {
        mFragments.add(null);
        mBackFragments.add(null);
      }
    }

    /**
     * Replaces the view pager fragment at specified position.
     */
    public void replace(int position, Fragment fragment) {
      // Get currently active fragment.
      Fragment old_fragment = mFragments.get(position);
      if (old_fragment == null) {
        return;
      }

      // Replace the fragment using transaction and in underlaying array list.
      // NOTE .addToBackStack(null) doesn't work
      this.startUpdate(mContainer);
      mFragmentManager.beginTransaction().setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
        .remove(old_fragment).add(mContainer.getId(), fragment)
        .commit();
      mFragments.set(position, fragment);
      this.notifyDataSetChanged();
      this.finishUpdate(mContainer);
    }

    /**
     * Replaces the fragment at specified position and stores the current fragment to back stack
     * so it can be restored by #back().
     */
    public void start(int position, Fragment fragment) {
      // Remember current fragment.
      mBackFragments.set(position, mFragments.get(position));

      // Replace the displayed fragment.
      this.replace(position, fragment);
    }

    /**
     * Replaces the current fragment by fragment stored in back stack. Does nothing and returns
     * false if no fragment is back-stacked.
     */
    public boolean back() {
      int position = mContainer.getCurrentItem();
      Fragment fragment = mBackFragments.get(position);
      if (fragment == null) {
        // Nothing to go back.
        return false;
      }

      // Restore the remembered fragment and remove it from back fragments.
      this.replace(position, fragment);
      mBackFragments.set(position, null);
      return true;
    }

    /**
     * Returns fragment of a page at specified position.
     */
    @Override
    public Fragment getItem(int position) {
      // If fragment not yet initialized, create its instance.
      if (mFragments.get(position) == null) {
        switch (position) {
          case FIRST_PAGE: {
            mFragments.set(FIRST_PAGE, new DefaultFirstFragment());
          }
          break;
          case SECOND_PAGE: {
            mFragments.set(SECOND_PAGE, new DefaultSecondFragment());
          }
          break;
          case THIRD_PAGE: {
            mFragments.set(THIRD_PAGE, new DefaultThirdFragment());
          }
          break;
        }
      }

      // Return fragment instance at requested position.
      return mFragments.get(position);
    }

    /**
     * Custom item ID resolution. Needed for proper page fragment caching.
     * @see FragmentPagerAdapter#getItemId(int).
     */
    @Override
    public long getItemId(int position) {
      // Fragments from second level page hierarchy have their ID raised above 100. This is
      // important to FragmentPagerAdapter because it is caching fragments to FragmentManager with
      // this item ID key.
      Fragment item = mFragments.get(position);
      if (item != null) {
        if ((item instanceof NewFirstFragment) || (item instanceof NewSecondFragment) ||
          (item instanceof NewThirdFragment)) {
          return 100 + position;
        }
      }

      return position;
    }

    /**
     * Returns number of pages.
     */
    @Override
    public int getCount() {
      return mFragments.size();
    }

    @Override
    public int getItemPosition(Object object)
    {
      int position = POSITION_UNCHANGED;
      if ((object instanceof DefaultFirstFragment) || (object instanceof NewFirstFragment)) {
        if (object.getClass() != mFragments.get(FIRST_PAGE).getClass()) {
          position = POSITION_NONE;
        }
      }
      if ((object instanceof DefaultSecondragment) || (object instanceof NewSecondFragment)) {
        if (object.getClass() != mFragments.get(SECOND_PAGE).getClass()) {
          position = POSITION_NONE;
        }
      }
      if ((object instanceof DefaultThirdFragment) || (object instanceof NewThirdFragment)) {
        if (object.getClass() != mFragments.get(THIRD_PAGE).getClass()) {
          position = POSITION_NONE;
        }
      }
      return position;
    }

    private ViewPager mContainer;
    private FragmentManager mFragmentManager;

    /**
     * List of page fragments.
     */
    private List<Fragment> mFragments;

    /**
     * List of page fragments to return to in onBack();
     */
    private List<Fragment> mBackFragments;
  }

  /**
   * Tab fragments adapter.
   */
  private PagerAdapter mPagerAdapter;
}

흠,이 코드에는 또 다른 문제가 있습니다. 제거되고 다시 추가 된 조각에서 활동을 시작하려고하면 "실패 저장 상태 : 활성 NewFirstFragment {405478a0}이 (가) 색인 : -1을 지 웠습니다"라는 오류가 표시됩니다 (풀 스택 추적 : nopaste.info/547a23dfea.html ). 안드로이드 소스를 살펴보면 백 스택과 관련이 있지만 지금까지 고치는 방법을 모르겠습니다. 누구나 단서가 있습니까?
Blackhex

2
<pre> mFragmentManager.beginTransaction (). setTransition (FragmentTransaction.TRANSIT_FRAGMENT_OPEN) 대신 <code> mFragmentManager.beginTransaction (). detach (old_fragment) .attach (fragment) .commit (); </ code> 대신 Cool 위의 예에서 old_fragment) .add (mContainer.getId (), fragment) .commit (); </ pre>는 두 가지 문제를 모두 해결하는 것으로 보입니다 :-).
Blackhex

1
이 방법을 사용하는 경우 기기를 회전 할 때 Android에서 액티비티를 다시 만들지 않도록하십시오. 그렇지 않으면 PagerAdapter가 다시 만들어지고 mBackFragments가 손실됩니다. 이것은 또한 FragmentManager를 심각하게 혼동합니다. BackStack을 사용하면 PagerAdapter를 더 복잡하게 구현하여이 문제를 피할 수 있습니다 (즉, FragmentPagerAdapter에서 상속 할 수 없음)
Norman

6

인덱스 2와 3에 대해 3 개의 요소와 2 개의 하위 요소가있는 ViewPager를 만들었습니다.

여기에 이미지 설명을 입력하십시오

StackOverFlow의 이전 질문과 답변의 도움으로 이것을 구현했으며 여기에 링크가 있습니다.

ViewPagerChildFragments


장치를 회전하면 프로젝트가 실패합니다.
도리

6

돌며 조각을 교체하려면 ViewPager소스의 코드로 이동 할 수 있습니다 ViewPager, PagerAdapter그리고 FragmentStatePagerAdapter당신의 프로젝트에 클래스를 코드에 다음을 추가.

ViewPager:

public void notifyItemChanged(Object oldItem, Object newItem) {
    if (mItems != null) {
            for (ItemInfo itemInfo : mItems) {
                        if (itemInfo.object.equals(oldItem)) {
                                itemInfo.object = newItem;
                        }
                    }
       }
       invalidate();
    }

FragmentStatePagerAdapter로 :

public void replaceFragmetns(ViewPager container, Fragment oldFragment, Fragment newFragment) {
       startUpdate(container);

       // remove old fragment

       if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
       int position = getFragmentPosition(oldFragment);
        while (mSavedState.size() <= position) {
            mSavedState.add(null);
        }
        mSavedState.set(position, null);
        mFragments.set(position, null);

        mCurTransaction.remove(oldFragment);

        // add new fragment

        while (mFragments.size() <= position) {
            mFragments.add(null);
        }
        mFragments.set(position, newFragment);
        mCurTransaction.add(container.getId(), newFragment);

       finishUpdate(container);

       // ensure getItem returns newFragemtn after calling handleGetItemInbalidated()
       handleGetItemInbalidated(container, oldFragment, newFragment);

       container.notifyItemChanged(oldFragment, newFragment);
    }

protected abstract void handleGetItemInbalidated(View container, Fragment oldFragment, Fragment newFragment);
protected abstract int  getFragmentPosition(Fragment fragment);

handleGetItemInvalidated()다음에 호출 getItem()할 때 newFragment getFragmentPosition()가 어댑터에서 프래그먼트의 위치를 ​​리턴하도록합니다.

이제 조각 호출을 대체하려면

mAdapter.replaceFragmetns(mViewPager, oldFragment, newFragment);

예제 프로젝트에 관심이 있다면 소스를 요청하십시오.


getFragmentPosition () 및 handleGetItemInbalidated () 메소드는 어떻게 유사해야합니까? NewFragment를 반환하도록 getItem ()을 업데이트 할 수 없습니다. 도와주세요 ..

안녕하세요! 이 링크에 테스트 프로젝트를 검색 할 files.mail.ru/eng?back=%2FEZU6G4을
AndroidTeam에서 Mail.Ru에게

1
장치를 회전 시키면 테스트 프로젝트가 실패합니다.
seb

1
@ AndroidTeamAtMail.Ru 링크가 만료 된 것 같습니다. 코드를 공유하십시오
Sunny

내 대답을 볼 수 있습니다 . 조각을 대체하고 첫 번째 조각으로 돌아갑니다.
Lyd

4

AndroidTeam의 솔루션으로 훌륭하게 작동하지만, 다시 돌아가는 기능이 필요하다는 것을 알았습니다. 그러나 이것을 FrgmentTransaction.addToBackStack(null) 추가하면 ViewPager에 알리지 않고 조각 만 교체됩니다. 제공된 솔루션과이 작은 향상 기능을 결합하면 단순히 활동의 onBackPressed()방법을 재정 의하여 이전 상태로 돌아갈 수 있습니다 . 가장 큰 단점은 한 번에 한 번만 되돌아 가서 여러 번의 클릭이 발생할 수 있다는 것입니다

private ArrayList<Fragment> bFragments = new ArrayList<Fragment>();
private ArrayList<Integer> bPosition = new ArrayList<Integer>();

public void replaceFragmentsWithBackOut(ViewPager container, Fragment oldFragment, Fragment newFragment) {
    startUpdate(container);

    // remove old fragment

    if (mCurTransaction == null) {
         mCurTransaction = mFragmentManager.beginTransaction();
     }
    int position = getFragmentPosition(oldFragment);
     while (mSavedState.size() <= position) {
         mSavedState.add(null);
     }

     //Add Fragment to Back List
     bFragments.add(oldFragment);

     //Add Pager Position to Back List
     bPosition.add(position);

     mSavedState.set(position, null);
     mFragments.set(position, null);

     mCurTransaction.remove(oldFragment);

     // add new fragment

     while (mFragments.size() <= position) {
         mFragments.add(null);
     }
     mFragments.set(position, newFragment);
     mCurTransaction.add(container.getId(), newFragment);

    finishUpdate(container);

    // ensure getItem returns newFragemtn after calling handleGetItemInbalidated()
    handleGetItemInvalidated(container, oldFragment, newFragment);

    container.notifyItemChanged(oldFragment, newFragment);
 }


public boolean popBackImmediate(ViewPager container){
    int bFragSize = bFragments.size();
    int bPosSize = bPosition.size();

    if(bFragSize>0 && bPosSize>0){
        if(bFragSize==bPosSize){
            int last = bFragSize-1;
            int position = bPosition.get(last);

            //Returns Fragment Currently at this position
            Fragment replacedFragment = mFragments.get(position);               
            Fragment originalFragment = bFragments.get(last);

            this.replaceFragments(container, replacedFragment, originalFragment);

            bPosition.remove(last);
            bFragments.remove(last);

            return true;
        }
    }

    return false;       
}

이것이 누군가를 돕기를 바랍니다.

또한 지금까지는 getFragmentPosition()거의 getItem()반대입니다. 어느 프래그먼트가 어디로 갈지 알고 있습니다. 올바른 위치로 돌아와야합니다. 예를 들면 다음과 같습니다.

    @Override
    protected int getFragmentPosition(Fragment fragment) {
            if(fragment.equals(originalFragment1)){
                return 0;
            }
            if(fragment.equals(replacementFragment1)){
                return 0;
            }
            if(fragment.equals(Fragment2)){
                return 1;
            }
        return -1;
    }

3

귀하의 onCreateView방법에서 container실제로는 ViewPager인스턴스입니다.

그래서 그냥 전화

ViewPager vpViewPager = (ViewPager) container;
vpViewPager.setCurrentItem(1);

의 현재 조각이 변경됩니다 ViewPager.


1
몇 시간 동안 검색 한 결과 모든 탭이 올바르게 작동하는 데 필요한 한 줄의 코드였습니다. vpViewPager.setCurrentItem(1);. 나는 마지막으로 당신에게 도달 할 때까지 매번 아무 일도 일어나지 않고 각 사람의 모범을 겪었습니다. 감사합니다.
Brandon

1
마음이 날아 갔다 ... 이것은 훌륭합니다! 컨테이너 변수가 viewpager 자체라는 것을 알지 못했습니다. 이 답변은 다른 모든 긴 답변보다 먼저 탐색해야합니다. 또한 리스너 변수가 프래그먼트 생성자로 전달되지 않으므로 구성 변경시 Android에서 인수없는 기본 생성자를 사용하여 프래그먼트를 자동으로 다시 생성 할 때 모든 구현을 단순화합니다.
Kevin Lee

3
기존 탭으로 변경되지 않습니까? 이것은 프래그먼트 교체와 어떤 관련이 있습니까?
behelit

2
@behelit에 동의하면 질문에 대답하지 않고 ViewPager다른 페이지 로 이동하며 조각을 대체하지 않습니다.
Yoann Hercouet

2

이 문제에 대한 비교적 간단한 해결책이 있습니다. 이 솔루션의 핵심 은 전자가 FragmentStatePagerAdapter대신 FragmentPagerAdapter사용하지 않는 프래그먼트를 제거하고 후자는 여전히 인스턴스를 유지 하므로 대신 사용하는 것입니다 . 두 번째는 POSITION_NONEgetItem () 의 사용입니다 . 간단한 List를 사용하여 조각을 추적했습니다. 필자의 요구 사항은 전체 조각 목록을 한 번에 새 목록으로 바꾸는 것이었지만 개별 조각을 대체하기 위해 아래를 쉽게 수정할 수 있습니다.

public class MyFragmentAdapter extends FragmentStatePagerAdapter {
    private List<Fragment> fragmentList = new ArrayList<Fragment>();
    private List<String> tabTitleList = new ArrayList<String>();

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

    public void addFragments(List<Fragment> fragments, List<String> titles) {
        fragmentList.clear();
        tabTitleList.clear();
        fragmentList.addAll(fragments);
        tabTitleList.addAll(titles);
        notifyDataSetChanged();
    }

    @Override
    public int getItemPosition(Object object) {
        if (fragmentList.contains(object)) {
            return POSITION_UNCHANGED;
        }
        return POSITION_NONE;
    }

    @Override
    public Fragment getItem(int item) {
        if (item >= fragmentList.size()) {
            return null;
        }
        return fragmentList.get(item);
    }

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

    @Override
    public CharSequence getPageTitle(int position) {
        return tabTitleList.get(position);
    }
}

2

또한 Stacks 와 함께 작동하는 솔루션을 만들었습니다 . 더 모듈 방식이므로 u에 각 Fragment 및 Detail Fragment를 지정할 필요가 없습니다 FragmentPagerAdapter. ActionbarSherlock의 예제 위에 빌드되어 Google 데모 앱에서 올바른지 여부를 알 수 있습니다.

/**
 * This is a helper class that implements the management of tabs and all
 * details of connecting a ViewPager with associated TabHost.  It relies on a
 * trick.  Normally a tab host has a simple API for supplying a View or
 * Intent that each tab will show.  This is not sufficient for switching
 * between pages.  So instead we make the content part of the tab host
 * 0dp high (it is not shown) and the TabsAdapter supplies its own dummy
 * view to show as the tab content.  It listens to changes in tabs, and takes
 * care of switch to the correct paged in the ViewPager whenever the selected
 * tab changes.
 * 
 * Changed to support more Layers of fragments on each Tab.
 * by sebnapi (2012)
 * 
 */
public class TabsAdapter extends FragmentPagerAdapter
        implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener {
    private final Context mContext;
    private final TabHost mTabHost;
    private final ViewPager mViewPager;

    private ArrayList<String> mTabTags = new ArrayList<String>();
    private HashMap<String, Stack<TabInfo>> mTabStackMap = new HashMap<String, Stack<TabInfo>>();

    static final class TabInfo {
        public final String tag;
        public final Class<?> clss;
        public Bundle args;

        TabInfo(String _tag, Class<?> _class, Bundle _args) {
            tag = _tag;
            clss = _class;
            args = _args;
        }
    }

    static class DummyTabFactory implements TabHost.TabContentFactory {
        private final Context mContext;

        public DummyTabFactory(Context context) {
            mContext = context;
        }

        @Override
        public View createTabContent(String tag) {
            View v = new View(mContext);
            v.setMinimumWidth(0);
            v.setMinimumHeight(0);
            return v;
        }
    }

    public interface SaveStateBundle{
        public Bundle onRemoveFragment(Bundle outState);
    }

    public TabsAdapter(FragmentActivity activity, TabHost tabHost, ViewPager pager) {
        super(activity.getSupportFragmentManager());
        mContext = activity;
        mTabHost = tabHost;
        mViewPager = pager;
        mTabHost.setOnTabChangedListener(this);
        mViewPager.setAdapter(this);
        mViewPager.setOnPageChangeListener(this);
    }

    /**
     * Add a Tab which will have Fragment Stack. Add Fragments on this Stack by using
     * addFragment(FragmentManager fm, String _tag, Class<?> _class, Bundle _args)
     * The Stack will hold always the default Fragment u add here.
     * 
     * DON'T ADD Tabs with same tag, it's not beeing checked and results in unexpected
     * beahvior.
     * 
     * @param tabSpec
     * @param clss
     * @param args
     */
    public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args){
        Stack<TabInfo> tabStack = new Stack<TabInfo>();

        tabSpec.setContent(new DummyTabFactory(mContext));
        mTabHost.addTab(tabSpec);
        String tag = tabSpec.getTag();
        TabInfo info = new TabInfo(tag, clss, args);

        mTabTags.add(tag);                  // to know the position of the tab tag 
        tabStack.add(info);
        mTabStackMap.put(tag, tabStack);
        notifyDataSetChanged();
    }

    /**
     * Will add the Fragment to Tab with the Tag _tag. Provide the Class of the Fragment
     * it will be instantiated by this object. Proivde _args for your Fragment.
     * 
     * @param fm
     * @param _tag
     * @param _class
     * @param _args
     */
    public void addFragment(FragmentManager fm, String _tag, Class<?> _class, Bundle _args){
        TabInfo info = new TabInfo(_tag, _class, _args);
        Stack<TabInfo> tabStack = mTabStackMap.get(_tag);   
        Fragment frag = fm.findFragmentByTag("android:switcher:" + mViewPager.getId() + ":" + mTabTags.indexOf(_tag));
        if(frag instanceof SaveStateBundle){
            Bundle b = new Bundle();
            ((SaveStateBundle) frag).onRemoveFragment(b);
            tabStack.peek().args = b;
        }
        tabStack.add(info);
        FragmentTransaction ft = fm.beginTransaction();
        ft.remove(frag).commit();
        notifyDataSetChanged();
    }

    /**
     * Will pop the Fragment added to the Tab with the Tag _tag
     * 
     * @param fm
     * @param _tag
     * @return
     */
    public boolean popFragment(FragmentManager fm, String _tag){
        Stack<TabInfo> tabStack = mTabStackMap.get(_tag);   
        if(tabStack.size()>1){
            tabStack.pop();
            Fragment frag = fm.findFragmentByTag("android:switcher:" + mViewPager.getId() + ":" + mTabTags.indexOf(_tag));
            FragmentTransaction ft = fm.beginTransaction();
            ft.remove(frag).commit();
            notifyDataSetChanged();
            return true;
        }
        return false;
    }

    public boolean back(FragmentManager fm) {
        int position = mViewPager.getCurrentItem();
        return popFragment(fm, mTabTags.get(position));
    }

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

    @Override
    public int getItemPosition(Object object) {
        ArrayList<Class<?>> positionNoneHack = new ArrayList<Class<?>>();
        for(Stack<TabInfo> tabStack: mTabStackMap.values()){
            positionNoneHack.add(tabStack.peek().clss);
        }   // if the object class lies on top of our stacks, we return default
        if(positionNoneHack.contains(object.getClass())){
            return POSITION_UNCHANGED;
        }
        return POSITION_NONE;
    }

    @Override
    public Fragment getItem(int position) {
        Stack<TabInfo> tabStack = mTabStackMap.get(mTabTags.get(position));
        TabInfo info = tabStack.peek();
        return Fragment.instantiate(mContext, info.clss.getName(), info.args);
    }

    @Override
    public void onTabChanged(String tabId) {
        int position = mTabHost.getCurrentTab();
        mViewPager.setCurrentItem(position);
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    }

    @Override
    public void onPageSelected(int position) {
        // Unfortunately when TabHost changes the current tab, it kindly
        // also takes care of putting focus on it when not in touch mode.
        // The jerk.
        // This hack tries to prevent this from pulling focus out of our
        // ViewPager.
        TabWidget widget = mTabHost.getTabWidget();
        int oldFocusability = widget.getDescendantFocusability();
        widget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
        mTabHost.setCurrentTab(position);
        widget.setDescendantFocusability(oldFocusability);
    }

    @Override
    public void onPageScrollStateChanged(int state) {
    }

}

MainActivity에서 뒤로 버튼 기능을 위해 이것을 추가하십시오 .

@Override
public void onBackPressed() {
  if (!mTabsAdapter.back(getSupportFragmentManager())) {
    super.onBackPressed();
  }
}

제거 할 때 조각 상태저장하고 싶다면 . 프래그먼트 SaveStateBundle가 함수에 저장 상태의 번들을 반환 하여 인터페이스를 구현하도록 하십시오. 로 인스턴스화 후 번들을 가져옵니다 this.getArguments().

다음 과 같이 탭을 인스턴스화 할 수 있습니다 .

mTabsAdapter.addTab(mTabHost.newTabSpec("firstTabTag").setIndicator("First Tab Title"),
                FirstFragmentActivity.FirstFragmentFragment.class, null);

탭 스택 위에 조각을 추가하려는 경우 비슷하게 작동합니다. 중요 : 두 탭 위에 동일한 클래스의 인스턴스 2 개를 원하면 작동하지 않습니다. 이 솔루션을 빠르게 함께 사용했기 때문에 경험이 없어도 공유 할 수 있습니다.


뒤로 버튼을 누를 때 nullpointer 예외가 발생하고 교체 된 조각도 표시되지 않습니다. 어쩌면 내가 잘못하고있는 것일 수 있습니다 : 조각을 올바르게 변경하려면 올바른 호출은 무엇입니까?
Jeroen

업데이트 : 대부분의 문제가 해결되었지만 새 조각을 추가 한 후 뒤로 버튼을 누른 다음 다른 탭으로 이동하면 nullpointer 예외가 발생합니다. 그래서 나는 두 가지 질문을 가지고 있습니다 : 1 : 탭의 기록을 제거하고 최신 조각 (te stack의 맨 위) 만 여전히 존재할 수 있습니까? 2 : nullpointer 예외를 어떻게 제거 할 수 있습니까?
Jeroen

@Jeroen 1 : u는 mTabStackMap에서 올바른 스택을 얻을 수 있으며 스택 맨 아래에있는 모든 TabInfo 객체를 제거합니다 .2 : 말하기 매우 어렵습니다. 코드에서 실패하지 않았습니까? 직접 이웃 (오른쪽 또는 왼쪽)에있는 탭 (및 다시)으로 전환하면 이런 일이 발생합니까?
seb

좋아, 새로운 문제가 발생했습니다. 화면을 회전하면 스택이 다시 비어있는 것 같습니다. 그것은 이전 항목
Jeroen

1
감사합니다. 이 코드를 사용하고 있지만 중첩 조각이 가능하므로 더 이상 문제가되지 않습니다. :)
Garcon

2

viewpager에서 프래그먼트를 교체하는 것은 상당히 관련이 있지만 매우 가능하며 매우 매끄럽게 보일 수 있습니다. 먼저, viewpager 자체가 프래그먼트 제거 및 추가를 처리하도록해야합니다. SearchFragment 내부의 조각을 교체하면 viewpager가 조각보기를 유지합니다. 따라서 SearchFragment는 교체하려고 할 때 제거되기 때문에 빈 페이지로 끝납니다.

해결 방법은 viewpager 내부에서 리스너를 만들어 외부에서 변경 한 내용을 처리하므로 먼저이 코드를 어댑터 하단에 추가하십시오.

public interface nextFragmentListener {
    public void fragment0Changed(String newFragmentIdentification);
}

그런 다음 조각을 변경하려고 할 때 리스너가되는 viewpager에 개인 클래스를 만들어야합니다. 예를 들어 이와 같은 것을 추가 할 수 있습니다. 방금 작성된 인터페이스를 구현합니다. 따라서이 메소드를 호출 할 때마다 아래 클래스 내부에서 코드가 실행됩니다.

private final class fragmentChangeListener implements nextFragmentListener {


    @Override
    public void fragment0Changed(String fragment) {
        //I will explain the purpose of fragment0 in a moment
        fragment0 = fragment;
        manager.beginTransaction().remove(fragAt0).commit();

        switch (fragment){
            case "searchFragment":
                fragAt0 = SearchFragment.newInstance(listener);
                break;
            case "searchResultFragment":
                fragAt0 = Fragment_Table.newInstance(listener);
                break;
        }

        notifyDataSetChanged();
    }

여기서 지적해야 할 두 가지 주요 사항이 있습니다.

  1. fragAt0은 "유연한"단편입니다. 당신이 제공하는 조각 유형을 취할 수 있습니다. 이를 통해 위치 0의 조각을 원하는 조각으로 변경할 때 가장 친한 친구가 될 수 있습니다.
  2. 'newInstance (listener) 생성자에 배치 된 리스너를 확인하십시오. 다음은 fragment0Changed (String newFragmentIdentification)`호출 방법입니다. 다음 코드는 프래그먼트 안에 리스너를 만드는 방법을 보여줍니다.

    정적 nextFragmentListener listenerSearch;

        public static Fragment_Journals newInstance(nextFragmentListener listener){
                listenerSearch = listener;
                return new Fragment_Journals();
            }

당신은 내부의 변화를 호출 할 수 있습니다 onPostExecute

private class SearchAsyncTask extends AsyncTask<Void, Void, Void>{

    protected Void doInBackground(Void... params){
        .
        .//some more operation
        .
    }
    protected void onPostExecute(Void param){

        listenerSearch.fragment0Changed("searchResultFragment");
    }

}

그러면 viewpager 내부의 코드가 fragAt0 위치에서 조각을 새 searchResultFragment로 전환하도록 트리거합니다. 뷰 페이지에 기능을 추가하기 전에 추가해야 할 작은 조각이 두 개 더 있습니다.

하나는 viewpager의 getItem override 메소드에 있습니다.

@Override
public Fragment getItem(int index) {

    switch (index) {
    case 0:
        //this is where it will "remember" which fragment you have just selected. the key is to set a static String fragment at the top of your page that will hold the position that you had just selected.  

        if(fragAt0 == null){

            switch(fragment0){
            case "searchFragment":
                fragAt0 = FragmentSearch.newInstance(listener);
                break;
            case "searchResultsFragment":
                fragAt0 = FragmentSearchResults.newInstance(listener);
                break;
            }
        }
        return fragAt0;
    case 1:
        // Games fragment activity
        return new CreateFragment();

    }

이제이 마지막 조각이 없으면 여전히 빈 페이지가 나타납니다. 절름발이이지만 viewPager의 필수 부분입니다. viewpager의 getItemPosition 메소드를 대체해야합니다. 일반적으로이 메소드는 POSITION_UNCHANGED를 반환합니다. POSITION_UNCHANGED는 viewpager에게 모든 것을 동일하게 유지하도록 지시하므로 getItem은 페이지에 새 조각을 배치하기 위해 호출되지 않습니다. 다음은 할 수있는 일의 예입니다.

public int getItemPosition(Object object)
{
    //object is the current fragment displayed at position 0.  
    if(object instanceof SearchFragment && fragAt0 instanceof SearchResultFragment){
        return POSITION_NONE;
    //this condition is for when you press back
    }else if{(object instanceof SearchResultFragment && fragAt0 instanceof SearchFragment){
        return POSITION_NONE;
    }
        return POSITION_UNCHANGED
}

내가 말했듯이 코드는 매우 복잡하지만 기본적으로 상황에 맞는 사용자 지정 어댑터를 만들어야합니다. 내가 언급 한 것은 조각을 변경할 수있게합니다. 모든 것을 담그는 데 오랜 시간이 걸릴 수 있으므로 인내심을 가지 겠지만 모두 이해가 될 것입니다. 정말 매끄럽게 보이는 응용 프로그램을 만들 수 있기 때문에 시간이 걸릴 가치가 있습니다.

뒤로 버튼을 다루기위한 너겟입니다. 이것을 MainActivity 안에 넣습니다.

 public void onBackPressed() {
    if(mViewPager.getCurrentItem() == 0) {
        if(pagerAdapter.getItem(0) instanceof FragmentSearchResults){
            ((Fragment_Table) pagerAdapter.getItem(0)).backPressed();
        }else if (pagerAdapter.getItem(0) instanceof FragmentSearch) {
            finish();
        }
    }

fragment0changed를 호출하는 FragmentSearchResults 내에 backPressed ()라는 메소드를 작성해야합니다. 이것은 이전에 보여준 코드와 함께 뒤로 버튼을 누르는 것을 처리합니다. viewpager를 변경하는 코드로 행운을 빕니다. 많은 작업이 필요하며 내가 찾은 한 빠른 적응이 없습니다. 내가 말했듯이 기본적으로 사용자 정의 viewpager 어댑터를 만들고 리스너를 사용하여 필요한 모든 변경 사항을 처리하도록합니다.


이 솔루션에 감사드립니다. 그러나 화면 회전 후 문제가 있습니까? 화면을 회전하고 동일한 탭에서 다른 조각을 다시로드하려고하면 앱이 충돌합니다.
AnteGemini

안드로이드 개발을 한 이후로 오랜 시간이 지났지 만 내 앱은 더 이상 신뢰할 수없는 데이터가 있다고 생각합니다. 당신은 전체 활동 다시 시작을 회전하고 모든없이 당신이 (onPause, onResume에서 번들에서 말을) 필요 데이터를 이전 상태를 다시로드하려고하면 충돌이 발생할 수있는 경우
user3331142

2

이것이 나의 목표입니다.

우선 버튼 클릭 이벤트 를 구현하려는 Root_fragment내부 viewPager탭을 추가 하십시오 fragment. 예;

@Override
public Fragment getItem(int position) {
  if(position==0)
      return RootTabFragment.newInstance();
  else
      return SecondPagerFragment.newInstance();
}

우선 조각 변경에 RootTabFragment포함시켜야합니다 FragmentLayout.

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:id="@+id/root_frame"
   android:layout_width="match_parent"
   android:layout_height="match_parent">
</FrameLayout>

그런 다음, 내부 RootTabFragment onCreateView구현 fragmentChange을 위해FirstPagerFragment

getChildFragmentManager().beginTransaction().replace(R.id.root_frame, FirstPagerFragment.newInstance()).commit();

그런 onClick다음 버튼 내부의 이벤트를 구현 FirstPagerFragment하고 조각을 다시 변경하십시오.

getChildFragmentManager().beginTransaction().replace(R.id.root_frame, NextFragment.newInstance()).commit();

이것이 당신에게 도움이되기를 바랍니다.


1

중간에 새 조각을 추가하거나 현재 조각을 바꾸려는 경우에도 잘 작동하는 간단한 솔루션을 찾았습니다. 내 솔루션에서는 재정의해야합니다.getItemId() 에서 각 조각에 대해 고유 ID를 반환 해야합니다. 기본적으로 배치되지 않습니다.

있습니다 :

public class DynamicPagerAdapter extends FragmentPagerAdapter {

private ArrayList<Page> mPages = new ArrayList<Page>();
private ArrayList<Fragment> mFragments = new ArrayList<Fragment>();

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

public void replacePage(int position, Page page) {
    mPages.set(position, page);
    notifyDataSetChanged();
}

public void setPages(ArrayList<Page> pages) {
    mPages = pages;
    notifyDataSetChanged();
}

@Override
public Fragment getItem(int position) {
    if (mPages.get(position).mPageType == PageType.FIRST) {
        return FirstFragment.newInstance(mPages.get(position));
    } else {
        return SecondFragment.newInstance(mPages.get(position));
    }
}

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

@Override
public long getItemId(int position) {
    // return unique id
    return mPages.get(position).getId();
}

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Fragment fragment = (Fragment) super.instantiateItem(container, position);
    while (mFragments.size() <= position) {
        mFragments.add(null);
    }
    mFragments.set(position, fragment);
    return fragment;
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    super.destroyItem(container, position, object);
    mFragments.set(position, null);
}

@Override
public int getItemPosition(Object object) {
    PagerFragment pagerFragment = (PagerFragment) object;
    Page page = pagerFragment.getPage();
    int position = mFragments.indexOf(pagerFragment);
    if (page.equals(mPages.get(position))) {
        return POSITION_UNCHANGED;
    } else {
        return POSITION_NONE;
    }
}
}

주의 사항 :이 예에서 FirstFragmentSecondFragment메소드가 추상 클래스 PageFragment를 확장 getPage().


1

나는 wize와 비슷한 것을하고 있지만 내 대답에 따르면 언제든지 두 조각 사이를 바꿀 수 있습니다. Wize 답변을 사용하면 화면 방향을 변경할 때 몇 가지 문제가 있습니다. 이것은 PagerAdapter의 모습입니다 :

    public class MyAdapter extends FragmentPagerAdapter
{
    static final int NUM_ITEMS = 2;
    private final FragmentManager mFragmentManager;
    private Fragment mFragmentAtPos0;
     private Map<Integer, String> mFragmentTags;
     private boolean isNextFragment=false;

    public MyAdapter(FragmentManager fm)
    {
        super(fm);
        mFragmentManager = fm;
         mFragmentTags = new HashMap<Integer, String>();
    }

    @Override
    public Fragment getItem(int position)
    {
        if (position == 0)
        {


            if (isPager) {
                mFragmentAtPos0 = new FirstPageFragment();
            } else {
                mFragmentAtPos0 = new NextFragment();
            }
            return mFragmentAtPos0;
        }
        else
            return SecondPageFragment.newInstance();
    }

    @Override
    public int getCount()
    {
        return NUM_ITEMS;
    }


 @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Object obj = super.instantiateItem(container, position);
        if (obj instanceof Fragment) {
            // record the fragment tag here.
            Fragment f = (Fragment) obj;
            String tag = f.getTag();
            mFragmentTags.put(position, tag);
        }
        return obj;
    }


    public void onChange(boolean isNextFragment) {

        if (mFragmentAtPos0 == null)
            mFragmentAtPos0 = getFragment(0);
        if (mFragmentAtPos0 != null)
            mFragmentManager.beginTransaction().remove(mFragmentAtPos0).commit();


        if (!isNextFragment) {
            mFragmentAtFlashcards = new FirstPageFragment();
        } else {
            mFragmentAtFlashcards = new NextFragment();
        }

        notifyDataSetChanged();


    }


    @Override
    public int getItemPosition(Object object)
    {
        if (object instanceof FirstPageFragment && mFragmentAtPos0 instanceof NextFragment)
            return POSITION_NONE;
         if (object instanceof NextFragment && mFragmentAtPos0 instanceof FirstPageFragment)
            return POSITION_NONE;
        return POSITION_UNCHANGED;
    }


    public Fragment getFragment(int position) {
        String tag = mFragmentTags.get(position);
        if (tag == null)
            return null;
        return mFragmentManager.findFragmentByTag(tag);
    }
}

어댑터 컨테이너 활동에서 구현할 리스너를 첨부 할 때 프래그먼트에 넣습니다.

    public class PagerContainerActivity extends AppCompatActivity implements ChangeFragmentListener {

//...

  @Override
    public void onChange(boolean isNextFragment) {
        if (pagerAdapter != null)
            pagerAdapter.onChange(isNextFragment);


    }

//...
}

그런 다음 조각에서 호출자를 첨부 할 때 리스너를 배치하십시오.

public class FirstPageFragment extends Fragment{


private ChangeFragmentListener changeFragmentListener;


//...
 @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        changeFragmentListener = ((PagerContainerActivity) activity);
    }

    @Override
    public void onDetach() {
        super.onDetach();
        changeFragmentListener = null;
    }
//...
//in the on click to change the fragment
changeFragmentListener.onChange(true);
//...
}

그리고 마지막으로 청취자 :

public interface changeFragmentListener {

    void onChange(boolean isNextFragment);

}

1

나는 @wize@mdelolmo 의 대답을 따랐 으며 해결책을 얻었습니다. 고마워 톤. 그러나 메모리 소비를 개선하기 위해 이러한 솔루션을 약간 조정했습니다.

내가 관찰 한 문제 :

Fragment교체 된 인스턴스를 저장합니다 . 내 경우에는 파편이 들어서 MapView비용이 많이 든다고 생각했습니다. 그래서 FragmentPagerPositionChanged (POSITION_NONE or POSITION_UNCHANGED)대신 Fragment자체를 유지하고 있습니다.

여기 내 구현이 있습니다.

  public static class DemoCollectionPagerAdapter extends FragmentStatePagerAdapter {

    private SwitchFragListener mSwitchFragListener;
    private Switch mToggle;
    private int pagerAdapterPosChanged = POSITION_UNCHANGED;
    private static final int TOGGLE_ENABLE_POS = 2;


    public DemoCollectionPagerAdapter(FragmentManager fm, Switch toggle) {
        super(fm);
        mToggle = toggle;

        mSwitchFragListener = new SwitchFragListener();
        mToggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                mSwitchFragListener.onSwitchToNextFragment();
            }
        });
    }

    @Override
    public Fragment getItem(int i) {
        switch (i)
        {
            case TOGGLE_ENABLE_POS:
                if(mToggle.isChecked())
                {
                    return TabReplaceFragment.getInstance();
                }else
                {
                    return DemoTab2Fragment.getInstance(i);
                }

            default:
                return DemoTabFragment.getInstance(i);
        }
    }

    @Override
    public int getCount() {
        return 5;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return "Tab " + (position + 1);
    }

    @Override
    public int getItemPosition(Object object) {

        //  This check make sures getItem() is called only for the required Fragment
        if (object instanceof TabReplaceFragment
                ||  object instanceof DemoTab2Fragment)
            return pagerAdapterPosChanged;

        return POSITION_UNCHANGED;
    }

    /**
     * Switch fragments Interface implementation
     */
    private final class SwitchFragListener implements
            SwitchFragInterface {

        SwitchFragListener() {}

        public void onSwitchToNextFragment() {

            pagerAdapterPosChanged = POSITION_NONE;
            notifyDataSetChanged();
        }
    }

    /**
     * Interface to switch frags
     */
    private interface SwitchFragInterface{
        void onSwitchToNextFragment();
    }
}

여기 데모 링크 .. https://youtu.be/l_62uhKkLyM

데모 목적으로 2 개의 프래그먼트 TabReplaceFragment와 2 DemoTab2Fragment번 위치를 사용했습니다 . 다른 모든 경우에는 DemoTabFragment인스턴스를 사용하고 있습니다.

설명:

SwitchActivity에서으로 전달 하고 DemoCollectionPagerAdapter있습니다. 이 스위치의 상태에 따라 올바른 조각이 표시됩니다. 스위치 검사가 변경되면 변수 의 값을로 변경하는 SwitchFragListeneronSwitchToNextFragment메소드를 호출합니다 . 에 대한 자세한 내용을 확인하십시오pagerAdapterPosChangedPOSITION_NONEPOSITION_NONE . 이렇게하면 getItem이 무효화되고 올바른 조각을 인스턴스화하는 논리가 있습니다. 설명이 약간 지저분하면 죄송합니다.

원래 아이디어에 대한 @wize와 @mdelolmo에게 다시 한번 감사드립니다.

이것이 도움이 되길 바랍니다. :)

이 구현에 결함이 있는지 알려주세요. 그것은 내 프로젝트에 큰 도움이 될 것입니다.


0

연구 후 짧은 코드로 솔루션을 찾았습니다. 우선 프래그먼트에서 퍼블릭 인스턴스를 만들고 방향 변경시 프래그먼트가 다시 생성되지 않으면 onSaveInstanceState에서 프래그먼트를 제거하십시오.

 @Override
public void onSaveInstanceState(Bundle outState) {
    if (null != mCalFragment) {
        FragmentTransaction bt = getChildFragmentManager().beginTransaction();
        bt.remove(mFragment);
        bt.commit();
    }
    super.onSaveInstanceState(outState);
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.