I wrote an app that has an action bar and 3 view pagers, now I finished the first pager, which is a Google map, and for the other 2, I currently just inflate them with a layout that has only a text view. The problem is that whenever I swipe to the 3rd view pager, and then swipe back, the app crashes, but switching between the first two is okay, except that the UI of the second pager seems to be affected by the google map UI. The code for the main activity is here:
public class LobbyActivity extends ActionBarActivity implements ActionBar.TabListener {
SectionsPagerAdapter mSectionsPagerAdapter;
ViewPager mViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lobby);
// Set up the action bar
final ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
initializePager();
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener(){
#Override
public void onPageSelected(int position) {
actionBar.setSelectedNavigationItem(position);
}
});
for(int i=0; i<mSectionsPagerAdapter.getCount(); i++){
actionBar.addTab(
actionBar.newTab()
.setText(mSectionsPagerAdapter.getPageTitle(i))
.setTabListener(this));
}
}
private void initializePager() {
List<Fragment> fragments = new Vector<Fragment>();
fragments.add(Fragment.instantiate(this, MapFragment.class.getName()));
fragments.add(Fragment.instantiate(this, ListFragment.class.getName()));
fragments.add(Fragment.instantiate(this, SavedFragment.class.getName()));
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager(), fragments);
mViewPager = (ViewPager) findViewById(R.id.lobby_pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
}
#Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
mViewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
}
#Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
}
public class SectionsPagerAdapter extends FragmentPagerAdapter {
private List<Fragment> fragments;
public SectionsPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
#Override
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a fragment in the fragment container
return this.fragments.get(position);
}
#Override
public int getCount() {
// Show 3 total pages.
return this.fragments.size();
}
#Override
public CharSequence getPageTitle(int position) {
Locale l = Locale.getDefault();
switch (position) {
case 0:
return "MAP VIEW";
case 1:
return "YANK LIST";
case 2:
return "SAVED YANKS";
}
return null;
}
}
}
and I have 3 fragment classes, they're almost the same, so I just show the map fragment:
public class MapFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle saveInstanceState) {
if (container == null)
return null;
return inflater.inflate(R.layout.fragment_lobby_map, container, false);
}
}
And below is the layout for the main activity:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/lobby_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
layout for map fragment:
<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.google.android.gms.maps.MapFragment"/>
The other two layouts for the remaining 2 fragments are just a linearLayout containing a textview.
When the app crashes, I get
05-12 12:05:18.963 147-227/? W/MemoryDealer﹕ madvise(0x423c3000, 16384, MADV_REMOVE) returned Operation not supported on transport endpoint
05-12 12:05:18.963 9173-9173/com.yankteam.yank.app W/dalvikvm﹕ threadid=1: thread exiting with uncaught exception (group=0x40cf2390)
Thanks a lot for reading this.
I found this answer to call viewPager.setOffscreenPageLimit(3); and its working for me nicely.
Related
I want to get the last fragment in the backstack, or the current displayed it's the same for me, in the tab b_1. As you can see in the following image, I have a ViewPager, and another one inner tab b. Thus there are four current fragments displayed.
Question: How can I get the Fragment 2 instance?
I have seen another solutions, but none works for this scenario.
Annotation: The fragment to return is not necessary the hosted in the ViewPager. I can have opened two more fragments in a tab.
With this method I get all the current visible fragments, but not the one specific I want.
public ArrayList<Fragment> getVisibleFragment() {
List<Fragment> fragments = getSupportFragmentManager().getFragments();
ArrayList<Fragment> visibleFragments = new ArrayList<>();
if (fragments != null) {
for (Fragment fragment : fragments) {
if (fragment != null && fragment.isVisible())
visibleFragments.add(fragment);
}
}
return visibleFragments;
}
Some interesting code
activity_main.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>
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static ViewPagerAdapter adapter;
private static ViewPager viewPager;
private TabLayout tabLayout;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager = (ViewPager) findViewById(R.id.viewpager);
setupViewPager();
tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(viewPager);
setupTabIcons();
}
private void setupViewPager() {
adapter = new ViewPagerAdapter(getSupportFragmentManager());
// Wrap with HostFragment to get separate tabbed nagivation.
adapter.addFrag(HostFragment.newInstance(new Fragment1()), null);
adapter.addFrag(HostFragment.newInstance(new RootFragment2()), null);
adapter.addFrag(HostFragment.newInstance(new Fragment4()), null);
viewPager.setAdapter(adapter);
viewPager.setOffscreenPageLimit(2);
}
public void openNewFragment(Fragment fragment) {
HostFragment hostFragment = (HostFragment) adapter.getItem(viewPager.getCurrentItem());
hostFragment.replaceFragment(fragment, true);
}
}
fragment_host.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/hosted_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
HostFragment.java
/**
* This class implements separate navigation for a tabbed viewpager.
*
* Based on https://medium.com/#nilan/separate-back-navigation-for-
* a-tabbed-view-pager-in-android-459859f607e4#.u96of4m4x
*/
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.fragment_host, 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;
}
public Fragment getFragment() {
return fragment;
}
}
fragment2_root.xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/fragment2_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.TabLayout
android:id="#+id/tab2_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="fixed"
app:tabGravity="fill"/>
<android.support.v4.view.ViewPager
android:id="#+id/tab2_viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"/>
</LinearLayout>
RootFragment2.java
public class RootFragment2 extends Fragment {
private ViewPagerAdapter adapter;
private ViewPager viewPager;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Inflate the layout for this fragment.
View root = inflater.inflate(R.layout.fragment2_root, container, false);
viewPager = (ViewPager) root.findViewById(R.id.tab2_viewpager);
setupViewPager(viewPager);
TabLayout tabLayout = (TabLayout) root.findViewById(R.id.tab2_tabs);
tabLayout.setupWithViewPager(viewPager);
return root;
}
private void setupViewPager(ViewPager viewPager) {
adapter = new ViewPagerAdapter(getActivity().getSupportFragmentManager());
// Wrap with HostFragment to get separate tabbed nagivation.
adapter.addFrag(HostFragment.newInstance(new Fragment2()), null);
adapter.addFrag(HostFragment.newInstance(new Fragment3()), null);
viewPager.setAdapter(adapter);
viewPager.setOffscreenPageLimit(1);
}
public ViewPagerAdapter getAdapter() {
return adapter;
}
public ViewPager getViewPager() {
return viewPager;
}
}
First define a SparseArray in your ViewPagers' adapters like below. In this array we'll hold the instance of fragments.
SparseArray<Fragment> registeredFragments = new SparseArray<>();
And Override your Adapters' instantiateItem method.
#Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
}
Also Override destroyItem method of your ViewPagers
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}
And define a new method to get your ViewPager Fragments instance.
public Fragment getRegisteredFragment(int position) {
return registeredFragments.get(position);
}
And finally set add a PageChangeListener to your ViewPagers:
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
// Here's your instance
YourFragment fragment =(YourFragment)yourPagerAdapter.getRegisteredFragment(position);
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
I hope this'll help you. Good luck.
Edit: I'm sorry i cannot understand exactly what you're planning to do but if you need to keep sub fragment (b_1, b_2) instance you can define a method to your activity such as
public void setCurrentFragment(Fragment fragment){
this.currentFragment = fragment;
}
and in your sub view pager's adapter you can call this method like below:
subViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
// Here's your instance
YourFragment fragment =(YourFragment)yourSubPagerAdapter.getRegisteredFragment(position);
((MyActivity)getActivity).setCurrentFragment(fragment);
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
With this way you can keep one instance and your top fragment.
After some comments with DEADMC, I decided to implement it with extra memory, saving in a list the open fragments.
I explain my solution. I hve two types of special fragments, host and root. Host Fragments are those which are the init of a tab. Root Fragments are those which hold a ViewPager. In this scenario, tab_b has a Root Fragment with an inner ViewPager.
For each tab, this is, for each HostFragment, I have added a list fragments to save the fragments opened in this tab. Also a method getCurrentFragment to get the last displayed in this tab.
public class HostFragment extends BackStackFragment {
private ArrayList<Fragment> fragments = new ArrayList<>();
public Fragment getCurrentFragment() {
return fragments.get(fragments.size() - 1);
}
Also a method to remove the last fragment displayed in this tab is needed, to maintain a true state. Back button needs to be redirect to this method.
public void removeCurrentFragment() {
getChildFragmentManager().popBackStack();
fragments.remove(fragments.size() - 1);
}
Also previous methods need to be changed, to insert the new fragments opened into the list.
public void replaceFragment(Fragment fragment, boolean addToBackstack) {
// NEW: Add new fragment to the list.
fragments.add(fragment);
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();
// NEW: Add first fragment to the list.
hostFragment.fragments.add(fragment);
return hostFragment;
}
} // HostFragment.
RootFragment is an interface implemented by those fragments which holds a ViewPager.
public interface RootFragment {
/**
* Opens a new Fragment in the current page of the ViewPager held by this Fragment.
*
* #param fragment - new Fragment to be opened.
*/
void openNewFragment(Fragment fragment);
/**
* Returns the fragment displayed in the current tab of the ViewPager held by this Fragment.
*/
Fragment getCurrentFragment();
}
And then, the implementation would be:
MainActivity is not a Fragment, but I implement RootFragment interface for meaning.
public class MainActivity extends AppCompatActivity implements RootFragment {
private void setupViewPager() {
adapter = new ViewPagerAdapter(getSupportFragmentManager());
// Wrap with HostFragment to get separate tabbed nagivation.
adapter.addFrag(HostFragment.newInstance(new Fragment1()), null);
adapter.addFrag(new RootFragment2(), null); // This is a Root, not a Host.
adapter.addFrag(HostFragment.newInstance(new Fragment4()), null);
viewPager.setAdapter(adapter);
// 2 because TabLayout has 3 tabs.
viewPager.setOffscreenPageLimit(2);
}
#Override
public void openNewFragment(Fragment fragment) {
Fragment hosted = adapter.getItem(viewPager.getCurrentItem());
// Replace the fragment of current tab to [fragment].
if (hosted instanceof HostFragment) {
((HostFragment) hosted).replaceFragment(fragment, true);
// Spread action to next ViewPager.
} else {
((RootFragment) hosted).openNewFragment(fragment);
}
}
#Override
public Fragment getCurrentFragment() {
Fragment hosted = adapter.getItem(viewPager.getCurrentItem());
// Return current tab's fragment.
if (hosted instanceof HostFragment) {
return ((HostFragment) hosted).getCurrentFragment();
// Spread action to next ViewPager.
} else {
return ((RootFragment) hosted).getCurrentFragment();
}
}
}
and RootFragment2
public class RootFragment2 extends Fragment implements RootFragment {
private void setupViewPager(ViewPager viewPager) {
adapter = new ViewPagerAdapter(getActivity().getSupportFragmentManager());
// Wrap with HostFragment to get separate tabbed nagivation.
adapter.addFrag(HostFragment.newInstance(new Fragment2()), null);
adapter.addFrag(HostFragment.newInstance(new Fragment3()), null);
viewPager.setAdapter(adapter);
// 1 because TabLayout has 2 tabs.
viewPager.setOffscreenPageLimit(1);
}
#Override
public void openNewFragment(Fragment fragment) {
((HostFragment) adapter.getItem(viewPager.getCurrentItem())).replaceFragment(fragment, true);
}
#Override
public Fragment getCurrentFragment() {
return ((HostFragment) adapter.getItem(viewPager.getCurrentItem())).getCurrentFragment();
}
}
And then, I just need to call:
((MainActivity) getActivity()).getCurrentFragment();
You can create something like CustomFragment class which contains getClassName method and extends from Fragment class. Then extend all your fragments such as RootFragment2 from CustomFragment class instead of just Fragment.
So you can get fragment like this:
public CustomFragment getNeededFragment(String fragmentName) {
List<Fragment> fragments = getSupportFragmentManager().getFragments();
CustomFragment result = null;
if (fragments != null) {
for (Fragment fragment : fragments) {
if (fragment != null && fragment.isVisible()) {
try {
CustomFragment customFragment = (CustomFragment) customFragment;
if (customFragment.getClassName().equals(fragmentName)))
result = customFragment;
} catch (ClassCastException e) {
}
}
}
}
return result ;
}
I would do something like that:
1. Create a interface like this.
public interface ReturnMyself {
Fragment returnMyself();
}
2. All fragments inside ViewPagers should implements this one.
3. Add OnPageChangeListener to your main VP. So You will always know current position.
4. Add OnPageChangeListener your inner(s) VP so you will know which one is there on screen.
5. Add method to your adapter (both main and inner) which returns your fragment from list passed to it.
public ReturnMyself getReturnMyselfAtPosition()
6. All Fragments should return this in returnMyself()
7. Fragment that has inner fragments should return in returnMyself something like.
Fragment returnMyself() {
return this.myInnerFragmentAdapter.getReturnMyselfAtPosition().returnMyself();
}
8. From main fragment/activity you just call.
this.adapter.getReturnMyselfAtPosition().returnMyself();
And you got your current fragment.
I'm using a SlidingTabLayout (from the Google sources) in my app to navigate between different fragments.
I can swipe between contents just fine, but the problem I'm facing is the title of the Action bar that is acting weird.
Suppose that I have two tabs with two titles ('First Title' and 'Second Title') :
| Action bar |
| First Title | Second Title |
When I first enter the Fragment containing the SlidingTabLayout, the title of the Actionbar is like this :
| Firs... |
| First Title | Second Title |
When I swipe (to the second tab for example), the title of the actionbar becomes :
| Second Title |
| First Title | Second Title |
And stays like this. It seems that the Actionbar takes the title of the last Fragment loaded when I swipe at least once.
What I want is this :
I want to show a 'Main Title' in the Actionbar that never changes no matter what the title in the SlidingTabLayout is.
Here are some portions of my code:
** Fragment containing the SlidingTabLayout:
private String mainTitle;
....
#Override
public void onCreate(Bundle savedInstance) {
super.onCreate(savedInstance);
// The mainTitle is given to this fragment by another fragment
Bundle args = getArguments();
if (args != null) {
mainTitle = args.getString("TITLE");
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.layout, container, false);
mViewPager = (ViewPager) rootView.findViewById(R.id.viewpager);
mSlidingLayout = (SlidingTabLayout) rootView.findViewById(R.id.sliding_layout);
List<Fragment> listFragments = new ArrayList<>();
List<String> listTitles = new ArrayList<>();
for (int i = 0; i < mSize; i++) {
Bundle bundle = new Bundle();
....
listFragments.add(Fragment.instanciate(getActivity(), CustomFragment.class.getName(), bundle));
listTitles.add("...");
}
mViewPager.setAdapter(new PagerAdapter(getChildFragmentManager(), listFragments, listTitles));
mSlidingLayout.setDistributedEvenly(true);
mSlidingLayout.setViewPager(mViewPager);
return rootView;
}
#Override
public void onResume() {
super.onResume();
// I TRY TO SET THE TITLE HERE !!
getActivity().getSupportActionBar().setTitle(mainTitle);
}
** Adapter:
class PagerAdapter extends FragmentPagerAdapter {
List<Fragment> fragments;
List<String> titles;
...
#Override
public Fragment getItem(int position) {
Fragment fragment = fragments.get(position);
return fragment;
}
#Override
public CharSequence getPageTitle(int position) {
// maybe the problem is in this method.
// this method is supposed to provide the titles for the tab
// but maybe it's also changing the actionbar title
return titles.get(position);
}
I'm setting the title of the ActionBar in the onResume method every time I enter a Fragment.
Thanks !
EDIT 1 :
I tried using the new SlidingTabLayout which I've got from the Google I/O, and the result is still the same !
It seems that the Title in the ActionBar is a hint at first, and then it changes to the last loaded fragment when I swipe to another fragment.
It's like it's loading fragment, and each time a fragment is loaded, the title in the ActionBar is overridden with the title of that fragment.
EDIT 2 :
I changed my code to post the latest version of it (I'm using now a SlidingTabLayout) and to show how I get my mainTitle.
you can use below code it works fine for me
public class MatchesActivity extends AppCompatActivity implements ActionBar.TabListener {
SectionsPagerAdapter mSectionsPagerAdapter;
ViewPager mViewPager;
FloatingActionButton skipButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_matches);
final ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setDisplayShowHomeEnabled(true);
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) findViewById(R.id.pager);
skipButton = (FloatingActionButton) findViewById(R.id.skip_next);
skipButton.setVisibility(View.GONE);
mViewPager.setAdapter(mSectionsPagerAdapter);
mViewPager.setOffscreenPageLimit(1);
mViewPager.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
return false;
}
});
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
actionBar.setSelectedNavigationItem(position);
}
});
actionBar.addTab(
actionBar.newTab().setText("MATCHES")
.setTabListener(this));
actionBar.addTab(
actionBar.newTab().setText("PINS")
.setTabListener(this));
actionBar.addTab(
actionBar.newTab().setText("CHATS")
.setTabListener(this));
}
#Override
public void onBackPressed() {
startActivity(new Intent(MatchesActivity.this, MainActivity.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK & Intent.FLAG_ACTIVITY_CLEAR_TOP));
MatchesActivity.this.finish();
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
onBackPressed();
}
return super.onOptionsItemSelected(item);
}
#Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
mViewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
}
#Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
}
public class SectionsPagerAdapter extends FragmentStatePagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new MatchesFragment();
case 1:
return new PinsFragment();
case 2:
return new ConversationFragment();
default:
return new MatchesFragment();
}
}
#Override
public int getCount() {
return 3;
}
#Override
public CharSequence getPageTitle(int position) {
Locale l = Locale.getDefault();
switch (position) {
case 0:
return getString(R.string.title_section1).toUpperCase(l);
case 1:
return getString(R.string.title_section2).toUpperCase(l);
case 2:
return getString(R.string.title_section3).toUpperCase(l);
}
return null;
}
}
}
I am posting here a code of mine.It worked for me.Make required changes in it and try it.I used PagerSlidingTabStrip library in it.
public class DealFragment extends Fragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view= inflater.inflate(R.layout.fragment_deal, container, false);
ViewPager viewPager = (ViewPager) view.findViewById(R.id.viewpager);
viewPager.setAdapter(new SampleFragmentPagerAdapter(getChildFragmentManager()));
PagerSlidingTabStrip tabsStrip = (PagerSlidingTabStrip)view.findViewById(R.id.tabs);
tabsStrip.setBackgroundColor(Color.parseColor("#333333"));
// Attach the view pager to the tab strip
tabsStrip.setViewPager(viewPager);
return view;
}
public class SampleFragmentPagerAdapter extends FragmentPagerAdapter {
final int PAGE_COUNT = 2;
private final String tabTitles[] = new String[] { "Today's Deals", "Deals Close By" };
public SampleFragmentPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return PAGE_COUNT;
}
#Override
public android.support.v4.app.Fragment getItem(int position) {
if(position==0)
{
return new TodaysDeal();
}
else
{
return new DealsCloseBy();
}
}
#Override
public CharSequence getPageTitle(int position) {
// Generate title based on item position
return tabTitles[position];
}
}
}
hope it might help you.
The actionBar title doesn't change because onResume() is not called the time you swipe back.
It's because ViewPager doesn't call fragment's onResume the second time. Even though in reality the fragment is resumed.
What you can do is to move the setting of your actionbar title in your parent fragment (that contains pagertabstrip) to onCreateView instead.
For your reference:
** Fragment containing the PagerTabStrip:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.layout, container, false);
mViewPager = (ViewPager) rootView.findViewById(R.id.viewpager);
List<Fragment> listFragments = new ArrayList<>();
List<String> listTitles = new ArrayList<>();
...
// HERE's what you want
final ActionBar actionBar = getActivity().getSupportActionBar();
if (actionBar != null) {
actionBar.setTitle(R.string.main_title);
}
mViewPager.setAdapter(new PagerAdapter(getChildFragmentManager(), listFragments));
return rootView;
}
Found it !
It was really dumb of me (it explains why others didn't have this problem)
I was setting the title also in each of the Fragments (even those contained in the Tabs), so when I swiped, the onResume on those Fragments was called and it changed the title in the ActionBar...
Thank you all for the help, I appreciate it !
I have a static menu on the left side of my screen in my android application.Whenever I click an item from the left menu a fragment will appear along side it.
One of my items on the left menu is the course. In the course fragment contains a viewpager to watch read or listen to the course. You can navigate to one of these three options by side swipe or clicking the corresponding tab on the title bar. This all works fine.
However if I click another item on the left menu bar and navigate back to the course section, it messes up. The onCreateView() is invoked again and therefore adds the tabs to the title bar again and the viewpager. I tried removing all tabs in the onPause() method and removing all views in the viewpager. The tabs no longer be added again, but the pages on the screen are now blank.
Any help is much appreciated.
Here's the class which extends FragmentPagerAdapter
public class TabsPagerAdapter extends FragmentPagerAdapter {
public TabsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int index) {
switch (index) {
case 0:
// Top Rated fragment activity
return new WatchCourseFrag();
case 1:
// Games fragment activity
return new ReadCourseFrag();
case 2:
// Movies fragment activity
return new ListenCourseFrag();
}
return null;
}
#Override
public int getCount() {
// get item count - equal to number of tabs
return 3;
}
}
and here is the tab fragment class:
public class TabFrag2 extends Fragment implements ActionBar.TabListener {
private ViewPager viewPager;
private TabsPagerAdapter mAdapter;
private ActionBar actionBar;
// Tab titles
private String[] tabs = { "Watch", "Read", "Listen" };
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.pager, container, false);
// Initilization
viewPager = (ViewPager) view.findViewById(R.id.pager);
actionBar = ((HomeScreenTablet) getActivity()).getSupportActionBar();
mAdapter = new TabsPagerAdapter(getFragmentManager());
viewPager.setAdapter(mAdapter);
actionBar.setHomeButtonEnabled(false);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// Adding Tabs
// Adding Tabs
for (String tab_name : tabs) {
actionBar.addTab(actionBar.newTab().setText(tab_name)
.setTabListener(this));
}
/**
* on swiping the viewpager make respective tab selected
* */
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
// on changing the page
// make respected tab selected
actionBar.setSelectedNavigationItem(position);
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
viewPager.setOffscreenPageLimit(3);
return view;
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
// on tab selected
// show respected fragment view
viewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
}
public void onPause() {
super.onPause();
viewPager.removeAllViews();
viewPager.removeAllViewsInLayout();
actionBar.removeAllTabs();
}
}
Found the answer:
Changed "TabsPagerAdapter extends FragmentPagerAdapter" to "TabsPagerAdapter extends FragmentStatePagerAdapter"
My application has 3 tabs, each represented by Fragments. There is a custom FragmentPagerAdapter which also implements ActionBar.TabListener. Initially my FragmentPagerAdapter was returning new instances of fragments in public Fragment getItem(int index) method. Also, my TabListener implementation was instantiating Fragments and showing/hiding them in the onTabSelected/onTabUnselected. Unfortunately this approach doesn't fit me well, because in this case each Fragment is actually created twice. What I want is to create it once and then just hide/show, when necessary.
For that I have instantiated my fragment earlier in the activity and passed them to the PagerAdapter:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_tabs_layout);
List<Fragment> fragments = initTabFragments();
ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
final ActionBar actionBar = getSupportActionBar();
TabsPagerAdapter pagerAdapter = new TabsPagerAdapter(this, getSupportFragmentManager(), fragments, viewPager);
viewPager.setAdapter(pagerAdapter);
actionBar.setHomeButtonEnabled(false);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
for (int tab_name : tabs) {
Tab tab = actionBar.newTab();
actionBar.addTab(tab.setText(tab_name).setTabListener(pagerAdapter));
}
...
}
private List<Fragment> initTabFragments() {
List<Fragment> fragments = new ArrayList<Fragment>(3);
fragments.add(SherlockFragment.instantiate(this, TabFragment1.class.getName()));
fragments.add(SherlockFragment.instantiate(this, TabFragment2.class.getName()));
fragments.add(SherlockFragment.instantiate(this, TabFragment3.class.getName()));
return fragments;
}
Then in the PagerAdapter I just return fragment:
#Override
public Fragment getItem(int index) {
return fragments.get(index);
}
To handle Tab selection there is the following code:
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
int position = tab.getPosition();
String tag = "android:switcher:" + viewPagerId + ":" + position;
Fragment fragment = context.getSupportFragmentManager().findFragmentByTag(tag);
switch (position) {
case 0:
if (fragment == null) {
ft.add(viewPagerId, fragments.get(0), tag);
} else {
// If it exists, simply attach it in order to show it
ft.show(fragments.get(0));
}
break;
}
case 1:
if (fragment == null) {
ft.add(viewPagerId, fragments.get(1), tag);
} else {
// If it exists, simply attach it in order to show it
ft.show(fragments.get(1));
}
break;
}
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
switch (tab.getPosition()) {
case 0:
ft.hide(fragments.get(0));
break;
case 1:
ft.hide(fragments.get(1));
break;
}
}
In this case each fragment is instantiated once. And the first tab is shown correctly, but when I switch between tabs second and third tabs are empty (just white background). Why?
I was trying to add fragment to a container android.R.id.content, but exception was thrown: Can't change container ID of fragment.... I found out that it is because when Fragment is returned in getItem method it is added to a viewPager container. That why I use viewPagerId when add fragment to a transaction.
Any help would be greatly appreciated.
Have a ExampleFragment class defined that extends Fragment. Something like shown below
public class ExampleFragment extends Fragment {
private View categoryView;
private ViewPager mViewPager;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
categoryView = inflater.inflate(R.layout.viewpager, container, false);
mViewPager = (ViewPager) categoryView.findViewById(R.id.pager);
return categoryView;
}
public ViewPager returnViewPager() {
return mViewPager;
}
}
main_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:layout_width="fill_parent"
android:layout_height="wrap_content"
tools:context=".ExampleFragmentActivity" >
<fragment
android:id="#+id/main_fragment"
android:name="somepackage.ExampleFragment"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
And then add this in the onCreate() of ExampleFragmentActivity
ExampleFragment fragment = (ExampleFragment) getSupportFragmentManager().findFragmentById(R.id.main_fragment);
mViewPager = fragment.returnViewPager();
fragmentManager = getSupportFragmentManager();
Am using SlidingMenu which uses different types of Fragment/SherlockFragment as menu.
Am in need of SherlockFragmentActivity, as I am using Tabs inside menu.
How to use SherlockFragmentActivity with SlidingMenu?
If it's not possible, is there any other library through which I can have sliding menu & tabs inside sliding menu? Am not sure if it can be achieved using android-menudrawer
Edit : want to achieve the below. ie, Tabs inside Menu
when menu button is clicked, menu Fragment is opened, inside Menufragment I want to add tabs.
EDIT:
I wanted to take this design as a challenge and see what is the result:
As I suggested in the comments, I used PagerSlidingTabStrip andViewPager.
I did not include the ActionBarSherlock, but if needed that will be easy to modify: the MainActivity will be required to extend from SherlockFragmentActivity, and the theme #style/Theme.Sherlock.Light added to manifest file, and that is all.
(Just to be sure you got the idea, PagerSlidingTabStrip is the one who creates the tabs at the bottom)
Here are the steps I took to integrate PagerSlidingTabStrip and ViewPager with SlidingMenu:
1 - Import SlidingMenu library into Eclipse workspace
2 - Import PagerSlidingTabStrip library into Eclipse workspace
3 - Add Android Support Library to your project (and copy the same .jar into SlidingMenu and PagerSlidingTabString libraries, otherwise eclipse might complain)
4 - A minimal example of MainActivity:
public class MainActivity extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
SlidingMenu menu = new SlidingMenu(this);
menu.setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
menu.setBehindOffset(50);
menu.setFadeDegree(0.35f);
menu.attachToActivity(this, SlidingMenu.SLIDING_CONTENT);
menu.setMenu(R.layout.left_menu);
ViewPager pager = (ViewPager) findViewById(R.id.pager);
pager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager()));
PagerSlidingTabStrip tabs = (PagerSlidingTabStrip) findViewById(R.id.tabs);
tabs.setViewPager(pager);
}
}
5 - The layout of sliding menu, R.layout.left_menu:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/left_menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#CCC"
android:orientation="vertical" xmlns:app="http://schemas.android.com/apk/res/org.grec">
<com.astuetz.viewpager.extensions.PagerSlidingTabStrip
android:id="#+id/tabs"
app:shouldExpand="true"
android:layout_width="match_parent"
android:layout_alignParentBottom="true"
android:layout_height="48dip" />
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#id/tabs" />
</RelativeLayout>
6 - The ViewPagerAdapter:
public class ViewPagerAdapter extends FragmentStatePagerAdapter {
private final int PAGES = 3;
private String[] titles={"Tab 1", "Tab 2", "Tab 3"};
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new TabFragment1();
case 1:
return new TabFragment2();
case 2:
return new TabFragment3();
default:
throw new IllegalArgumentException("The item position should be less or equal to:" + PAGES);
}
}
#Override
public CharSequence getPageTitle(int position) {
return titles[position];
}
#Override
public int getCount() {
return PAGES;
}
}
7 - And an example of fragment, TabFragment1.java (the other 2 are similar):
public class TabFragment1 extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_tab_1, container, false);
}
}
8 - And the layout of the fragment R.layout.fragment_tab_1 which is pretty simple, it contains a single TextView, so I won't include it here.
I hope this example addresses your issue and will help you getting started on the right track.
Full source on github: https://github.com/vgrec/SlidingMenuWithViewpager
1) Add SherlockActionBar library to SlidingMenu
2) Change SlidingFragmentActivity extends FragmentActivity to SlidingFragmentActivity extends SherlockFragmentActivity
3) Then add SlidingMenu library to your project and your project should use MyProjectActivity extends SlidingFragmentActivity
You can use SherlockActionBar to create sliding menu with tabs inside
Here sample code to create Tab with view pager
For sliding, you can look at their documentation
public static class TabsAdapter extends FragmentPagerAdapter implements ActionBar.TabListener, ViewPager.OnPageChangeListener {
private final Context mContext;
private final ActionBar mActionBar;
private final ViewPager mViewPager;
private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
static final class TabInfo {
private final Class<?> clss;
private final Bundle args;
TabInfo(Class<?> _class, Bundle _args) {
clss = _class;
args = _args;
}
}
public TabsAdapter(SherlockFragmentActivity activity, ViewPager pager) {
super(activity.getSupportFragmentManager());
mContext = activity;
mActionBar = activity.getSupportActionBar();
mViewPager = pager;
mViewPager.setAdapter(this);
mViewPager.setOnPageChangeListener(this);
}
public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args) {
TabInfo info = new TabInfo(clss, args);
tab.setTag(info);
tab.setTabListener(this);
mTabs.add(info);
mActionBar.addTab(tab);
notifyDataSetChanged();
}
#Override
public int getCount() {
return mTabs.size();
}
#Override
public Fragment getItem(int position) {
TabInfo info = mTabs.get(position);
return Fragment.instantiate(mContext, info.clss.getName(), info.args);
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
mActionBar.setSelectedNavigationItem(position);
}
#Override
public void onPageScrollStateChanged(int state) {
}
public void onTabSelected(Tab tab, FragmentTransaction ft) {
Object tag = tab.getTag();
for (int i = 0; i < mTabs.size(); i++) {
if (mTabs.get(i) == tag) {
mViewPager.setCurrentItem(i);
}
}
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}
}
mViewPager = new ViewPager(this);
mViewPager.setId(R.id.pager);
setContentView(mViewPager);
ab.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
mTabsAdapter = new TabsAdapter(this, mViewPager);
mTabsAdapter.addTab(ab.newTab().setText("tab1"), FirstFragment.class, null);
mTabsAdapter.addTab(ab.newTab().setText("tab2"), SecondFragment.class, null);
mTabsAdapter.addTab(ab.newTab().setText("tab3"), ThirdFragment.class, null);
As what you have said the SlidingMenu uses fragments and you want to to have Tabs inside a Menu. For me, using Tabs inside a Menu needs nested fragments which means that you can't use SherlockFragmentActivity since it is like the Main Activity. You can't contain an Activity inside a fragment.
I have not implemented this kind of approach but maybe you can try implementing TabListener inside a fragment.
I am using this code for my activity not the fragment,
class MainActivity extends SherlockFragmentActivity implements ActionBar.TabListener
{
}
maybe you can have,
class FragmentA extends SherlockFragment implements TabListener{
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
// TODO Auto-generated method stub
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
// TODO Auto-generated method stub
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
// TODO Auto-generated method stub
}
}