8

Что я хочу.Отдельная обратная навигационная панель для пейджера с вкладками в Android

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

К примеру, у меня есть tab_a с Fragment_1, tab_b с Fragment_4 и tab_c с Fragment_7. Теперь я хочу кнопку в Fragment_1, которая открывает мне Fragment_2, но я хочу, чтобы иметь возможность пронести по фрагментам tab_b и tab_c. И когда я вернусь к tab_a, необходимо отобразить Fragment_2.

enter image description here

MainActivity 
    | 
    | 
ContainerFragment 
    | 
    | 
    |_ _ _ Tab A 
    |  |_ _ _ Fragment 1 
    |  | 
    |  |_ _ _ Fragment 2 
    |  | 
    |  |_ _ _ Fragment 3 
    |  | 
    |  |_ _ _ ... 
    |   
    |_ _ _ Tab B 
    |  |_ _ _ Fragment 4 
    |  | 
    |  |_ _ _ Fragment 5 
    |  | 
    |  |_ _ _ Fragment 6 
    |  | 
    |  |_ _ _ ... 
    | 
    |_ _ _ Tab C 
    |  |_ _ _ Fragment 7 
    |  | 
    |  |_ _ _ Fragment 8 
    |  | 
    |  |_ _ _ Fragment 8 
    |  | 
    |  |_ _ _ ... 

Моя текущая основная деятельность.

Xml

<android.support.design.widget.CoordinatorLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:app="http://schemas.android.com/apk/res-auto" 
    android:id="@+id/activity_main" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent"> 

    <android.support.design.widget.AppBarLayout 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"> 

     <android.support.design.widget.TabLayout 
      android:id="@+id/tabs" 
      android:layout_width="match_parent" 
      android:layout_height="wrap_content" 
      app:tabMode="fixed" 
      app:tabGravity="fill"/> 
    </android.support.design.widget.AppBarLayout> 

    <android.support.v4.view.ViewPager 
     android:id="@+id/viewpager" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     app:layout_behavior="@string/appbar_scrolling_view_behavior" /> 

</android.support.design.widget.CoordinatorLayout> 

Код

public class MainActivity extends AppCompatActivity { 

    private TabLayout tabLayout; 

    private final static int[] tabIcons = { 
      R.drawable.ic_tab_a, 
      R.drawable.ic_tab_b, 
      R.drawable.ic_tab_c 
    }; 

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

     ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager); 
     setupViewPager(viewPager); 

     tabLayout = (TabLayout) findViewById(R.id.tabs); 
     tabLayout.setupWithViewPager(viewPager); 
     setupTabIcons(); 
    } 

    private void setupTabIcons() { 
     tabLayout.getTabAt(0).setIcon(tabIcons[0]); 
     tabLayout.getTabAt(1).setIcon(tabIcons[1]); 
     tabLayout.getTabAt(2).setIcon(tabIcons[2]); 
    } 

    private void setupViewPager(ViewPager viewPager) { 
     ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager()); 
     adapter.addFrag(new Fragment1()); 
     adapter.addFrag(new Fragment4()); 
     adapter.addFrag(new Fragment7()); 
     viewPager.setAdapter(adapter); 
    } 

    class ViewPagerAdapter extends FragmentPagerAdapter { 

     private final List<Fragment> mFragmentList = new ArrayList<>(); 

     ViewPagerAdapter(FragmentManager manager) { 
      super(manager); 
     } 

     @Override 
     public Fragment getItem(int position) { 
      return mFragmentList.get(position); 
     } 

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

     void addFrag(Fragment fragment) { 
      mFragmentList.add(fragment); 
     } 
    } 
} 

Фрагменты

Xml

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:tools="http://schemas.android.com/tools" 
    android:background="@android:color/holo_blue_light" 
    android:id="@+id/fragment_1" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:paddingBottom="@dimen/activity_vertical_margin" 
    android:paddingLeft="@dimen/activity_horizontal_margin" 
    android:paddingRight="@dimen/activity_horizontal_margin" 
    android:paddingTop="@dimen/activity_vertical_margin" 
    tools:context="myContext"> 

    <Button 
     android:text="Go to next Fragment" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_centerVertical="true" 
     android:layout_centerHorizontal="true" 
     android:id="@+id/button_nextFrag" /> 

    <FrameLayout 
     android:id="@+id/tab1_container" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent"/> 

</RelativeLayout> 

Код

public class Fragment1 extends Fragment { 

    private void goNextFragment() { 
     FragmentTransaction trans = getFragmentManager().beginTransaction(); 
     trans.replace(R.id.tab1_container, new Fragment2()); 
     trans.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); 
     trans.addToBackStack(null); 
     trans.commit(); 
    } 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, 
          Bundle savedInstanceState) { 
     // Inflate the layout for this fragment. 
     View root = inflater.inflate(R.layout.fragment_1, container, false); 
     // Go to next Fragment Button. 
     final Button nextFrag = (Button) root.findViewById(R.id.button_nextFrag); 
     nextFrag .setOnClickListener(new View.OnClickListener() { 
      public void onClick(View v) { 
       goNextFragment(); 
      } 
     }); 
     return root; 
    } 
} 

Edit:

У меня есть какие-то решения, но никто не совершенен. Если я поставлю Framelayaout в MainActivity, Fragment_1 будет правильно заменен на Fragment_2, но он также находится в tab_b и tab_c. Если FrameLayout находится в Fragment_1, я могу правильно провести другие вкладки, но замена идет не очень хорошо, причина: Fragment_2 открыт, но Fragment_1 также есть.

+0

Что именно происходит в вашем случае? Сохраняет ли ориентация экрана все фрагменты в начальном состоянии? –

+0

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

+0

@SantiGil, вы имеете в виду, что хотите что-то вроде приложения Flipboard? вкладки внутри вкладок? – Amir

ответ

2

Вы можете использовать ViewPager вместе с TabLayout.

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

<android.support.v4.view.ViewPager 
    android:id="@+id/viewpager" 
    android:layout_width="match_parent" 
    android:layout_height="0dp" 
    android:layout_weight="1"/> 
<android.support.design.widget.TabLayout 
    android:id="@+id/tablayout" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content"/> 
</LinearLayout> 

Затем создать класс, который расширяет FragmentStatePagerAdapter для инициализации ViewPager с начальными Fragment с:

public class CustomPagerAdapter extends FragmentStatePagerAdapter { 
    private final List<String> tabTitles = new ArrayList<String>() {{ 
     add("Fragment 1"); 
     add("Fragment 4"); 
     add("Fragment 7"); 
    }}; 

    private List<Fragment> tabs = new ArrayList<>(); 

    public CustomPagerAdapter(FragmentManager fragmentManager) { 
     super(fragmentManager); 

     initializeTabs(); 
    } 

    private void initializeTabs() { 
     tabs.add(HostFragment.newInstance(new Fragment1())); 
     tabs.add(HostFragment.newInstance(new Fragment4())); 
     tabs.add(HostFragment.newInstance(new Fragment7())); 
    } 

    @Override 
    public Fragment getItem(int position) { 
     return tabs.get(position); 
    } 

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

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

фрагменты, содержащие фактическое содержимое должны быть обернуты HostFragment, который использует его ребенок FragmentManager к замените текущий фрагмент на новый, если вы хотите перейти, или вывести последний фрагмент из стека фрагментов. Вызовите его replaceFragment навигации:

public class HostFragment extends BackStackFragment { 
    private Fragment fragment; 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, 
          Bundle savedInstanceState) { 
     super.onCreateView(inflater, container, savedInstanceState); 
     View view = inflater.inflate(R.layout.host_fragment, container, false); 
     if (fragment != null) { 
      replaceFragment(fragment, false); 
     } 
     return view; 
    } 

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

    public static HostFragment newInstance(Fragment fragment) { 
     HostFragment hostFragment = new HostFragment(); 
     hostFragment.fragment = fragment; 
     return hostFragment; 
    } 
} 

HostFragment должны расширить абстрактный класс BackstackFragment

public abstract class BackStackFragment extends Fragment { 
    public static boolean handleBackPressed(FragmentManager fm) 
    { 
     if(fm.getFragments() != null){ 
      for(Fragment frag : fm.getFragments()){ 
       if(frag != null && frag.isVisible() && frag instanceof BackStackFragment){ 
        if(((BackStackFragment)frag).onBackPressed()){ 
         return true; 
        } 
       } 
      } 
     } 
     return false; 
    } 

    protected boolean onBackPressed() 
    { 
     FragmentManager fm = getChildFragmentManager(); 
     if(handleBackPressed(fm)){ 
      return true; 
     } else if(getUserVisibleHint() && fm.getBackStackEntryCount() > 0){ 
      fm.popBackStack(); 
      return true; 
     } 
     return false; 
    } 
} 

провода Everthing вверх в MainActivity:

public class MainActivity extends AppCompatActivity { 
    private CustomPagerAdapter customPagerAdapter; 
    private ViewPager viewPager; 
    private TabLayout tabLayout; 

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

     setContentView(R.layout.activity_main); 

     viewPager = (ViewPager) findViewById(R.id.viewpager); 
     tabLayout = (TabLayout) findViewById(R.id.tablayout); 
     customPagerAdapter = new CustomPagerAdapter(getSupportFragmentManager()); 

     // 2 is enough for us; increase if you have more tabs! 
     viewPager.setOffscreenPageLimit(2); 
     viewPager.setAdapter(customPagerAdapter); 
     tabLayout.setupWithViewPager(viewPager); 
    } 

    @Override 
    public void onBackPressed() 
    { 
     if(!BackStackFragment.handleBackPressed(getSupportFragmentManager())){ 
      super.onBackPressed(); 
     } 
    } 

    public void openNextFragment() { 
     HostFragment hostFragment = (HostFragment) customPagerAdapter.getItem(viewPager.getCurrentItem()); 

     // your logic to change the fragments... 
    } 
} 

Это решение, конечно, не идеальный, но получает работа выполнена. Читайте больше пуха об этом в моем blog post по проблеме.

+1

Отлично! Я [добавил это] (https://gyazo.com/8f55aeb51323113b093ad5c6e40ba2a7), чтобы сделать openNextFragment более гибким. –

3

Основываясь на том, что вы объяснили мне Реализовать следующее:

1- В вашей деятельности создать свой Tabs и ViewPager:

mPager = (ViewPager) findViewById(R.id.vp_pager); 
    mPager.setOffscreenPageLimit(3); 
    PagerAdapter mAdapter = new PagerAdapter(getSupportFragmentManager(), getContext()); 
    mPager.setAdapter(mAdapter); 
    //tab.setupWithViewPager(mPager); 

2- В каждой вкладке создать ViewPager (я предполагаю, что вы хотите вкладки внутри tabs если вам не нужны вкладки, они просто удалены). Реализация каждого fragment будет примерно такой:

ViewPager mPager = (ViewPager) rootView.findViewById(R.id.vp_pager); 
    mPager.setOffscreenPageLimit(3); 
    CategoryAdapter mAdapter = new CategoryAdapter(getChildFragmentManager());//make sure this ChildFragmentManager 
    mPager.setAdapter(mAdapter); 

Кроме того, в фрагменте создать кнопку, которая когда Нажмите идет к другому фрагменту:

button.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View view) { 
       mPager.setCurrentItem(1); // go to second fragment 
      } 
     }); 

конечный результат будет что-то вроде следующий и когда Если вы идете в Home Вкладки и затем возвращается к Категория Tab вам 'Ваша позиция Fragment не изменилась.

Две точки вы должны помнить:

  1. установить mPager.setOffscreenPageLimit(3);, если вы не хотите, чтобы уничтожить фрагмент
  2. Во внутреннем проходе фрагмента getChildFragmentManager() вместо FragmentManager()

enter image description here

Если вам не нужен Внутренний ViewPager И вы просто хотите загрузить один i nstance of Fragment at time здесь еще один вариант:

1- Сделайте первый элемент предыдущего пути.

2- В вашем фрагменте XML поместить FrameContainer и загрузить фрагмент внутри него:

CategoryResultFragment f = CategoryResultFragment.newInstance(); 
     getFragmentManager().beginTransaction() 
       .replace(R.id.frame_result_container, f) 
       .commit(); 

Edit: Это не лучший подход, но я думаю, что решить проблему:

1- Создать статическое поле в MainActivity как следующее:

static String[] type = new String[3]; 

2- при вызове OnClick в вашем фрагменте обновления String значение в вашем MainActivity.

public static update currentType(int pos,String type); 

первое значение позиции ViewPager, второе значение является ваш внутренний тип фрагмента (например: fragment_d);

3- В вашем ViewPager

@Override 
     public Fragment getItem(int position) { 
      switch (position) { 
       case 0: // first tab 
        if(type[position] == "fragment_d") 
         return new Fragment_D(); 
        else 
         return new Fragment_B(); 

      } 
     } 
+0

Я пытаюсь ваше решение. Первый 'mPager.setOffscreenPageLimit (3);' Я предполагаю, что это потому, что у меня есть 3 вкладки 'tab_a'' tab_b' 'tab_c'. Но второй, почему же 3? –

+1

@SantiGil Я предполагаю, что ваш внутренний ViewPager имеет 3 страницы. (также по умолчанию setOffscreenPageLimit равно 3) – Amir

+0

Хорошо! Должен ли я иметь «корневой фрагмент» для каждой вкладки, внутри его viewpager я помещаю свои 3 фрагмента? Это будет в общей сложности 4 фрагмента. 1 «корневой фрагмент» и 3 «нормальные фрагменты» –

0

Вы можете изменить фрагменты внутри фрагмента страницы. Например, TAB_A реализует логику выбора фрагмента 1 или 2 и отображает выбранный фрагмент внутри. Иерархия выглядит так:

ViewPager -> TAB_A -> фрагмент 1 или 2 (отображается внутри TAB_A).

Также вы должны использовать getChildFragmentManager() для управления фрагментами внутри TAB_A.

Смежные вопросы