5

Estoy usando ViewPager desde la ACL para mostrar un par de Fragment s. Al principio, estos Fragmentos son todos ListFragment s ya que contienen una lista. Estoy tratando de imitar una tabla de 2 columnas, por lo que cada elemento en la lista contiene otra lista de dos objetos. Quiero que cada celda tenga un clic largo (no todo el elemento de la lista), por lo tanto implementé mi propio ColumnLayout (quizás debería llamarse CellLayout). Cada elemento de la lista contiene dos ColumnLayouts en los que se hace clic con el botón largo e implementa getContextMenuInfo() para devolver información sobre la entidad a la que se hizo clic en forma prolongada. Este método busca el ViewPager, solicita el fragmento actual y luego llama al fragmento para devolver el id de la entidad de la vista en la que se hizo clic con el botón largo. Para obtener la fila correcta, el fragmento debe hacer:onCreateView no llamado con Fragment en ViewPager

getListView().getPositionForView(v) 

Y aquí es donde comienza el problema. Algunas veces getListView() lanza IllegalStateException diciendo "Vista de contenido aún no creada". Es decir. onCreateView(...) no ha sido llamado. Esto solo sucede a veces cuando la aplicación se reanuda. Hoy pude volver a producir el problema activando la opción "No mantener actividades" en Configuración> Opciones de desarrollador en mi Galaxy Nexus. Inicié la aplicación, presiono Inicio y luego volví a abrir la aplicación desde el menú de aplicaciones recientes y ahora, si hago un clic largo en cualquiera de las celdas de mi vista de lista, se lanza esta excepción. Por algunos resultados de depuración logré verificar que nunca se ha llamado a onCreateView. ¡Lo cual es un poco extraño porque todo el ViewPager con su Fragmento y su ListView y sus elementos de celda están dibujados!

me dijeron en # android-dev que ListFragment en el ACL no es compatible con diseños personalizados, así que reimplementado mi android.support.v4.app.Fragment sin utilizar ListFragment pero eso no ayuda.

Breve resumen: Tengo un diseño personalizado que maneja el caso de una presión larga en el diseño con el fin de crear un MenuInfo objeto que contiene información sobre entitiy en la celda presionado. Para encontrar la entidad contenida en el diseño, eventualmente se llama listview.getPositionForView(View v), lo que a veces causa un IllegalStateException.

Aquí mi diseño personalizado (tal vez hay una manera más fácil controlar la ViewPager que cavar en la jerarquía de vistas):

package org.remotestick; 

import android.content.Context; 
import android.support.v4.view.ViewPager; 
import android.util.AttributeSet; 
import android.view.ContextMenu.ContextMenuInfo; 
import android.view.View; 
import android.widget.LinearLayout; 

public class ColumnLayout extends LinearLayout { 

    public ColumnLayout(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 

    public ColumnLayout(Context context) { 
     super(context); 
    } 

    @Override 
    protected ContextMenuInfo getContextMenuInfo() { 
     int itemTypeId = getChildAt(0).getId(); 
     int positionTypeId = getId(); 
     View currentView = this; 
     ViewPager viewPager = null; 
     while(currentView.getParent() != null) { 
      currentView = (View) currentView.getParent(); 
      if(currentView.getId() == R.id.pager) { 
       viewPager = (ViewPager) currentView; 
       break; 
      } else if (currentView.getId() == R.id.rootLayout) 
       break; 
     } 
     if(viewPager == null) 
      return null; 

     WorkspaceAdapter adapter = (WorkspaceAdapter) viewPager.getAdapter(); 
     Pair<Integer, Integer> itemIdAndListPosition = adapter.getItemIdFromWorkspace(viewPager.getCurrentItem(), this, itemTypeId, (positionTypeId == R.id.leftColumn)); 
     if(itemIdAndListPosition == null) 
      return null; 
     else 
      return new MatrixAdaptableContextMenuInfo(itemTypeId, itemIdAndListPosition.first, itemIdAndListPosition.second); 
    } 

    public static class MatrixAdaptableContextMenuInfo implements ContextMenuInfo { 

     public final int itemTypeId; 
     public final Integer itemId; 
     public final Integer positionInList; 

     public MatrixAdaptableContextMenuInfo(int itemTypeId, Integer itemId, Integer positionInList) { 
      this.itemTypeId = itemTypeId; 
      this.itemId = itemId; 
      this.positionInList = positionInList; 
     } 

    } 
} 

Y aquí está (un fragmento de) la Fragment:

public class EntityTableFragment extends Fragment implements TelldusEntityChangeListener { 

    protected static final String ARG_TITLE = "title"; 

    protected MatrixAdapter mAdapter; 
    protected ProgressViewer mProgressViewer; 
    protected MatrixFilter mFilter; 
    protected Handler mHandler = new Handler(); 
    protected RemoteStickData db; 

    public boolean onAttach = false; 
    public boolean onCreateView = false; 

    private ListView listView; 

    public static EntityTableFragment newInstance(String title) { 
     EntityTableFragment f = new EntityTableFragment(); 

     Bundle args = new Bundle(); 
     args.putString(ARG_TITLE, title); 
     f.setArguments(args); 
     return f; 
    } 

    @Override 
    public void onAttach(SupportActivity activity) { 
     super.onAttach(activity); 
     onAttach = true; 
      try { 
      mProgressViewer = (ProgressViewer) activity; 
      mAdapter = new MatrixAdapter(getActivity(), mProgressViewer, new ArrayList<List<MatrixEntity>>(), null); 
      TelldusLiveService.addListener(this); 
     } catch (ClassCastException e) { 
      throw new ClassCastException(activity.toString() + " must implement ProgressViewer"); 
     } 
     db = new RemoteStickData(getActivity()); 
    } 

    @Override 
    public void onDetach() { 
     super.onDetach(); 
     TelldusLiveService.removeListener(this); 
    } 

    @Override 
    public void onActivityCreated(Bundle savedInstanceState) { 
     super.onActivityCreated(savedInstanceState); 
     listView.setAdapter(mAdapter); 
    } 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
     View view = inflater.inflate(R.layout.list, null); 
     listView = (ListView) view.findViewById(R.id.list); 
     return view; 
    } 

    @Override 
    public String toString() { 
     return getArguments().getString(ARG_TITLE); 
    } 

    public Pair<Integer, Integer> getIdAndPositionForView(View v, boolean isLeft) { 
     if(listView == null) 
      throw new IllegalStateException("Listview not yet created"); 
     int positionForView = listView.getPositionForView(v); 
     if(isLeft) 
      return new Pair<Integer, Integer>(mAdapter.getItem(positionForView).get(0).getId(), positionForView); 
     else 
      return new Pair<Integer, Integer>(mAdapter.getItem(positionForView).get(1).getId(), positionForView); 
    } 

¿No es extraño que, al reanudar la aplicación (si se destruyó la actividad), se dibujan ViewPager con sus Fragmentos y su ListView y sus diseños personalizados? Sin embargo, recibo IllegalStateException diciendo que la vista no se ha creado si presiona largamente alguna de las celdas en la vista de lista?

editar/ En realidad, una salida Log.d(Constants.TAG, "onCreateView!!!!"); en onCreateView muestra que los métodos se invocan dos veces la primera vez que se inicia la aplicación (una para cada fragmento en el ViewPager), pero luego nunca es llamado de nuevo cuando se reanude la aplicación! ? ¿Es eso un error o qué podría estar haciendo mal?

Respuesta

4

La parte vital parece haber sido FragmentPagerAdapter (que no incluí). La depuración muestra que getItem() no se llama cuando la actividad se vuelve a crear. Los fragmentos se vuelven a crear por otros medios. Lo que significaba que mi implementación de FragmentPagerAdapter tenía referencias incorrectas a los fragmentos en el FragmentManager.

Así que en vez, cuando necesito para buscar un fragmento utilizo findFragmentByTag() (en la FragmentManager) utilizando el makeFragmentName() para obtener el nombre de la etiqueta correspondiente. Ese es el método utilizado por el FragmentPagerAdapter cuando se agrega el fragmento. Sin embargo, no estoy seguro de si este es un enfoque seguro o no.

Cuestiones relacionadas