Я создаю приложение для Android, используя FragmentStatePagerAdapter
для навигации с вкладками и динамического содержимого на каждой вкладке. Каждая вкладка имеет Fragment
с содержимым, которое должно быть заменено при вводе пользователя (например, первая вкладка имеет Fragment
, содержащий список книг, и после нажатия вы можете получить доступ к подробной информации о книге, которая отображается с помощью другого Fragment
Android - проблемы с BackStack с использованием FragmentStatePagerAdapter
Проблема: я не нашел способ правильно обрабатывать события onBack
или BackStack, поэтому, когда я просматриваю детали любой книги, я могу легко вернуться назад, нажав кнопку «Назад» - я имею в виду, выталкивая последнее состояние из Backstack.
Что я подозреваю: способ, которым я переключаю объекты Fragment
, может не быть лучшим, но кроме проблемы с кнопкой «Назад», он работает так, как я хочу. Я подозреваю, что некоторые проблемы между адаптером FragmentStatePagerAdapter
и собственной коллекцией FragmentManager Fragment
s; возможно, это что-то с простым решением, которого я не видел.
Unaswered вопрос (не очень подробно, хотя): Adding Fragment to BackStack using FragmentStatePagerAdapter
Код:
// ОСНОВНАЯ ДЕЯТЕЛЬНОСТЬ, Просто это просто.
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;
}
.
// АДАПТЕР СТРАНИЦЫ, используемый для обработки переключения фрагментов вкладки.
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;
}
}
}
.
// И НАКОНЕЦ ФРАГМЕНТ Я ХОЧУ ВЕРНУТЬСЯ ОТ; это обзор книги Fragment, который отображается также на первой вкладке при нажатии на книгу из списка. Вторая и третья вкладки опущены.
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;
}
}
Итак, это куча кода. Идея, как резюме, состоит в том, чтобы переключиться с просмотра списка книг (показано на вкладке один), на обзоры книги, когда вы нажимаете на любую книгу из списка; обзоры также отображаются на первой вкладке, и я хочу вернуться к списку книг при нажатии. В настоящее время он закрывает приложение ударяя назад один раз, и если добавить транзакцию в backstack (см мой CustomFragmentStatePageAdapter
), ДВАЖДЫ (но вид не меняется после удара назад в первый раз.
Любая помощь с этот вопрос будет высоко оценен.
На самом деле вы делаете в неправильном направлении. Ваш ViewPager находится внутри фрагмента, поэтому вы должны использовать getChildFragmentManger() внутри адаптера [концепция вложенного фрагмента]. – Krish
ОК, я просто изменил это, спасибо за подсказку. Просто попробовал - теперь он работает лучше (до того, как я опустил молчаливое исключение, записанное как возможная ошибка в этом https://code.google.com/p/android/issues/detail?id=89244 контролере ошибок, и это не происходит сейчас), но теперь он просто выходит из приложения при ударе. Задняя сторона ставится «Фрагмент», когда я начинаю второй, но после удара он не выскакивает. – Azurlake