0

Я создаю приложение для Android, используя FragmentStatePagerAdapter для навигации с вкладками и динамического содержимого на каждой вкладке. Каждая вкладка имеет Fragment с содержимым, которое должно быть заменено при вводе пользователя (например, первая вкладка имеет Fragment, содержащий список книг, и после нажатия вы можете получить доступ к подробной информации о книге, которая отображается с помощью другого FragmentAndroid - проблемы с 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), ДВАЖДЫ (но вид не меняется после удара назад в первый раз.

Любая помощь с этот вопрос будет высоко оценен.

+0

На самом деле вы делаете в неправильном направлении. Ваш ViewPager находится внутри фрагмента, поэтому вы должны использовать getChildFragmentManger() внутри адаптера [концепция вложенного фрагмента]. – Krish

+0

ОК, я просто изменил это, спасибо за подсказку. Просто попробовал - теперь он работает лучше (до того, как я опустил молчаливое исключение, записанное как возможная ошибка в этом https://code.google.com/p/android/issues/detail?id=89244 контролере ошибок, и это не происходит сейчас), но теперь он просто выходит из приложения при ударе. Задняя сторона ставится «Фрагмент», когда я начинаю второй, но после удара он не выскакивает. – Azurlake

ответ

0

Для фиксации вопрос popback вы можете использовать этот код в классе деятельности,

@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(); 
} 
-1

Я сделал somethink так:

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; 
} 
Смежные вопросы