FragmentPager not updating views - android

I tried to do what the other answers suggested, but when I look at the logs, after getItemPosition is returned with POSITION_NONE, position 0 does not get called for getItem, any idea? I also tried to override destoryItem when getItemPosition is returned, it DOES get called but nothing happens.
public class ProvidersActivityPagerAdapter extends FragmentPagerAdapter {
private ArrayList<String> fragments;
public ProvidersActivityPagerAdapter(FragmentManager fm) {
super(fm);
fragments=new ArrayList<String>();
if(provider.isInitialized()){
fragments.add(BaseDetailsFragment.class.getName());
}else{
fragments.add(BaseAuthFragment.class.getName());
}
this.fm=fm;
}
#Override
public int getCount(){
return fragments.size();
}
#Override
public int getItemPosition(Object object) {
AppUtils.logI("Get Item Position: "+POSITION_NONE+" "+object.getClass().getName());
return POSITION_NONE;
}
#Override
public Fragment getItem(int position) {
AppUtils.logI("Get Fragment for: "+position);
String f=fragments.get(position);
if(f.equals(BaseDetailsFragment.class.getName())){
return provider.getDetailFragment();
}
if(f.equals(BaseAuthFragment.class.getName())){
return provider.getAuthFragment();
}
if(f.equals(BaseSelectorFragment.class.getName())){
return provider.getCurrentSelector();
}
return new Fragment();
}
private void addItem(int loc,String fa,String tag){
this.fragments.add(loc,fa);
AppUtils.logI(fragments.toString());
notifyDataSetChanged(); //only creates the new fragment but not updating the old ones.
mViewPager.setCurrentItem(loc);
}

It appears that the support library version of the FragmentPagerAdapter does NOTHING in destroy item. So the solution is to save the fragment manager when constructing the page adapter and override the destoryItem() function. Note that this may not be compatible to future editions of the support library.
#Override
public void destroyItem(ViewGroup container,int position,Object object){
super.destroyItem(container, position, object);
FragmentTransaction bt = fm.beginTransaction();
bt.remove((Fragment)object);
bt.commit();
}

Related

FragmentStatePagerAdapter cannot update viewpager's view

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.

how to get info when fist page loaded in ViewPager?

I want to update container fragment base on data inside the page fragment in ViewPager. I used this solution Retrieve a Fragment from a ViewPager and it works fine when I swipe the view.
But I cannot figure out how to get hint (callback) if first page is loaded. At the beginning? Of course onPageSelected is called but when I try to get fragment I got null.
Data data = null;
mViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener(){
#Override
public void onPageSelected(int position) {
PageFragment page = MyPagerAdapter.getPageFragment(position);
if(page != null)
data = page.getData();
else
Log.w(TAG,"page is null"); //i get it at first time. :(
}
});
Below MyPageAdapter which I use (accordint to Retrieve a Fragment from a ViewPager)
public class MyPagerAdapter extends FragmentStatePagerAdapter {
SparseArray<PageFragment> registeredFragments = new SparseArray<PageFragment>();
public MyPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return ...;
}
#Override
public Fragment getItem(int position) {
return PageFragment.newInstance(...);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}
public PageFragment getPageFragment(int position) {
return registeredFragments.get(position);
}
}

get currunt item from viewpager android

I using view pager for swipe view for android app. I build view pager Dynamically. Like add and remove item. Now i want to know how to get exactly position of view pager items.For example, I have 3 items, i want to get 3rd number position when i am in 2nd or 1st item. i used viewPager.getCurrentItem() but it return where i am.
Please suggest me how to give personal ids of every viewpager items and used it in when item is background or on screen.Thanks please help me about this, i left 2 weeks already for this. Thanks in advance
public TabsPageAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int i) {
return new MyWebBrowser();
}
#Override
public int getItemPosition(Object object) {
return PagerAdapter.POSITION_NONE;
}
#Override
public int getCount() {
return COUNT_TAB;
}
}
Now this code Under my fragment class, from WebView chrom class i want to set up title as website title.
#Override
public void onReceivedTitle(WebView view, String title) {
TAB_TITLE=title;
Toast.makeText(HomeActivity.this,title,Toast.LENGTH_LONG).show();
super.onReceivedTitle(view, title);
}
take a look at this Adapter:
class MyAdapter extends FragmentStatePagerAdapter
{
private final SparseArray<WeakReference<YourFragment>> mPageReferences;
public MyAdapter(FragmentManager fm) {
super(fm);
mPageReferences = new SparseArray<WeakReference<YourFragment>>();
}
#Override
public Fragment getItem(int i) {
WeakReference<YourFragment> fragment =
new WeakReference<YourFragment>(YourFragment.newInstance());
mPageReferences.put(i, fragment);
return fragment.get();
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
mPageReferences.remove(position);
}
#Override
public int getCount() {
return number of fragment;
}
public YourFragment getFragment(int key) {
return mPageReferences.get(key).get();
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
WeakReference<YourFragment> fragment = new WeakReference<YourFragment>((YourFragment) super.instantiateItem(container,
position));
mPageReferences.put(position, fragment);
return fragment.get();
}
}
you can easily get the reference of any pages from mPageReferences BUT be careful because it may be null because it is possible that it has not created when you call it. you can use (getFragment(int key) to get the item at the position of key)
you can know the current position from the PagerAdapter.
by calling pageAdapter.getItemPosition(itemNumber)
link to the documentation

Android ViewPager prepend new View

I want to prepend a new View as first page of my ViewPager.
My adapter looks like this:
public class PagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> fragments;
public PagerAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
#Override
public Fragment getItem(int index) {
return fragments.get(index);
}
#Override
public int getCount() {
return fragments.size();
}
public void add(int i, ImageFileObject imageFile) {
ImageViewFragment f = new ImageViewFragment();
f.setImage(imageFile);
fragments.add(0, f);
notifyDataSetChanged();
}
public void add(ImageFileObject imageFile) {
ImageViewFragment f = new ImageViewFragment();
f.setImage(imageFile);
fragments.add(f);
notifyDataSetChanged();
}
}
But when calling add(0, aImageFile) the item is not prepended to the fragment list. (It's not even appended).
Any ideas?
Update: The problem that you described in comment is real, I missed it in first testing. After analyzing the source code of FragmentPagerAdapter I realized that all we need to do is to Override getItemId() in a way that it won't return same id for different fragment items. E.g. current default implementation would just return position as an id for fragment which won't work for this case:
public long getItemId(int position) {
return position;
}
I am updating this answer, please have a look. As before I have tested this code and the problem you described is not happening now.
Only thing you need is not to keep reference of fragments by yourself in List, This is done for you by FragmentPagerAdapter. And as far as I know its not a good practise either. And also even if you return POSITION_NONE from getItemPosition() as suggested by other answer(s), you will end up with Exception
Caused by: java.lang.IllegalStateException: Can't change tag of fragment.
This is because you are trying to reposition alive Fragments in your List by adding a fragment at 0th index (which causes other fragments to reposition) and ViewPager assigns tags based on position.
Keeping all this in mind, here is a tested and working modified adapter:
public class PagerAdapter extends FragmentPagerAdapter {
public static class FragmentInfo {
public String classz;
public ImageFileObject imageFile;
public static long IDS;
public long id;
public FragmentInfo(){
id = IDS++;
}
}
private final List<FragmentInfo> fragments;
private Context context;
public PagerAdapter(FragmentManager fm, List<FragmentInfo> fragments,
Context context) {
super(fm);
this.fragments = fragments;
this.context = context;
}
#Override
public Fragment getItem(int index) {
FragmentInfo info = fragments.get(index);
ImageViewFragment frag = (ImageViewFragment) Fragment.instantiate(
context, info.classz, /* null arguments*/ null);
frag.setImage(info.imageFile);
return frag;
}
#Override
public long getItemId(int position) {
return fragments.get(position).id;
}
#Override
public int getCount() {
return fragments.size();
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
public void add() {
FragmentInfo f = new FragmentInfo();
f.classz = ImageViewFragment.class.getName();
fragments.add(0, f);
notifyDataSetChanged();
}
}
Please change your code accordingly.
The problem is actually with notifyDataSetChanged. Just add to your Adapter:
public int getItemPosition(Object object) {
return POSITION_NONE;
}
However, I think you will get other problems when the Adapter actually tries to call getItem again. These problems will be in the lines of "Fragment Already Added".

insert and remove fragments into viewpager properly

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

Categories

Resources