I have been struggling a few days with building functionality in my application that allows a user to remove a page/Fragment from a Viewpager, I have tried changing my FragmentPageAdapter to a FragmentStatePageAdapter and override my getItemPosition() to return POSITION_NONE; however this still does not allow me to remove the Fragment dynamically when I call fragments.remove(page);
pageAdapter.notifyDataSetChanged();
has anyone done this successfully before? seems like it should be really simple but it hasnt been, below is my fragment adapter, any help would go a long way thanks!
class MyPageAdapter extends FragmentStatePagerAdapter {
private List<Fragment> fragments;
public MyPageAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public Fragment getItem(int position) {
return this.fragments.get(position);
}
#Override
public float getPageWidth(int position) {
if (position == 0) {
return (0.75f);
} else {
return (1f);
}
}
#Override
public CharSequence getPageTitle(int position) {
MyFragment theFragment = (MyFragment) this.fragments.get(position);
return theFragment.returnTitle();
}
#Override
public int getCount() {
return this.fragments.size();
}
}
Use Jake Wharton's ViewPagerIndicator. It also has an open source sample application with this funcionality (add and remove fragments from options menu).
Put this into you adapter:
public void setCount(int count) {
if (count > 0 && count <= 10) {
mCount = count;
notifyDataSetChanged();
}
}
And this is adding a page:
if (mAdapter.getCount() < 10) {
mAdapter.setCount(mAdapter.getCount() + 1);
mIndicator.notifyDataSetChanged();
}
This is how to remove a page:
if (mAdapter.getCount() > 1) {
mAdapter.setCount(mAdapter.getCount() - 1);
mIndicator.notifyDataSetChanged();
}
Related
I have tried with so many given examples but nothing worked for me. When I am trying to remove a page dynamically from viewPager then I am getting exception:
Cannot setMaxLifecycle for Fragment not attached to FragmentManager
My PagerAdapter is given below:
public class MyPagerAdapter extends FragmentStatePagerAdapter {
private final Context mContext;
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
long baseId = 0;
public MyPagerAdapter(Context context, FragmentManager fm) {
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
mContext = context;
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
public void addFragment(Fragment fragment, String title, int position) {
mFragmentList.add(position, fragment);
mFragmentTitleList.add(position, title);
}
public void removeFragment(int position) {
mFragmentList.remove(position);
mFragmentTitleList.remove(position);
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
And to remove page I am calling method like
myPagerAdapter.removeFragment(viewPager.getCurrentItem());
I have tried with below given method too, but still getting exception
#Override
public int getItemPosition (Object object) {
int index = mFragmentList.indexOf (object);
if (index == -1)
return POSITION_NONE;
else
return index;
}
Try Handler().post { myPagerAdapter.removeFragment(..) }
If you are running this method for some reason on another thread, make sure to put Handler(Looper.getMainLooper()).post { ... } instead.
The problem is that you might be doing a transaction while the ViewPager itself is handling an animation or its own state saving. Doing the above allows us to completely run the ViewPager's state saving completely before doing a transaction.
I had the same problem, doing this fixed it for me.
Specifically, this is my code that I am running inside of onPageScrollStateChanged(..) (presumably you are doing the same)
override fun onPageScrollStateChanged(state: Int) {
if (upcomingPage == 0 && state == ViewPager.SCROLL_STATE_IDLE) {
homePagerRoot?.post { vpAdapter.clearExtraFragments() }
}
}
hope this is not too late
the exception happens because you didn't access the right fragment manager attached to your fragment so you should use
YourFragment.fragmentManager?.beginTransaction()!!.remove(YourFragment as Fragment).commitNowAllowingStateLoss()
sorry for kotlin try to convert back to java
I use FragmentStatePagerAdapter to manage my ViewPager. I have overwritten "getItemPosition" function. Here is my code:
mViewPager.setAdapter(new FragmentStatePagerAdapter(fm) {
#Override
public Fragment getItem(int position) {
Crime c = mCrimes.get(position);
return CrimeFragment.newInstance(c.getmId());
}
#Override
public int getCount() {
return mCrimes.size();
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
});
But the problem is that when I delete the first fragment, the last fragment will be deleted, but others work well. I don't know why.
I created a viewpager and an adapter with fragments that contains 2 views. I want to make the second view in the fragment invisible when I click on the frist one. I implemented this method on my view to handle the click events
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
ImageFragment fragment = (ImageFragment) ImageGallery.fragmentmanager.findFragmentByTag("android:switcher:"+R.id.imagerpager+":"+position);
if (fragment != null) {
fragment.setTransparency();
}
return super.onSingleTapConfirmed(e);
}
All fragments I get are null. To make sure that my objects are not null, following works but for the wrong(preloaded) fragments
ImageFragment fragment = (ImageFragment) ImageGallery.fragmentmanager.findFragmentById(R.id.imagepager);
I`m quite sure I have to implement a FragmentTransition to my adapter to make fragmentmanager.findFragmentByTag work. But I could not figure out how to do that. My adapter looks like
public class GalleryAdapter extends FragmentStatePagerAdapter {
private int mCount = pictureUrlList.size();
public GalleryAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
ImageFragment fragment = ImageFragment.newInstance(position);
return fragment;
}
#Override
public int getCount() {
return mCount;
}
public void setCount(int count) {
if (count > 0 && count <= 10) {
mCount = count;
notifyDataSetChanged();
}
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}
Can anybody show me how to do that?
Let's say I have two pages in my viewpager, is it any way to move from page 2 to page 1, but doing this like user is accesing page 3 (with all the animation)
You can achieve this by using a custom FragmentPagerAdapter that provides the same fragments over and over again:
private class EndlessPagerAdapter extends FragmentPagerAdapter {
private EndlessPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
if (position % 2 == 0) {
return fragmentOne;
} else {
return fragmentTwo;
}
}
#Override
public int getCount() {
return Integer.MAX_VALUE;
}
}
Set the adapter for your ViewPager and start somewhere in the middle to allow almost endless swiping in either direction:
mViewPager.setAdapter(new EndlessPagerAdapter(getChildFragmentManager()));
mViewPager.setCurrentItem(Integer.MAX_VALUE/2);
I'm making an adroid application with viewpager and fragments. I want to make an option to add or remove fragment pages to the pager dynamically. I have a custom FragmentPagerAdapter:
public class MyAdapter extends FragmentPagerAdapter implements TitleProvider{
protected final List<PageFragment> fragments;
/**
* #param fm
* #param fragments
*/
public MyAdapter(FragmentManager fm, List<PageFragment> fragments) {
super(fm);
this.fragments = fragments;
}
public void addItem(PageFragment f){
fragments.add(f);
notifyDataSetChanged();
}
public void addItem(int pos, PageFragment f){
for(int i=0;i<fragments.size();i++){
if(i>=pos){
getSupportFragmentManager().beginTransaction().remove(getSupportFragmentManager().findFragmentByTag(getFragmentTag(i))).commit();
}
}
fragments.add(pos,f);
notifyDataSetChanged();
pager.setAdapter(this);
pager.setCurrentItem(pos);
}
public void removeItem(int pos){
getSupportFragmentManager().beginTransaction().remove(getSupportFragmentManager().findFragmentByTag(getFragmentTag(pos))).commit();
for(int i=0;i<fragments.size();i++){
if(i>pos){
getSupportFragmentManager().beginTransaction().remove(getSupportFragmentManager().findFragmentByTag(getFragmentTag(i))).commit();
}
}
fragments.remove(pos);
notifyDataSetChanged();
pager.setAdapter(this);
if(pos<fragments.size()){
pager.setCurrentItem(pos);
}else{
pager.setCurrentItem(pos-1);
}
}
#Override
public Fragment getItem(int position) {
return fragments.get(position).toFragment();
}
#Override
public int getCount() {
return fragments.size();
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
private String getFragmentTag(int pos){
return "android:switcher:"+R.id.pager+":"+pos;
}
public String getTitle(int position) {
String name = fragments.get(position).getName();
if(name.equals(""))
return "- "+position+" -";
return name;
}
}
I can remove any fragment except the 0. when I try to remove it I got a NullPointerException on the notifyDataSetChanged(); or on the pager.setAdapter(this); if i comment out the notify.
I also got a NullPointerException when I try to insert a new page. When I add the new page to the end of the list it's working fine.
I even tried to readd the fragments in the insert with this after the fragments.add(pos,f)
for(int i=0;i<fragments.size();i++){
if(i>=pos){
getSupportFragmentManager().beginTransaction().add(fragments.get(i).toFragment(),getFragmentTag(i-1)).commit();
}
}
if I use getFragmentTag(i-1) I got nullpointer again. With using just i I got illegalstateexception because can not modify fragment's tag. With beginTransaction().add(pager.getId,fragments.get(i).toFragment()) it is still nullpointer...
My question is: what am I doing wrong, and how can it be done properly? (and maybe: from where notyfyDataSetChanged() get the data when causes nullpointerexception?)
This is what I used to get around the problem with fragments being tagged when instantiated and the fact that previously tagged fragments cannot change position within the ViewPager when trying to add or remove pages, i.e. getting the exception:
java.lang.IllegalStateException: Can't change tag of fragment
MyFragment{4055f558 id=0x7f050034 android:switcher:2131034164:3}: was
android:switcher:2131034164:3 now android:switcher:2131034164:4
This adapter is created with a list of all pages upfront and you can then use setEnabled(int position, boolean enabled) to disable or enable certain pages which hides or shows these pages in the ViewPager.
It works by maintaining an internal list of all fragments but exposing to the ViewPager and mapping the positions of enabled fragments only.
public class DynamicFragmentPagerAdapter extends FragmentPagerAdapter {
public final ArrayList<Fragment> screens = new ArrayList<Fragment>();
private Context context;
private List<AtomicBoolean> flags = new ArrayList<AtomicBoolean>();
public DynamicFragmentPagerAdapter(FragmentManager fm, Context context, List<Class<?>> screens) {
super(fm);
this.context = context;
for(Class<?> screen : screens)
addScreen(screen, null);
notifyDataSetChanged();
}
public DynamicFragmentPagerAdapter(FragmentManager fm, Context context, Map<Class<?>, Bundle> screens) {
super(fm);
this.context = context;
for(Class<?> screen : screens.keySet())
addScreen(screen, screens.get(screen));
notifyDataSetChanged();
}
private void addScreen(Class<?> clazz, Bundle args) {
screens.add(Fragment.instantiate(context, clazz.getName(), args));
flags.add(new AtomicBoolean(true));
}
public boolean isEnabled(int position) {
return flags.get(position).get();
}
public void setEnabled(int position, boolean enabled) {
AtomicBoolean flag = flags.get(position);
if(flag.get() != enabled) {
flag.set(enabled);
notifyDataSetChanged();
}
}
#Override
public int getCount() {
int n = 0;
for(AtomicBoolean flag : flags) {
if(flag.get())
n++;
}
return n;
}
#Override
public Fragment getItem(int position) {
return screens.get(position);
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE; // To make notifyDataSetChanged() do something
}
#Override
public void notifyDataSetChanged() {
try {
super.notifyDataSetChanged();
} catch (Exception e) {
Log.w("", e);
}
}
private List<Fragment> getEnabledScreens() {
List<Fragment> res = new ArrayList<Fragment>();
for(int n = 0; n < screens.size(); n++) {
if(flags.get(n).get())
res.add(screens.get(n));
}
return res;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
// We map the requested position to the position as per our screens array list
Fragment fragment = getEnabledScreens().get(position);
int internalPosition = screens.indexOf(fragment);
return super.instantiateItem(container, internalPosition);
}
#Override
public CharSequence getPageTitle(int position) {
Fragment fragment = getEnabledScreens().get(position);
if(fragment instanceof TitledFragment)
return ((TitledFragment)fragment).getTitle(context);
return super.getPageTitle(position);
}
public static interface TitledFragment {
public String getTitle(Context context);
}
}
I have a solution that works with a non-fragment based adapter. Take a look at my post and see if it helps.
I can remove any fragment except the 0.
For me it works if just use a >= condition whitin the removeItem's for-loop:
if (i >= pos){
fm.beginTransaction().remove(fragments.get(i).getFragment()).commit();
}