[英]Separate Back Navigation for a Tabbed View Pager in Android
What I want. 我想要的是。
In a tab sliding menu context, I want to replace a fragment to another inside a tab, and maintaining the tab menu, and also the current tab. 在选项卡滑动菜单上下文中,我想将一个片段替换为选项卡内的另一个片段,并保持选项卡菜单以及当前选项卡。 When sliding to another tab and returning to original, I want the last fragment to be displayed.
当滑到另一个选项卡并返回原始位置时,我希望显示最后一个片段。
For example, I have tab_a
with Fragment_1
, tab_b
with Fragment_4
and tab_c
with Fragment_7
. 例如,我有
tab_a
与Fragment_1
, tab_b
与Fragment_4
和tab_c
与Fragment_7
。 Now I want a button in Fragment_1
that opens me Fragment_2
, but I want to be able to swipe to the fragments of the tab_b
and tab_c
. 现在,我想使用
Fragment_1
中的按钮来打开Fragment_2
,但我希望能够滑动到tab_b
和tab_c
的片段。 And when I return to tab_a, Fragment_2
must be displayed. 当我返回tab_a时,必须显示
Fragment_2
。
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
| |
| |_ _ _ ...
My current main activity. 我当前的主要活动。
Xml 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>
Code 码
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);
}
}
}
Fragments 碎片
Xml 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>
Code 码
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: 编辑:
I've got some solutions, but none is perfect. 我有一些解决方案,但没有一个是完美的。 If I put a Framelayaout in MainActivity,
Fragment_1
is correctly replaced to Fragment_2
, but it is also in tab_b
and tab_c
. 如果我在MainActivity中放置Framelayaout,则
Fragment_1
被正确替换为Fragment_2
,但它也会同时位于tab_b
和tab_c
。 If the FrameLayout is in Fragment_1
, I can swipe the other tabs correctly, but the replacement goes not good, cause Fragment_2
is opened, but Fragment_1
is also there. 如果FrameLayout在
Fragment_1
,则可以正确滑动其他选项卡,但是替换效果不佳,因为Fragment_2
已打开,但Fragment_1
也存在。
Based on what you explained I Implement following: 根据您的解释,我实现了以下内容:
1- In your Activity create your Tabs
and ViewPager
: 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- In each Tabs create ViewPager
(I assume you want tabs inside tabs
if you don't need tabs just removed them), Implementation of each fragment
would be something like : 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);
Also in fragment create a Button which when Click goes to another fragment: 同样在片段中创建一个Button,当Click转到另一个片段时:
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mPager.setCurrentItem(1); // go to second fragment
}
});
final result would be something like following and when If you go to Home Tabs and then goes back to Category Tab you're your Fragment
position not changed. 最终结果将类似于以下内容,当您转到“ 主页”选项卡,然后返回到“ 类别”选项卡时,您的
Fragment
位置不变。
Two point you should remember: 您应该记住两点:
mPager.setOffscreenPageLimit(3);
mPager.setOffscreenPageLimit(3);
if you don't want to destroy your fragment getChildFragmentManager()
instead of FragmentManager()
getChildFragmentManager()
而不是FragmentManager()
If you don't need Inner ViewPager
And you Just want load one instance of Fragment at time here is another option: 如果您不需要Inner
ViewPager
并且只想一次加载一个Fragment实例,那么这里是另一种选择:
1- Do first item of previous way. 1-做以前的方式的第一项。
2- In your fragment xml put FrameContainer and load your fragment inside it: 2-在片段xml中放入FrameContainer并将片段加载到其中:
CategoryResultFragment f = CategoryResultFragment.newInstance();
getFragmentManager().beginTransaction()
.replace(R.id.frame_result_container, f)
.commit();
Edit: This is not best approach but I think solve your issue: 编辑:这不是最好的方法,但我认为解决您的问题:
1- Create a static field in your MainActivity
like following: 1-在您的
MainActivity
创建一个静态字段,如下所示:
static String[] type = new String[3];
2- When you call onClick in your fragment, update String
value in your MainActivity
. 2-当您在片段中调用onClick时,更新
MainActivity
String
值。
public static update currentType(int pos,String type);
first value is ViewPager position, Second value is your inner Fragment type (eg: fragment_d); 第一个值为ViewPager位置,第二个值为您的内部Fragment类型(例如:fragment_d);
3- In your ViewPager 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();
}
}
You can use a ViewPager
together with a TabLayout
. 您可以使用
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>
Then create a class that extends FragmentStatePagerAdapter
to initialize the ViewPager
with your initial Fragment
s: 然后创建一个扩展
FragmentStatePagerAdapter
的类,以使用初始Fragment
初始化ViewPager
:
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);
}
}
The fragments containing the actual contents should be wrapped by a HostFragment
, that uses its child FragmentManager
to replace the current fragment with a new one if you want to navigate, or to pop the last fragment from the fragment stack. 包含实际内容的片段应由
HostFragment
包裹,如果要导航或从片段堆栈中弹出最后一个片段,则使用HostFragment
的子FragmentManager
将当前片段替换为新片段。 Call its replaceFragment
to navigate: 调用其
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;
}
}
The HostFragment should extend the abstract class BackstackFragment
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;
}
}
Wire everthing up in the MainActivity
: 在
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...
}
}
This solution is certainly not perfect but gets the job done. 该解决方案当然不是完美的,但可以完成工作。 Read more fluff about it in my blog post on the issue.
在有关此问题的博客文章中阅读有关它的更多信息。
You can change fragments inside page fragment. 您可以在页面片段内更改片段。 For example, TAB_A implements logic of picking fragment 1 or 2, and displaying picked fragment inside.
例如,TAB_A实现拾取片段1或2并在其中显示拾取的片段的逻辑。 Hierarchy looks like:
层次结构如下:
ViewPager -> TAB_A -> fragment 1 or 2 (displaying inside TAB_A). ViewPager-> TAB_A->片段1或2(显示在TAB_A内部)。
Also you should use getChildFragmentManager() to manage fragments inside TAB_A. 另外,您应该使用getChildFragmentManager()来管理TAB_A中的片段。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.