2012-04-11 22 views
9

Estoy haciendo una aplicación para Android con viewpager y fragmentos. Quiero hacer una opción para agregar o eliminar páginas de fragmento al buscapersonas dinámicamente. Tengo un encargo FragmentPagerAdapter:inserte y elimine fragmentos en viewpager correctamente

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

puedo eliminar cualquier fragmento, excepto el 0. cuando intento para sacarlo Tengo una NullPointerException en el notifyDataSetChanged(); o en el pager.setAdapter (esto); si comento el notificar

También recibí una NullPointerException cuando intento insertar una nueva página. Cuando agrego la nueva página al final de la lista, funciona bien. Incluso intenté readd los fragmentos en el relleno con este después de la 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(); 
      } 
     } 

si uso getFragmentTag(i-1) I consiguieron NullPointer de nuevo. Con solo usar i obtuve una excepción de estado ilegal porque no puedo modificar la etiqueta del fragmento. Con beginTransaction().add(pager.getId,fragments.get(i).toFragment()) sigue siendo nullpointer ...

Mi pregunta es: ¿qué estoy haciendo mal y cómo se puede hacer correctamente? (y quizás: ¿de dónde obtiene notyfyDataSetChanged() los datos cuando causa nullpointerexception?)

Respuesta

1

Tengo una solución que funciona con un adaptador sin fragmento. Eche un vistazo al my post y vea si ayuda.

+1

No se recomienda el enlace. –

0

que puede eliminar cualquier fragmento, excepto el 0.

Para mí funciona si sólo tiene que utilizar a> = condición WHITIN del removeItem ciclo for:

 if (i >= pos){ 
      fm.beginTransaction().remove(fragments.get(i).getFragment()).commit(); 
     } 
7

Esto es lo que solía hacer para solucionar el problema con los fragmentos que se etiquetan cuando se crean instancias y el hecho de que los fragmentos etiquetados anteriormente no pueden cambiar de posición dentro de ViewPager al intentar añadir o eliminar páginas, es decir, conseguir la excepción:

java.lang.IllegalStateException: No se puede cambiar la etiqueta de fragmento MyFragment {4055f558 id = 0x7f050034 androide: Interruptor: 2131034164: 3}: era androide: conmutador : 2131034164: 3 ahora androide: Interruptor: 2131034164: 4

Este adaptador se crea una lista de todas las páginas iniciales y, a continuación, puede utilizar setEnabled (posición int, boolean activado) para activar o desactivar ciertas páginas que oculta o muestra estas páginas en ViewPager.

Funciona manteniendo una lista interna de todos los fragmentos pero exponiéndolos a ViewPager y asignando las posiciones de los fragmentos habilitados solamente.

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); 
    } 
} 
+0

Funciona bien, pero cuando se llama a 'notifyDatasetChanged', crea nuevas vistas para cada' Fragmento', por lo que pierdo el estado de UI. ¿Cómo puedo prevenir esto? – WonderCsabo

Cuestiones relacionadas