5

Używam ViewPager z listy ACL, aby wyświetlić kilka z Fragment s. Na początku te Fragmenty, w których wszystkie są ListFragment s, ponieważ zawierają listę. Próbuję naśladować tabelę 2 kolumn, więc każdy element na liście zawiera inną listę dwóch obiektów. Chcę, aby każda komórka była długo klikalna (a nie cały element listy), dlatego zaimplementowałem własny ColumnLayout (być może powinien on nazywać się CellLayout). Każdy element listy zawiera dwa ColumnLayouts, które są długo klikalne i implementuje getContextMenuInfo(), aby zwrócić informacje o tym, która encja była długo kliknięta. Ta metoda wyszukuje ViewPager, prosi o bieżący fragment, a następnie wywołuje fragment, aby zwrócić identyfikator encji z widoku, który był długo klikany. Aby uzyskać prawidłowy wiersz, fragment musi wykonać:onCreateView nie jest wywoływany z fragmentem w ViewPager

getListView().getPositionForView(v) 

Tutaj zaczyna się problem. Czasami getListView() wyrzuca IllegalStateException z napisem "Nie utworzono jeszcze widoku treści". To znaczy. onCreateView(...) nie został wywołany. Dzieje się tak tylko wtedy, gdy aplikacja jest wznowiona. Dzisiaj udało mi się ponownie stworzyć problem, włączając opcję "Nie wypełniaj aktywności" w Ustawienia> Opcje programisty na moim Galaxy Nexus. Uruchamiam aplikację, naciskam Home, a następnie ponownie otwieram aplikację z ostatniego menu aplikacji, a teraz, jeśli kliknę długo jedną z komórek w moim widoku listy, ten wyjątek zostanie zgłoszony. Przez niektóre wyniki debugowania udało mi się zweryfikować, że onCreateView nigdy nie został wywołany. Co jest dziwne, ponieważ cały ViewPager z jego Fragmentem i ListView oraz jego komórkami są rysowane!

Powiedziano mi na # android-dev, że ListFragment w ACL nie obsługuje układów niestandardowych, więc ponownie zaimplementowałem mój android.support.v4.app.Fragment bez używania ListFragment, ale to nie pomogło.

Krótkie podsumowanie: mam niestandardowy układ, który obsługuje zdarzenie o długim naciśnięciu układ dla celów tworzenia MenuInfo obiekt zawierający informacje o entitiy w komórce przycisku. Aby znaleźć obiekt zawarty w układzie, ostatecznie wywoływana jest nazwa listview.getPositionForView(View v), powodująca niekiedy IllegalStateException.

Oto mój układ niestandardowy (być może istnieje łatwiejszy sposób zdobyć na ViewPager niż kopanie w hierarchii widok):

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

    } 
} 

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

Czy to nie jest dziwne, że po wznowieniu aplikacji (jeśli działanie zostało zniszczone), ViewPager wraz z Fragmentami i ListView oraz jego układami niestandardowymi są rysowane. Jednak otrzymuję IllegalStateException mówiąc, że widok nie został utworzony, jeśli długo naciśnij dowolną komórkę w widoku listy?

edytować/ Faktycznie, Log.d(Constants.TAG, "onCreateView!!!!"); wyjście w onCreateView pokazuje, że metody jest wywoływana dwa razy po raz pierwszy uruchomić aplikację (po jednym dla każdego fragmentu w ViewPager), ale to nigdy nie jest ponownie wywoływana, gdy aplikacja zostanie wznowione! ? Czy to błąd, czy też coś, co robię źle?

Odpowiedz

4

Najważniejszą częścią wydaje się być FragmentPagerAdapter (której nie uwzględniłem). Debugowanie pokazuje, że getItem() nie jest wywoływane, gdy działanie jest ponownie tworzone. Fragmenty są tworzone ponownie w inny sposób. Co oznaczało, że moja implementacja FragmentPagerAdapter miała błędne odniesienia do fragmentów w FragmentManager.

Zamiast tego, gdy potrzebuję wyszukać fragment, używam findFragmentByTag() (na FragmentManager) przy użyciu makeFragmentName(), aby uzyskać odpowiednią nazwę znacznika. Jest to metoda używana przez FragmentPagerAdapter po dodaniu fragmentu. Nie jestem pewien, czy jest to bezpieczne podejście, czy nie.