Android - 使用 FragmentStatePagerAdapter 的 BackStack 问题

Android - Problems with BackStack using FragmentStatePagerAdapter

我正在构建一个 Android 应用程序,它使用 FragmentStatePagerAdapter 用于选项卡式导航和每个选项卡中的动态内容。每个选项卡都有 Fragment 内容,用户输入时会替换内容(例如,第一个选项卡有一个 Fragment 包含书籍列表,点击后,您可以访问该书的详细信息,使用另一个 Fragment

显示

问题:我还没有找到正确处理 onBack 事件和 BackStack 的方法,所以当我查看任何书籍的详细信息时,我可以轻松地按后退按钮返回 - 我的意思是, 从 BackStack 弹出最后一个状态。

我怀疑的是:我切换 Fragment 对象的方式可能不是最好的方式,但除了后退按钮问题外,它工作正常。我怀疑 FragmentStatePagerAdapter 的适配器和 FragmentManager 自己的 Fragment 集合之间存在一些问题;可能这是我没有看到的简单解决方案。

未回答的问题(虽然不是很详细):Adding Fragment to BackStack using FragmentStatePagerAdapter

代码:

// MAIN ACTIVITY, 就这么简单。

public class MainActivity extends FragmentActivity {

    public static final String TAG = "MainActivity";

    // Whether the Log Fragment is currently shown
    private boolean mLogShown;

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

        if (savedInstanceState == null) {
            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
            MainTabSliderFragment fragment = new MainTabSliderFragment();
            transaction.replace(R.id.sample_content_fragment, fragment);
            transaction.commit();
        }
    }
}

.

// 幻灯片标签片段,成为标签的父视图。

public class MainTabSliderFragment extends Fragment {

    static final String LOG_TAG = MainTabSliderFragment.class.getSimpleName();
    private SlidingTabLayout mSlidingTabLayout;
    private ViewPager mViewPager;
    private CustomFragmentStatePageAdapter cfspAdapter;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
        View root = inflater.inflate(R.layout.fragment_sample, container, false);
        return root;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        mViewPager = (ViewPager) view.findViewById(R.id.viewpager);

        cfspAdapter = new CustomFragmentStatePageAdapter(getFragmentManager());
        List<String> pageTitles = new ArrayList<>();
        pageTitles.add(getString(R.string.page_one));
        pageTitles.add(getString(R.string.page_two));
        pageTitles.add(getString(R.string.page_three));
        List<Fragment> pageFragments = new ArrayList<>();
        final BookListPageFragment pageOne = BookListPageFragment.newInstance(new CustomFragmentStatePageAdapter.SwitchFragmentListener() {
            @Override
            public void onSwitchFragments(Class<? extends Fragment> clazz, Map<String, String> ... args) {
                cfspAdapter.switchFragment(CustomFragmentStatePageAdapter.PagePosition.POSITION_PAGE_ONE, clazz, this, args);
            }
        });
        CustomerPageFragment pageTwo = CustomerPageFragment.newInstance(...);
        ForumPageFragment pageThree = ForumPageFragment.newInstance(...);
        pageFragments.add(pageOne);
        pageFragments.add(pageTwo);
        pageFragments.add(pageThree);
        cfspAdapter.addFragments(pageFragments, pageTitles);
        mViewPager.setAdapter(cfspAdapter);

        mSlidingTabLayout = (SlidingTabLayout) view.findViewById(R.id.sliding_tabs);
        mSlidingTabLayout.setViewPager(mViewPager);
    }
}

.

// 第一个标签,处于初始状态(初始片段)。

public class BookListPageFragment extends Fragment {

    private static final String TAG = BookListPageFragment.class.getSimpleName();

    private BookListAdapter bAdapter;
    private static CustomFragmentStatePageAdapter.SwitchFragmentListener switchFragmentListener;

    public static BookListPageFragment newInstance(CustomFragmentStatePageAdapter.SwitchFragmentListener _switchFragmentListener) {
        switchFragmentListener = _switchFragmentListener;
        BookListPageFragment f = new BookListPageFragment();
        return f;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        View v = inflater.inflate(R.layout.page_one_booklist, container, false);
        final ListView lv = (ListView) v.findViewById(R.id.list);
        lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                BookRowData bRow = (BookRowData) lv.getItemAtPosition(position);
                Log.i(TAG, "Clicked on book " + bRow.getBookId());
                Map<String, String> param = new HashMap<>();
                param.put("book_id", Long.toString(bRow.getBookId()));
                switchFragmentListener.onSwitchFragments(ReviewBookPageFragment.class, new Map[]{param});
            }
        });
        initializeTestList(v, lv); // Just add some books to the list.

        return v;
    }

.

// PAGE ADAPTER,用于处理tab的Fragment切换。

public class CustomFragmentStatePageAdapter extends FragmentStatePagerAdapter {

    private final static String TAG = FragmentStatePagerAdapter.class.getSimpleName();

    private FragmentManager fragmentManager;
    private List<Fragment> fragmentList = new ArrayList<>();
    private List<String> tabTitleList = new ArrayList<>();

    public CustomFragmentStatePageAdapter(FragmentManager fm) {
        super(fm);
        fragmentManager = 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);
    }

    /**
     * Switching pages
     *
     * @param newFragment
     */
    public void switchFragment(final PagePosition position, Class<? extends Fragment> newFragment, SwitchFragmentListener sfListener, Map<String, String> ... args) {
        final Fragment old = fragmentList.get(position.getPagePosition());
        fragmentManager.beginTransaction().remove(old).commit(); //FIRST VERSION: IF HITTING BACK, IT EXITS APP AT ONCE.
        //fragmentManager.beginTransaction().addToBackStack("page_one").remove(old).commit(); //SECOND VERSION: NOW I NEED TO HIT BACK TWICE TO EXIT, BUT THE VIEW DOESN'T CHANGE AFTER HITTING THE FIRST TIME.
        try {
            Fragment f = (Fragment) newFragment.asSubclass(Fragment.class).getMethod("newInstance", SwitchFragmentListener.class, Map[].class).invoke(newFragment, new Object[]{sfListener, args});
            fragmentList.set(position.getPagePosition(), f);
        } catch (IllegalAccessException iae) {
            Log.e(TAG, "Fragment class access exception");
        } catch (NoSuchMethodException e) {
            Log.e(TAG, "Fragment instantiation exception (reflection)");
        } catch (InvocationTargetException e) {
            Log.e(TAG, "Fragment instantiation exception (reflection: no public constructor)");
        }
        notifyDataSetChanged();
    }


    public interface SwitchFragmentListener {
        void onSwitchFragments(Class<? extends Fragment> clazz, Map<String, String> ... args);
    }

    public enum PagePosition {
        POSITION_PAGE_ONE (0),
        POSITION_PAGE_TWO (1),
        POSITION_PAGE_THREE (2);

        private final int position;

        PagePosition(int position) {
            this.position = position;
        }

        public int getPagePosition() {
            return this.position;
        }
    }
}

.

// 和 最后是我想从 返回的片段;这是书评片段,当从列表中单击一本书时,它也会显示在第一个选项卡中。第二个和第三个选项卡被省略了。

public class ReviewBookPageFragment extends Fragment {

    private static final String TAG = ReviewBookPageFragment.class.getSimpleName();
    private CommentsListAdapter cAdapter;
    private Long bookId;

    private static CustomFragmentStatePageAdapter.SwitchFragmentListener switchFragmentListener;

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

    public static ReviewBookPageFragment newInstance(CustomFragmentStatePageAdapter.SwitchFragmentListener _sfListener, Map<String, String> ... args) {
        switchFragmentListener = _sfListener;
        Bundle b = BundlePacker.packMaps(args); // Custom util class for packing the params into a bundle.
        ReviewBookPageFragment f = new ReviewBookPageFragment();
        f.setArguments(b);
        return f;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        View v = inflater.inflate(R.layout.page_review_book, container, false);
        Bundle bookIdBundle = this.getArguments();
        Long bId = Long.parseLong(bookIdBundle.getString("book_id"));
        Log.i(TAG, "Book ID: " + bId);
        initializeTestList(v); // Just fill the book's reviews with test data.

        return v;
    }
}

所以,这就是一堆代码。总而言之,这个想法是在点击列表中的任何一本书时从图书列表视图(显示在选项卡一)切换到该书的评论;评论也显示在第一个选项卡上,我想按返回键返回图书列表。 目前,它会关闭应用程序回击一次,如果我将事务添加到后台堆栈(请参阅我的 CustomFragmentStatePageAdapter),则会关闭两次(但视图在第一次回击后不会改变。

我们将不胜感激任何对此问题的帮助。

要解决弹回问题,您可以在 activity class,

中使用此代码
@Override
public void onBackPressed() {
    // if there is a fragment and the back stack of this fragment is not empty,
    // then emulate 'onBackPressed' behaviour, because in default, it is not working
    FragmentManager fm = getSupportFragmentManager();
    for (Fragment frag : fm.getFragments()) {
        if (frag.isVisible()) {
            FragmentManager childFm = frag.getChildFragmentManager();
            if (childFm.getBackStackEntryCount() > 0) {
                childFm.popBackStack();
                return;
            }
        }
    }
    super.onBackPressed();
}

我做了这样的事情:

 private View _view;
 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    if(_view==null){
         _view = inflater.inflate(R.layout.page_review_book, container, false);
         // your_code
   }
   return _view;
}