5

Sto usando ViewPager dall'ACL per mostrare un paio di Fragment s. Inizialmente, questi frammenti contengono tutti gli ListFragment mentre contengono un elenco. Sto cercando di imitare una tabella a 2 colonne, quindi ogni elemento nell'elenco contiene un altro elenco di due oggetti. Voglio che ogni cella sia cliccabile a lungo (non l'intero elemento della lista), quindi ho implementato il mio ColumnLayout (forse dovrebbe essere chiamato CellLayout). Ogni elemento dell'elenco contiene due ColumnLayout che sono cliccabili a lungo e implementa getContextMenuInfo() per restituire informazioni su quale entità è stata cliccata a lungo. Questo metodo cerca il ViewPager, richiede il frammento corrente e quindi chiama il frammento per restituire l'id entità dalla vista che è stata cliccata a lungo. Per ottenere la riga corretta il frammento deve fare:onCreateView non chiamato con Fragment in ViewPager

getListView().getPositionForView(v) 

E qui è dove inizia il problema. A volte il numero getListView() genera "IllegalStateException" "Visualizzazione contenuto non ancora creata". Cioè onCreateView(...) non è stato chiamato. Questo succede solo a volte quando l'app viene ripresa. Oggi sono riuscito a riprodurre il problema attivando l'opzione "Non tenere attività" in Impostazioni> Opzioni sviluppatore sul mio Galaxy Nexus. Avvio l'app, premere Home, quindi riaprire l'app dal menu delle app recenti e ora, se faccio un lungo clic su una delle celle nella mia lista, viene generata questa eccezione. Con alcune uscite di debug sono riuscito a verificare che onCreateView non è mai stato chiamato. Il che è abbastanza strano perché l'intero ViewPager con il suo Frammento e il suo ListView e i suoi elementi di cella sono tutti disegnati!

Mi è stato detto su # android-dev che ListFragment in ACL non supporta i layout personalizzati quindi ho implementato nuovamente il mio android.support.v4.app.Fragment senza utilizzare ListFragment ma ciò non ha aiutato.

Breve sintesi: Ho un layout personalizzato che gestisce l'evento di lunga premendo il layout per lo scopo di creare un MenuInfo oggetto contenente le informazioni relative entitiy nella cella premuto. Per trovare l'entità contenuta nel layout, alla fine viene chiamato listview.getPositionForView(View v), a volte causando uno IllegalStateException.

Qui il mio layout personalizzato (forse c'è un modo più semplice per entrare in possesso del ViewPager che scavare nella gerarchia della vista):

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

    } 
} 

Ed ecco (un frammento del) 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); 
    } 

Non è strano che, riprendendo l'app (se l'attività è stata distrutta), il ViewPager con i suoi Frammenti e il suo ListView e i suoi layout personalizzati sono tutti disegnati. Eppure ottengo IllegalStateException dicendo che la vista non è stata creata se premo a lungo una delle celle nella listview?

modificare/ In realtà, un uscita Log.d(Constants.TAG, "onCreateView!!!!"); in onCreateView dimostra che i metodi viene richiamato due volte la prima volta che avvia l'applicazione (una per ogni frammento nel ViewPager), ma poi non è mai chiamato di nuovo quando l'applicazione è ripreso! ? È un bug o cosa potrei fare di sbagliato?

risposta

4

La parte vitale sembra essere stata la FragmentPagerAdapter (che non ho incluso). Il debug mostra che getItem() non viene chiamato quando l'attività viene ricreata. I frammenti vengono ricreati con altri mezzi. Il che significava che la mia implementazione di FragmentPagerAdapter aveva riferimenti errati ai frammenti nello FragmentManager.

Quindi, invece, quando devo cercare un frammento, utilizzo findFragmentByTag() (su FragmentManager) utilizzando makeFragmentName() per ottenere il nome del tag appropriato. Questo è il metodo utilizzato da FragmentPagerAdapter quando viene aggiunto il frammento. Non sono sicuro se questo sia un approccio sicuro o meno.