FragmentPagerAdapter getItem wrong position - android

I've got strange problem with FramentPageAdapter
MainActivity.java
#SuppressLint("ValidFragment")
public class MainActivity<DashboardActivity> extends FragmentActivity implements ActionBar.TabListener {
...
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
mTabHost = (FragmentTabHost)findViewById(android.R.id.tabhost);
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
actionBar.setSelectedNavigationItem(position);
}
});
actionBar.addTab(actionBar.newTab().setText(mSectionsPagerAdapter.getPageTitle(0)).setTabListener(this).setIcon(R.drawable.rating_good));
actionBar.addTab(actionBar.newTab().setText(mSectionsPagerAdapter.getPageTitle(1)).setTabListener(this).setIcon(R.drawable.action_search));
actionBar.addTab(actionBar.newTab().setText(mSectionsPagerAdapter.getPageTitle(2)).setTabListener(this).setIcon(R.drawable.action_search));
}
...
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
public Fragment getItem(int position) {
Fragment fragment = null;
switch(position) {
case 0:
fragment = new Fragment0();
break;
case 1:
fragment = new Fragment1();
break;
case 2:
fragment = new Fragment2();
break;
}
return fragment;
}
#Override
public int getCount() {
return 3;
}
/*
* Title
*/
#Override
public CharSequence getPageTitle(int position) {
Locale l = Locale.getDefault();
switch (position) {
case 0:
return getString(R.string.title_section0).toUpperCase(l);
case 1:
return getString(R.string.title_section1).toUpperCase(l);
case 2:
return getString(R.string.title_section2).toUpperCase(l);
}
return null;
}
}
public Fragment getItem(int position) return wrong position when i try to switch between 3 tabs. When i create app with 2 tabs only, everything work just fine. Adding more then 2 of them, creating strange problem.
Switch from 0 to 1 position - works fine, switch from 1 to 0 - works fine, switch from 1 to 2 posiition - works fine, but, when i try to go back from 2 to 1 position, public Fragment getItem(int position) - int position return "0" instead of "1".
Does anyone help me with this ?

Ok, I've found the solution.
First of all, the getItem "int position", doesn't indicate current display fragment.
To display 3 or more tabs, without unload firts fragment You must added this line:
mViewPager.setOffscreenPageLimit(3);
End of story...

You just need:
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
Log.d("test", "position = " + position);
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
The position in onPageSelected is what you want.

I had had the same problem, and I used fragment inside each tabs;
So instead of
mViewPager.setAdapter(new MainTabs(getFragmentManager()));
use this one:
mViewPager.setAdapter(new MainTabs(getChildFragmentManager()));

In case anyone else has this issue, I solved it by not using the position given. Instead, I get a List of Fragment with getSupportFragmentManager, loop through, and check if the current Fragment is instanceof my desired Fragment.

Related

Android ViewPager inside Fragment loaded only once v2

My question is similtar to this:
Android ViewPager Inside Fragment Loaded Only Once
The difference is that its answer doesn't work for me.
I have 4 fragments. Inside one of these fragments there is a Fragment X with ViewPager that switch betwen two other fragments. The first time I load the FragmentX the ViewPager works fine. The problem is if I switch to one of the other 4 fragments, when I go back to the FragmentX the ViewPager has nothing inside.
Fragment X
...
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_campus, container, false);
TabLayout tabLayout = (TabLayout)view.findViewById(R.id.tab_layout);
tabLayout.addTab(tabLayout.newTab().setText("Mapa"));
tabLayout.addTab(tabLayout.newTab().setText("Eventos"));
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
vpPager = (ViewPager)view.findViewById(R.id.vpPager);
adapterViewPager = new PagerAdapter(getChildFragmentManager());
vpPager.setAdapter(adapterViewPager);
vpPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
vpPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
return view;
}
PageAdapter
public class PagerAdapter extends FragmentPagerAdapter {
private int NUM_ITEMS = 2;
public PagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
// Returns total number of pages
#Override
public int getCount() {
return NUM_ITEMS;
}
// Returns the fragment to display for that page
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return MapFragment.newInstance(0);
case 1:
return EventosFragment.newInstance(1);
default:
return null;
}
}
// Returns the page title for the top indicator
#Override
public CharSequence getPageTitle(int position) {
return "Page " + position;
}
}
MainActivity
public boolean onNavigationItemSelected(MenuItem item) {
int id = item.getItemId();
...
if (...) {
if (campusFragment == null) // the problem is not solved by commenting this condition
campusFragment = new CampusFragment(); // Fragment X
fragmentManager.beginTransaction().replace(R.id.content_frame, campusFragment).commit();
}
}

how not to show some tabs on tabbed activity?

I have the following activity wich show different tabs.
public class BagContent extends FragmentActivity implements ActionBar.TabListener, CosmeListener {
String[] profileTypes;
SectionsPagerAdapter mSectionsPagerAdapter;
ViewPager mViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_bag_content);
mViewPager = (ViewPager) findViewById(R.id.pager);
final ActionBar actionBar = getActionBar();
actionBar.setTitle(getString(R.string.title_activity_bag_content) + " [" + bagName + "]");
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
mSectionsPagerAdapter = new SectionsPagerAdapter(getFragmentManager());
mViewPager.setAdapter(mSectionsPagerAdapter);
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
actionBar.setSelectedNavigationItem(position);
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) {
actionBar.addTab(actionBar.newTab().setText(mSectionsPagerAdapter.getPageTitle(i)).setTabListener(this));
}
}
#Override
protected void onResume() {
super.onResume();
// TODO Auto-generated method stub
}
#Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
// When the given tab is selected, switch to the corresponding page in
// the ViewPager.
mViewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
}
#Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
}
/**
* A {#link FragmentPagerAdapter} that returns a fragment corresponding to
* one of the sections/tabs/pages.
*/
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new FormatShortFragment();
case 1:
return new FormatLongFragment();
case 2:
return new FormatPlantFragment();
}
return null;
}
#Override
public int getCount() {
// Show 3 total pages.
return 3;
}
#Override
public CharSequence getPageTitle(int position) {
Locale l = Locale.getDefault();
switch (position) {
case 0:
return getString(R.string.title_bag_content_format_short).toUpperCase(l);
case 1:
return getString(R.string.title_bag_content_format_long).toUpperCase(l);
case 2:
return getString(R.string.title_bag_content_format_plant).toUpperCase(l);
}
return null;
}
}
I have and array wiht different profile types, depending on each type i have to show some tabs or others.
Actually all tabs are been showing for every profile type. I tried to insert manually the tabs on the actionbar, but then the tabs are not showing the fragments correctly. Other problem is that navigation breaks when i go to an inexistent tab on the right edge.
I need help with this, how can I select just 1 tab for a specific profile type?
for example, how can I show just the title_bag_content_format_long and his fragment for profileTypes[1]?
Any walk aroun can be good, for example remove some tabs from some profile types or any other thing.
Thanks a lot guys, I really need help with this.
EDITED
I have made some modifications:
Added a profile type parameter to SectionsPagerAdapter constructor:
private String profileType;
public SectionsPagerAdapter(FragmentManager fm, String profileType) {
super(fm);
this.profileType = profileType;
}
Then i have modified this other methods to control the profile type:
#Override
public Fragment getItem(int position) {
if (this.profileType.equals(profileTypes[1])){
switch (position) {
case 0:
return new FormatPlantFragment();
}
} else {
switch (position) {
case 0:
return new FormatShortFragment();
case 1:
return new FormatLongFragment();
case 2:
return new FormatPlantFragment();
}
}
return null;
}
#Override
public int getCount() {
if (this.profileType.equals(profileTypes[1])){
return 1;
}
return 3;
}
#Override
public CharSequence getPageTitle(int position) {
Locale l = Locale.getDefault();
if (this.profileType.equals(profileTypes[1])){
switch (position) {
case 0:
return getString(R.string.title_bag_content_format_plant).toUpperCase(l);
}
} else {
switch (position) {
case 0:
return getString(R.string.title_bag_content_format_short).toUpperCase(l);
case 1:
return getString(R.string.title_bag_content_format_long).toUpperCase(l);
case 2:
return getString(R.string.title_bag_content_format_plant).toUpperCase(l);
}
}
return null;
}
This is working correctly and do the filter by profile type more versatile.
Thanks to Filpe.costa01 that gave me the push that I needed.
You're returning all the tabs because your method
#Override
public int getCount() {
// Show 3 total pages.
return 3;
}
return 3 tabs. Maybe you could use the SectionsPagerAdapter constructor to include the number of tabs you'd like, according to each profile wanted.
Something like this:
private mNumOfTabs;
public SectionsPagerAdapter(FragmentManager fm, int numberOfTabs) {
super(fm);
mNumOfTabs = numberOfTabs;
}
and you should replace your getCount() method for this one:
#Override
public int getCount() {
// Show 3 total pages.
return mNumOfTabs;
}
Organizing your profiles in an ascendant way (low profile = small number of tabs), you can use this method like it's now
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new FormatShortFragment();
case 1:
return new FormatLongFragment();
case 2:
return new FormatPlantFragment();
}
return null;
}
Otherwise, you should think about the profiles and implement a new logic to retrieve the correct tabs for each profile.

How can I set fragment Id in some of the override method of FragmentPagerAdapter?

I have a FragmentPagerAdapter, and in the fragment class I have a public method that I need to use from main activity.
In this point I don't know how to access the fragment that contains that method. Can I modify something on the class below to set id or something to fragments?
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new Fragment_1();
case 1:
return new Fragment_2();
case 2:
return new Fragment_3();
}
return null;
}
#Override
public int getCount() {
return 3;
}
#Override
public CharSequence getPageTitle(int position) {
Locale l = Locale.getDefault();
switch (position) {
case 0:
return getString(R.string.fg_1).toUpperCase(l);
case 1:
return getString(R.string.fg_2).toUpperCase(l);
case 2:
return getString(R.string.fg_3).toUpperCase(l);
}
}
return null;
}
}
How can I set a Fragment id, or Fragment tag or something to acces the fragment later?
Any other way to access fragment methods could be good.
EDITED WITH MORE INFORMATION:
I'm really lost with this. But, I have a ViewPager too, these fragments are associated to actionBar Tabs.
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener(){
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
actionBar.setSelectedNavigationItem(position);
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) {
actionBar.addTab(actionBar.newTab().setText(mSectionsPagerAdapter.getPageTitle(i)).setTabListener(this));
}
Is there any way to communicate with the fragments?
Like so :
switch (position) {
case 0:
Fragment f = new Fragment_1();
f.setId(...);
return f;
case 1:
...
}
This assumes you have a setId method in your fragment.
What I do however is define a static string TAG per fragment, I can then access it with Fragment_1.TAG.

viewPager adapter null pointer exception

I got an error while setting adapter, i only have one folder for layouts
and to tabs fragments on activity_main , i have searched in many websites but no results. Please help!
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = this;
mTitle = getTitle();
navMenuTitle = getResources().getStringArray(R.array.titles);
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter); //Null Pointer Exception
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
actionBar.setSelectedNavigationItem(position);
getSupportActionBar().setTitle(navMenuTitle[position]);
}
});
this is the SectionAdapter
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a PlaceholderFragment (defined as a static inner class below).
switch(position){
case 0:
return MainGenFragment.newInstance(position + 1);
case 1:
return new FragmentMyPwd();
}
return null;
}
#Override
public int getCount() {
// Show 3 total pages. Now Changed to 2.
return 2;
}
#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;
}
}
You need to add setContentView(R.layout.yourlayout) in onCreate(). Without that, mViewPager will be null because it is not found with findViewById().

Navigating between Fragments while using a ViewPager

I've looked at so many questions here that I don't even know exactly what I'm looking for.
I have a simple app that uses a ViewPager. It has 3 tabs and there is a fragment for each tab. The first fragment contains a ListView. I want to be able to click on an element in the ListView and have that bring me to a different fragment.
So basically I want to remove the fragment that contained the ListView once an element is clicked and add in a new fragment. I've tried to do this in a few different ways with none working.
The last thing I tried was to edit the TabsPageAdapter once an element was clicked which pretty much works except when I press the back button it exits the app. Also it doesn't seem like the cleanest way of doing this.
TabsPagerAdapter
public class TabsPagerAdapter extends FragmentStatePagerAdapter {
SherlockFragment mf;
TalkingPointsFragment tpf;
ContactFragment gf;
int mode = 0;
public TabsPagerAdapter(FragmentManager fm) {
super(fm);
mf = new CanvassFragment();
tpf = new TalkingPointsFragment();
gf = new ContactFragment();
}
public TabsPagerAdapter(FragmentManager fm, int mode)
{
super(fm);
if(mode == 0)
{
mf = new CanvassFragment();
tpf = new TalkingPointsFragment();
gf = new ContactFragment();
}
else if(mode == 1)
{
mf = new ContactFragment();
tpf = new TalkingPointsFragment();
gf = new ContactFragment();
}
}
#Override
public SherlockFragment getItem(int index) {
switch (index) {
case 0:
return mf;
case 1:
return tpf;
case 2:
return gf;
}
return null;
}
#Override
public int getCount() {
// get item count - equal to number of tabs
return 3;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object)
{
}
}
The onclick code:
ViewPager viewp = (ViewPager) getActivity().findViewById(R.id.pager);
TabsPagerAdapter mAdapter = new TabsPagerAdapter(getActivity().getSupportFragmentManager(),1);
viewp.setAdapter(mAdapter);
mAdapter.notifyDataSetChanged();
layout_main.xml
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:actionBarTabStyle="#drawable/actionbar_tab_indicator">
I FragmentStatePagerAdapter too and when a user selects map from the ActionBar, I add the GoogleMapsFragment on top of the FragmentStatePagerAdapter:
// create a new map
mapsFragment = GoogleMapFragment.newInstance();
// Then we add it using a FragmentTransaction.
FragmentTransaction fragmentTransaction = getSupportFragmentManager()
.beginTransaction();
fragmentTransaction.add(android.R.id.content, mapsFragment,
FRAGMENT_MAP_TAG);
fragmentTransaction.commit();
For your case you would probably need to add it to the backstack too, which I have not because in my app the user has to navigate back using the ActionBar.
Thought this approach could work for you too when a user selects an item from your list.
Of course this has the disadvantage of not being able to use the FragmentStatePagerAdapter until the user navigates back. So I am not sure wether that would be acceptable for your app.
Ok, so this took a bit more code that I imagined. Hope you get the idea:
public class MainClass extends FragmentActivity implements CanvassCallback {
// save a single reference to ViewPager and TabsPagerAdapter
private ViewPager mViewPager;
private TabsPagerAdapter mAdapter;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.mViewPager = (ViewPager) findViewById(R.id.pager);
this.mAdapter = new TabsPagerAdapter(getSupportFragmentManager(), this);
mViewPager.setAdapter(mAdapter);
...
}
// from the CanvassCallback interface
public void itemSelected() {
mAdapter.canvassSelected();
mAdapter.notifyDataSetChanged();
}
#Override
public void onBackPressed() {
if (mViewPager.getCurrentItem() == 0 && mAdapter.isCanvassSelected() {
mAdapter.canvassSelected();
mAdapter.notifyDataSetChanged();
}
else {
super.onBackPressed();
}
}
}
Mockup of your CanvassFragment showing the callback
public class CanvassFragment extends SherlockFragment {
public interface CanvassCallback {
public void itemSelected();
}
private CanvassCallback canvassCallback;
public void setCanvassCallback(CanvassCallback canvassCallback) {
this.canvassCallback = canvassCallback;
}
...
// The onClick of your item
private void onClick() {
// notify your activity that an item was selected
canvassCallback.itemSelected();
}
}
The registeredFragments are not strictly needed but I think it provides some value if your need to call methoods on your Fragment from activity.
public class TabsPagerAdapter extends FragmentStatePagerAdapter {
// see upvoted answer from http://stackoverflow.com/questions/8785221/retrieve-a-fragment-from-a-viewpager
SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();
private boolean canvassSelected = false;
private CanvassCallback canvassCallback;
public TabsPagerAdapter(FragmentManager fm, CanvassCallback canvassCallback) {
super(fm);
this.canvassCallback = canvassCallback;
}
public void canvassSelected() {
canvassSelected = !canvassSelected;
}
public boolean isCanvassSelected() {
return canvassSelected;
}
#Override
public SherlockFragment getItem(int index) {
switch (index) {
case 0:
if (canvassSelected)
return new ContactFragment();
CanvassFragment canvassFragment = new CanvassFragment();
// this ensures that your Activity gets notified when an item is clicked
canvassFragment.setCanvassCallback(canvassCallback);
return canvassFragment;
case 1:
return new TalkingPointsFragment();
case 2:
return new ContactFragment();
}
return null;
}
#Override
public int getCount() {
// get item count - equal to number of tabs
return 3;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Log.d(TAG, "instantiateItem " + position);
Fragment fragment = (Fragment) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
Log.d(TAG, "destroyItem " + position);
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}
}

Categories

Resources