2012-05-24 11 views
5

EDYCJA: Nie wstawiłem mojego XML dla tego okna dialogowego.Android 4.0.3 CursorAdapter nie zapełnia ListView na changeCursor

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/tag_layout" 
    android:orientation="vertical" 
    android:layout_height="wrap_content" 
    android:layout_width="@dimen/min_dialog_width" 
    android:padding="5dp" 
    android:animateLayoutChanges="true" 
    > 

<!-- Here is the view to show if the list is emtpy --> 
<TextView 
     android:id="@android:id/empty" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:padding="50dp" 
     android:textAppearance="?android:attr/textAppearanceMedium" 
     android:layout_centerInParent="true" 
     android:gravity="center" 
     android:text="@string/no_items" 
     android:visibility="invisible" 
     /> 

<ListView 
     android:id="@android:id/list" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:visibility="invisible" 
     /> 

<ProgressBar 
     android:id="@+id/tag_spin_progress_bar" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_centerInParent="true" 
     android:indeterminate="true" 
     /> 

</RelativeLayout> 

Używam android.support.v4.CursorLoader i CursorAdapter i staram się zmusić go do aktualizacji swojego kursora. W systemie Android 2.3.3 działa dobrze. Jednak gdy próbuję go na moim urządzeniu 4.0.3, ListView nie odświeża się i metoda newView w moim adapterze nigdy nie jest wywoływana. Wiem, że kursor zawiera dane, ponieważ widzę je na moim urządzeniu 2.3.3.

Jeśli obracam urządzenie, ListView pokazuje, co chcę. Próbowałem unieważnić ListView, ale to nie rozwiązuje problemu.

Jeśli nie zresetuję adaptera ListView, lista nie zostanie pusta, ale nadal nie odświeży listy.

Robię to wszystko w rozszerzonym AlertDialog, który jest osadzony w DialogFragment.

Oto cała klasa

import android.app.AlertDialog; 
import android.content.Context; 
import android.content.DialogInterface; 
import android.content.res.Resources; 
import android.database.Cursor; 
import android.os.Bundle; 

import android.support.v4.app.LoaderManager; 
import android.support.v4.content.CursorLoader; 
import android.support.v4.content.Loader; 

import android.text.Editable; 
import android.text.TextWatcher; 
import android.util.Log; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.inputmethod.InputMethodManager; 
import android.widget.*; 
import org.lds.ldssa.service.MLDatabase; 
import org.lds.ldssa.service.aws.Annotation; 

import java.io.Serializable; 
import java.util.Collection; 
import java.util.HashMap; 
import java.util.Iterator; 
import java.util.Map; 

public class TagDialog extends AlertDialog implements LoaderManager.LoaderCallbacks<Cursor>,AdapterView.OnItemClickListener { 

private static final String TAG = "ldssa.tagdialog"; 
public static final int TAGLOADERID = 0; 

// View Items 
private EditText mEditText; 
private ListView mListView; 
private TextView mEmptyView; 
private ProgressBar mProgressBar; 
private ImageButton mNewTagButton; 
private ImageButton mSortTagButton; 
private TextView mTitle; 
private String mTagTitle; 
private String mNewTagTitle; 

private Annotation mAnnotation; 
private ContentFragment mContentFragment; 

private boolean isNewTagView; 
private static final String KEY_NEWTAGVIEW = "new_tag_view"; 

private static final String POSITION_KEY = "TAG_POSITION"; 
private static final String Y_KEY = "TAG_Y"; 
private static final String SORT_KEY = "TAG_SORT"; 
private static final String CHECKED_STATE_KEY = "TAG_CHECKED_STATE"; 
private static final int NOT_SET = -1; 
private int mPosition; 
private int mY; 

private TagSuggestionAdapter mSuggestionAdapter; 
private TagListAdapter mTagAdapter; 

private MLDatabase mlDatabase; 
private boolean mSortAlpha; 
private HashMap<Long, CheckedState> mCheckedState; 

protected TagDialog(Context context) { 
    super(context); 
} 

public void onCreate(Bundle savedInstanceState){ 
    Context context = getContext(); 
    Resources r = context.getResources(); 

    final LayoutInflater inflater = LayoutInflater.from(context); 
    View view = inflater.inflate(R.layout.dialog_tag, null); 

    // Main parts of the view   
    mEditText = (EditText) view.findViewById(R.id.tag_new_tag); 
    mListView = (ListView) view.findViewById(android.R.id.list); 
    mProgressBar = (ProgressBar) view.findViewById(R.id.tag_spin_progress_bar); 
    mEmptyView = (TextView) view.findViewById(android.R.id.empty); 
    mEmptyView.setVisibility(View.INVISIBLE); 

    // Titlebar 
    View titleBar = inflater.inflate(R.layout.dialog_tag_title, null); 
    mNewTagButton = (ImageButton) titleBar.findViewById(R.id.tag_new_icon); 
    mSortTagButton = (ImageButton) titleBar.findViewById(R.id.tag_sort_icon); 
    mTitle = (TextView) titleBar.findViewById(R.id.tag_title); 
    mTagTitle = r.getString(R.string.tag_dialog_title); 
    mNewTagTitle = r.getString(R.string.tag_new_dialog_title); 
    this.setCustomTitle(titleBar); 

    // Buttons 
    final String OK = r.getString(R.string.ok); 
    final String CANCEL = r.getString(R.string.cancel); 
    this.setButton(BUTTON_POSITIVE, OK, new OnClickListener() { 
     @Override 
     public void onClick(DialogInterface dialog, int which) { /*Never Used*/}}); 
    this.setButton(BUTTON_NEGATIVE, CANCEL, new OnClickListener() { 
     @Override 
     public void onClick(DialogInterface dialog, int which) { /*Never Used*/}}); 

    // Setup Button Listeners 
    setOnShowListener(new OnShowListener() { 
     @Override 
     public void onShow(DialogInterface dialog) { 
      Button ok = getButton(BUTTON_POSITIVE); 
      ok.setOnClickListener(new View.OnClickListener() { 
       @Override 
       public void onClick(View v) { 
        if(isNewTagView){ 
         hideIMM(); 
         addNewTag(); 
         mEditText.setText(""); 
         setupTagDialog(); 
        } else { 
         Collection<CheckedState> changes = mCheckedState.values(); 
         boolean success = true; 
         MLDatabase db = getDatabase(); 
         db.beginAnnotationTransaction(); 
         for(CheckedState change : changes){ 
          if(!change.checked()){ 
           //Detag 
           db.detagAnnotation(mAnnotation.getDbKey(), change.tagID()); 
          } else { 
           mAnnotation.saveHighlightsToDatabase(db); 
           if(mAnnotation.getDbKey().intValue() != MLDatabase.NOT_SET_INT & 
             change.tagID() != MLDatabase.NOT_SET_INT){ 
            success = db.tagAnnotation(mAnnotation.getDbKey(), change.tagID(), change.changed()); 
           } 
          } 
         } 
         if(success){ 
          db.setAnnotationTransactionSuccessful(); 
         } 
         db.endAnnotationTransaction(); 
         mCheckedState.clear(); 
         dismiss(); 
        } 
       } 
      }); 

      Button cancel = getButton(BUTTON_NEGATIVE); 
      cancel.setOnClickListener(new View.OnClickListener() { 
       @Override 
       public void onClick(View v) { 
        if(isNewTagView){ 
         hideIMM(); 
         setupTagDialog(); 
         mEditText.setText(""); 
        } else { 
         mCheckedState.clear(); 
         dismiss(); 
        } 
       } 
      }); 
     } 
    }); 

    mNewTagButton.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      setupNewTagDialog(); 
     } 
    }); 
    mSortTagButton.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      mSortAlpha = !mSortAlpha; 
      restartLoader(); 
     } 
    }); 

    mListView.setOnItemClickListener(TagDialog.this); 

    mEditText.addTextChangedListener(new TextWatcher() { 
     @Override 
     public void beforeTextChanged(CharSequence s, int start, int count, int after) {} 

     @Override 
     public void onTextChanged(CharSequence s, int start, int before, int count) {} 

     @Override 
     public void afterTextChanged(Editable s) { 
      LoaderManager lm = getLoaderManager(); 
      if(lm != null){ 
       Loader l = lm.getLoader(TAGLOADERID); 
       if(l != null){ 
        l.forceLoad(); 
       } else { 
        restartLoader(); 
       } 
      } else { 
       restartLoader(); 
      } 
     } 
    }); 

    //Handle Rotations 
    if(savedInstanceState == null){ 
     //New 
     mPosition = NOT_SET; 
     mY = NOT_SET; 
     mSortAlpha = false; 
     mCheckedState = getCheckedState(mAnnotation.getDbKey()); 
     isNewTagView = false; 
    } else { 
     //rotated 
     isNewTagView = savedInstanceState.getBoolean(KEY_NEWTAGVIEW, false); 
     mPosition = savedInstanceState.getInt(POSITION_KEY, NOT_SET); 
     mY = savedInstanceState.getInt(Y_KEY, NOT_SET); 
     mSortAlpha = savedInstanceState.getBoolean(SORT_KEY, false); 
     restoreCheckedState(savedInstanceState); 
    } 

    mTagAdapter = new TagListAdapter(context, null, mCheckedState); 
    mSuggestionAdapter = new TagSuggestionAdapter(context, null, 0); 

    LoaderManager lm = getLoaderManager(); 
    if(lm != null){ 
     lm.initLoader(TAGLOADERID, null, this); 
    } 

    this.setView(view); 
    super.onCreate(savedInstanceState); 
} 

private void addNewTag() { 
    String tag = mEditText.getText().toString().trim(); 
    if(!tag.equals("")){ 
     getDatabase(); 
     Integer langID = mAnnotation.getLanguageId(); 
     try{ 
      long tagID = mlDatabase.insertTag(langID, tag); 
      if(mAnnotation.getDbKey().intValue() != MLDatabase.NOT_SET_INT && 
        tagID != MLDatabase.NOT_SET_INT){ 
       mCheckedState.put(tagID, new CheckedState(tagID, true, true)); 
      } 
     } catch (Exception e) { 
      Log.d(TAG, "Problem saving new tag: " + tag + " : " + e.getMessage()); 
      e.printStackTrace(); 
     } 
    } 
} 

public void onStart(){ 
    if(isNewTagView){ 
     setupNewTagDialog(); 
    } else { 
     setupTagDialog(); 
    } 
    restartLoader(); 
} 

@Override 
public Bundle onSaveInstanceState(){ 
    Bundle bundle = super.onSaveInstanceState(); 

    //Save What dialog we are in. 
    bundle.putBoolean(KEY_NEWTAGVIEW, isNewTagView); 
    bundle.putBoolean(SORT_KEY, mSortAlpha); 

    //Save position 
    bundle.putInt(POSITION_KEY, mListView.getFirstVisiblePosition()); 
    final View v = mListView.getChildAt(0); 
    bundle.putInt(Y_KEY, (v == null) ? 0 : v.getTop()); 

    //Save Checked State 
    Iterator it = mCheckedState.entrySet().iterator(); 
    int i = 0; 
    while(it.hasNext()){ 
     Map.Entry pair = (Map.Entry)it.next(); 
     bundle.putSerializable(CHECKED_STATE_KEY + i, (CheckedState)pair.getValue()); 
     i++; 
    } 
    bundle.putInt(CHECKED_STATE_KEY, i); 

    return bundle; 
} 

private void restoreCheckedState(Bundle bundle){ 
    int count = bundle.getInt(CHECKED_STATE_KEY); 
    mCheckedState = new HashMap<Long, CheckedState>(); 
    boolean success = true; 
    for(int i = 0; i < count; i++){ 
     CheckedState cs = (CheckedState)bundle.getSerializable(CHECKED_STATE_KEY+i); 
     if(cs == null){ 
      success = false; 
      break; 
     } 
     mCheckedState.put(cs.tagID(), cs); 
    } 
    if(!success){ 
     mCheckedState = getCheckedState(mAnnotation.getDbKey()); 
    } 
} 

@Override 
public void onBackPressed(){ 
    if(isNewTagView){ 
     hideIMM(); 
     setupTagDialog(); 
    } else { 
     this.dismiss(); 
    } 
} 

private void setupTagDialog() { 
    isNewTagView = false; 
    mTitle.setText(mTagTitle); 
    mNewTagButton.setVisibility(View.VISIBLE); 
    mSortTagButton.setVisibility(View.VISIBLE); 
    mEmptyView.setVisibility(View.INVISIBLE); 
    mEditText.setVisibility(View.GONE); 
    mListView.setVisibility(View.GONE); 
    mProgressBar.setVisibility(View.VISIBLE); 
    mListView.setAdapter(mTagAdapter); 
    restartLoader(); 
} 

private void setupNewTagDialog() { 
    isNewTagView = true; 
    mTitle.setText(mNewTagTitle); 
    mNewTagButton.setVisibility(View.INVISIBLE); 
    mSortTagButton.setVisibility(View.INVISIBLE); 
    mEmptyView.setVisibility(View.INVISIBLE); 
    mEditText.setVisibility(View.VISIBLE); 
    mListView.setVisibility(View.GONE); 
    mProgressBar.setVisibility(View.VISIBLE); 
    mListView.setAdapter(mSuggestionAdapter); 
    restartLoader(); 
} 

public void setAnnotation(Annotation a) { 
    mAnnotation = a; 
} 

public void setContentViewInterface(ContentFragment contentFragment) { 
    mContentFragment = contentFragment; 
} 

private MLDatabase getDatabase() { 
    if(mlDatabase == null){ 
     GospelLibraryApplication app = (GospelLibraryApplication) getContext().getApplicationContext(); 
     mlDatabase = app.getMlDatabase(); 
    } 
    return mlDatabase; 
} 

public String getFilter() { 
    return mEditText.getText().toString().trim(); 
} 

public Integer getAnnotationID(){ 
    if(mAnnotation != null){ 
     return mAnnotation.getDbKey(); 
    } 
    return MLDatabase.NOT_SET_INT; 
} 

private LoaderManager getLoaderManager(){ 
    if(mContentFragment == null){ 
     Log.d(TAG, "ContentFragment is NULL!"); 
     return null; 
    } 
    return mContentFragment.getContentActivity().getSupportLoaderManager(); 
} 

private void restartLoader(){ 
    LoaderManager lm = getLoaderManager(); 
    if(lm != null){ 
     lm.restartLoader(TAGLOADERID, null, this); 
    } 
} 

private void hideIMM(){ 
    InputMethodManager imm = (InputMethodManager)getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 
    imm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0); 
} 

private HashMap<Long, CheckedState> getCheckedState(Integer annotationID) { 
    HashMap<Long, CheckedState> checkedState = new HashMap<Long, CheckedState>(); 
    MLDatabase db = getDatabase(); 
    Cursor cursor = db.queryAllTagsWithAnnotation(annotationID); 
    if(cursor != null){ 
     for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()){ 
      Long tagID = cursor.getLong(cursor.getColumnIndex(MLDatabase.CL_ID)); 
      boolean isChecked = !cursor.isNull(cursor.getColumnIndex(MLDatabase.CL_ANNOTATION)); 
      checkedState.put(tagID, new CheckedState(tagID, isChecked, false)); 
     } 
    } 
    return checkedState; 
} 

@Override 
public Loader<Cursor> onCreateLoader(int id, Bundle args) { 
    TagCursorLoader loader = new TagCursorLoader(getContext(), this); 
    return loader; 
} 

@Override 
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor data) { 
    if(isNewTagView) { 
     mSuggestionAdapter.changeCursor(data); 
     if(mListView.getAdapter() == null){ 
      mListView.setAdapter(mSuggestionAdapter); 
     } 
    } else { 
     mTagAdapter.changeCursor(data); 
     if(mListView.getAdapter() == null){ 
      mListView.setAdapter(mTagAdapter); 
     } 
    } 
    if(mPosition != NOT_SET && mY != NOT_SET){ 
     mListView.setSelectionFromTop(mPosition, mY); 
     mPosition = mY = NOT_SET; 
    } 

    if (mListView.getAdapter() != null) { 
     if (mListView.getAdapter().getCount() > 0) { 
      mEmptyView.setVisibility(View.INVISIBLE); 
     } 
     else { 
      mEmptyView.setVisibility(View.VISIBLE); 
     } 
    } 
    else { 
     mEmptyView.setVisibility(View.VISIBLE); 
    } 
    mProgressBar.setVisibility(View.GONE); 
    mListView.setVisibility(View.VISIBLE); 
    mListView.invalidate(); 
} 

@Override 
public void onLoaderReset(Loader<Cursor> cursorLoader) { 
    if(mSuggestionAdapter != null) { 
     mSuggestionAdapter.changeCursor(null); 
    } 
} 

@Override 
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 
    if(isNewTagView){ 
     TextView tv = (TextView)view; 
     mEditText.setText(tv.getText()); 
     Button ok = getButton(BUTTON_POSITIVE); 
     if(ok != null){ 
      ok.performClick(); 
     } 
    } else { 
     CheckedTextView ctv = (CheckedTextView)view; 
     boolean checked = !ctv.isChecked(); 
     ctv.setChecked(checked); 
     mCheckedState.put(id, new CheckedState(id, checked, true)); 
    } 

} 

public static class TagCursorLoader extends CursorLoader { 
    private final ForceLoadContentObserver mObserver = new ForceLoadContentObserver(); 

    private TagDialog dialog; 
    private MLDatabase mlDatabase; 
    private Cursor mCursor; 
    private String mFilter; 
    private Integer mAnnotationID; 

    // Runs on worker thread 
    @Override 
    public Cursor loadInBackground(){ 
     Cursor cursor = null; 
     if(dialog.isNewTagView){ 
      mFilter = dialog.getFilter(); 
      cursor = mlDatabase.getTagSuggestions(mFilter); 
     } else { 
      cursor = mlDatabase.queryTags(dialog.mSortAlpha); 
     } 

     if(cursor != null){ 
      cursor.registerContentObserver(mObserver); 
     } 

     return cursor; 

    } 

    //Runs on UI thread 
    @Override 
    public void deliverResult(Cursor cursor){ 
     //Handle if canceled in the middle. 
     if(isReset()){ 
      if(cursor != null){ 
       cursor.close(); 
      } 
      return; 
     } 

     Cursor oldCursor = mCursor; 
     mCursor = cursor; 
     if(isStarted()) { 
      super.deliverResult(cursor); 
     } 

     if(oldCursor != null && !oldCursor.equals(cursor) && !oldCursor.isClosed()) { 
      oldCursor.close(); 
     } 
    } 

    public TagCursorLoader(Context context, TagDialog dialog) { 
     super(context); 
     this.dialog = dialog; 
     mlDatabase = dialog.getDatabase(); 
    } 

    @Override 
    public void onStartLoading(){ 
     if(mCursor == null) { 
      forceLoad(); 
     } else { 
      if(dialog.isNewTagView && mFilter.equals(dialog.getFilter())) { 
       deliverResult(mCursor); 
      } else { 
       forceLoad(); 
      } 
     } 
    } 

    @Override 
    protected void onStopLoading() { 
     // Attempt to cancel the current load task if possible. 
     cancelLoad(); 
    } 

    @Override 
    public void onCanceled(Cursor cursor) { 
     if (cursor != null && !cursor.isClosed()) { 
      cursor.close(); 
     } 
    } 

    @Override 
    protected void onReset() { 
     super.onReset(); 

     // Ensure the loader is stopped 
     onStopLoading(); 

     if (mCursor != null && !mCursor.isClosed()) { 
      mCursor.close(); 
     } 
     mCursor = null; 
    } 

} 

/** 
* Class is used to store the temporary checked state of the tags. 
*/ 
public class CheckedState implements Serializable { 
    private static final long serialVersionUID = 1263560458217339487L; 

    /** 
    * @serialField 
    */ 
    private long tagID; 
    /** 
    * @serialField 
    */ 
    private boolean checked; 
    /** 
    * @serialField 
    */ 
    private boolean changed; 

    /** 
    * Constructor for CheckedState. 
    * @param tagID The tag ID 
    * @param checked The Current Checked State 
    * @param changed Ture if changed in the dialog. False if pulling from database. 
    */ 
    public CheckedState(long tagID, boolean checked, boolean changed){ 
     this.tagID = tagID; 
     this.checked = checked; 
     this.changed = changed; 
    } 

    public long tagID(){ 
     return tagID; 
    } 

    public boolean checked() { 
     return checked; 
    } 

    public boolean changed() { 
     return changed; 
    } 
    } 
} 
+0

[Ten komentarz] (http://stackoverflow.com/a/9063814/1348379) przez CommonsWare pomógł mi, gdy miałem podobne problemy. Nie sądzę, że powinieneś wymieniać adaptery tak, jak robisz. Zmiana kursora za pomocą '' '' swapCursor (kursor) '' '' - tak, lista adapterów - nie. Utwórz oddzielny fragment z innym programem ładującym dla tej informacji i zainicjuj adapter (za pomocą '' 'setAdapter (...)' '' ') i programu ładującego (' '' initLoader'''') podczas działania onActivityCreated. – OceanLife

+0

@OceanLife Nie używam działania, robię to wszystko w rozszerzonym AlertDialog. – Ge3ng

+0

próbowałeś initLoader (...). ForceLoad()? Miałem też problemy z ICS i udało mi się je naprawić. –

Odpowiedz

2

Uwaga Nie dodałem wcześniej swojego XML. Tutaj znajduje się problem.

andriod:animateLayoutChanges 

nie działa z tym, co próbowałem zrobić.

Po usunięciu tego z mojego XML-a działało to jak czar.

1

W większości przykładów, że widać, utworzeniu instancji adaptera raz i ustawić go w ListView podczas wyświetlania jest tworzony, a następnie zadzwonić getLoaderManager().initLoader().

// Prepare the loader. Either re-connect with an existing one, 
// or start a new one. 
getLoaderManager().initLoader(0, null, this); 

Następnie w metodzie onLoadFinished() zadzwonić swapCursor() który automatycznie odświeża ListView.

// This is the Adapter being used to display the list's data. 
SimpleCursorAdapter mAdapter; 
... 

public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 
    // Swap the new cursor in. (The framework will take care of closing the 
    // old cursor once we return.) 
    mAdapter.swapCursor(data); 
} 

Powyższy kod został skopiowany z dokumentacji http://developer.android.com/guide/topics/fundamentals/loaders.html

UPDATE Loaders: dokumentacji mówi o użyciu ładowarki dla działań i ich fragmenty, ale nie wspomina za pomocą okien dialogowych. Zgaduję, że jeśli istnieje funkcja getLoaderManager(), to wszystko jest w porządku, ale jeśli nie korzystasz z LoaderManager i uruchamiasz Loader ręcznie, wtedy myślę, że musisz upewnić się, że gdy wywołasz swapCursor() lub setAdapter(), że robisz to w wątku UI. Czasami najprostszym sposobem zapewnienia tego jest wywołanie

getListView().post(new Runnable() { 
    public void run() { 
     // so the setAdapter() or swapCursor() here 
    } 
}); 

mam biegać w przypadkach gdzie mam sobie aktualizowanych ListView w tle i nie odzwierciedla jak aktualizowany aż obracania urządzenia, ponieważ interfejs użytkownika nie został zaktualizowany w wątku interfejsu użytkownika.

+0

Używam LoaderManager, więc mam onLoadFinished i tam zmieniam kursor. Jest wywoływana, ale interfejs użytkownika nie aktualizuje się. To dziwne, ponieważ kiedy przełączam się z powrotem na pierwszy adapter, aktualizuje się interfejs użytkownika i działa tak, jak powinien. – Ge3ng

+0

Wspomniałeś jednak, że używasz tego w 'AlertDialog'. 'AlertDialog' nie rozszerza' Fragment' lub 'Activity', więc w jaki sposób uzyskujesz odniesienie do' LoaderManager'? – stuckless

+0

Osadzam mój AlertDialog w DialogFragment i to jest sposób uzyskiwania dostępu do LoaderManager. – Ge3ng

Powiązane problemy