Nested Fragment mit CursorLoader

  • Antworten:11
gr8
  • Forum-Beiträge: 14

22.04.2014, 17:52:28 via Website

Hallo,

ich habe einen NavigationDrawer. Wenn man auf einen Eintrag ("Personen") in diesem klickt, soll ein ViewPager geöffnet werden. Dabei sollen die Personen - je nach Tab - unterschiedlich sortiert werden. Soweit so gut.

Folgendermaßen habe ich das aufgrunddessen aufgebaut:

  • Activity-Klasse die den NavigationDrawer beinhaltet und das Handling macht zu dem "Personen"-Eintrag macht
  • "Personen"-Fragment die den ViewPager beinhaltet, und einen PagerAdapter (bzw. FragmentStatePagerAdapter) zuweist
  • Der PagerAdapter hat dann das Fragment, welche im jeweiligen Tab angezeigt werden soll. Nachdem nur die Sortierung unterschiedlich ist, existiert dafür nur ein ListFragment
  • Dieses ListFragment implementiert die LoaderCallbacks des LoaderManagers (LoaderManager .LoaderCallbacks), und erzeugt einen SimpleCursorLoader (je nach Sortierung würde ein untersch. Cursor zurückgegeben werden)

Meine Fragen:

  • Problem: Ab und an bleiben die Listen im ViewPager ("Personen") leer - woran kann das liegen?
  • Gibt es einen besseren Aufbau/Struktur? - Wie würde das aussehen? Ist der CursorLoader hier überhaupt ideal (benötige ein Suchfeld, deswegen hätte ich diesen verwendet - ist aber noch nicht implementiert)
  • Aufgrund der verwendeten Klassen (zB FragmentStatePagerAdapter) muss ich auf die Support Lib v4 setzen - ist das klug? - Die Applikation wurde für Android v. 4+ spezifziert.

Code:

Der Aufruf in der HauptActivity des Fragments, welche den ViewPager beinhaltet:

 getSupportFragmentManager().beginTransaction()
    .replace(R.id.container, new PersonFragment()).addToBackStack("person")
    .commit();

Das zugehörige PersonFragment sieht wie folgt aus:

public class PersonFragment extends Fragment {

private ViewPager mViewPager;
private View view;
public PersonFragment () {}


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    view = inflater.inflate(R.layout.person_fragment, container, false);
    PagerAdapter mPageAdapter = new PagerAdapter(getFragmentManager());
    mViewPager = (ViewPager) view.findViewById(R.id.viewpager);
    mViewPager.setAdapter(mPageAdapter);
    return view;
}

Der PagerAdapter:

public class PagerAdapter extends FragmentStatePagerAdapter {


private final String[] titles = { "Sortierung1", "Sortierung2", "Sortierung3, "Sortierung4", "Sortierung5" };

public PagerAdapter(FragmentManager fm) {
    super(fm);
}

@Override
public android.support.v4.app.Fragment getItem(int position) {
    return new PersonListFragment ();
}

@Override
public int getCount() {
    return 5;
}

@Override
public CharSequence getPageTitle(int position) {
    return titles[position];
}

}

Das PersonListFragment:

 public class PersonListFragment extends ListFragment implements LoaderManager
    .LoaderCallbacks<Cursor> {
private AbsListView listview;
private CursorAdapter mAdapter;


public final class PersonCursorAdapter extends CursorAdapter {

    public PersonCursorAdapter (Context context, Cursor c, int flags) {
        super(context, c, flags);
    }
    public PersonCursorAdapter (Context context, Cursor c, boolean autoRequery) {
        super(context, c, autoRequery);
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        LayoutInflater inflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        return inflater.inflate(R.layout.list_item_person, parent, false);
    }

    @Override
    public void bindView(View view, Context context, Cursor cursor) {

        String name = cursor.getString(cursor.getColumnIndex("name"));
        TextView txt_contactName = (TextView) view.findViewById(R.id.contactName);        
        txt_contactName.setText(surname + " " + name);
         //TBD - e.g. Image

    }
}


public PersonListFragment() {   }


@Override
public void onCreate(Bundle savedInstanceState) {
    setHasOptionsMenu(true);
    mAdapter = new PersonCursorAdapter(getActivity().getApplicationContext(), null, true);
    setListAdapter(mAdapter);
    getLoaderManager().initLoader(0, null, this);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_person_list, container, false);

    listview = (AbsListView) view.findViewById(android.R.id.list);
    return view;
}


private void restartLoader() {
    getLoaderManager().restartLoader(0, null, this);

}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle bargs) {
    return new PersonLoader(getActivity().getApplicationContext());
}

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    mAdapter.swapCursor(data);
}


@Override
public void onLoaderReset(Loader<Cursor> loader) {
    mAdapter.swapCursor(null);
}



public class PersonLoader extends SimpleCursorLoader {

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

@Override
public Cursor loadInBackground() {
    return new PersonBO(getContext()).getPersonCursor();
}

}

Ich bin über jede Hilfe sehr dankbar! Danke!

Liebe Grüße!

— geändert am 22.04.2014, 17:56:47

Antworten
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

22.04.2014, 18:06:22 via Website

Wenn das Fragment leer bleibt, dann sind in diesem Moment keine Daten da oder? Ab besten du gehst in den Debugmode rein und schaust was ausgeführt wird. Bzw. welche Daten im Fragment ankommen

LG Pascal //It's not a bug, it's a feature. :) ;)

Antworten
gr8
  • Forum-Beiträge: 14

22.04.2014, 18:20:03 via Website

Pascal P.

Wenn das Fragment leer bleibt, dann sind in diesem Moment keine Daten da oder? Ab besten du gehst in den Debugmode rein und schaust was ausgeführt wird. Bzw. welche Daten im Fragment ankommen

Nein, ab und an bleibt es leer bleiben wenn man, zum Beispiel, via Back-Button zurück switched (während man sich zB auf einem anderen NavigationDrawer-Item befindet).

— geändert am 22.04.2014, 18:22:00

Antworten
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

22.04.2014, 18:24:59 via Website

Wie wird denn auf die NavigationDrawer clicks reafiert, bzw wie wird das Fragment gestartet?

LG Pascal //It's not a bug, it's a feature. :) ;)

Antworten
gr8
  • Forum-Beiträge: 14

22.04.2014, 18:35:46 via Website

Pascal P.

Wie wird denn auf die NavigationDrawer clicks reafiert, bzw wie wird das Fragment gestartet?

So, beispielsweise:

getSupportFragmentManager().beginTransaction() .replace(R.id.container, new PersonFragment()).addToBackStack("person") .commit();

Ergänzung zu dem, dass die Listen leer bleiben: Wische ich zwischen den Listen hin und her, werden sie aktualisiert und sind befüllt. Vmtl. weil der Cursor das Laden asynchron durchführt. Das Problem tritt aber komischerweise nicht regelmäßig auf.

Antworten
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

22.04.2014, 18:38:15 via Website

Dann wird das wahrscheinlich durch das Asynchrone Laden hervorgerufen. Das solltest du überprüfen, dass die Daten zum Zeitpunkt des Anzeigen synchron und vorhanden sind.

LG Pascal //It's not a bug, it's a feature. :) ;)

Antworten
gr8
  • Forum-Beiträge: 14

22.04.2014, 18:50:02 via Website

Pascal P.

Dann wird das wahrscheinlich durch das Asynchrone Laden hervorgerufen. Das solltest du überprüfen, dass die Daten zum Zeitpunkt des Anzeigen synchron und vorhanden sind.

kannst du mir helfen, wie ich hierzu vorgehen zu habe?

Antworten
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

22.04.2014, 19:15:36 via Website

Jo, zuerst mal muss man wissen, wie der Asynchrone Vorgang gestartet wird. z.b. ob es ein Thread ist oder ein AsyncTask etc. Aus dem anderen Thread heraus kannst du dann entweder eine Callback Methode im UI Thread aufrufen oder in deiner Fragment Activity waren, bis der Thread beendet ist bzw. das laden der Daten abgeschlossen ist. Bitte poste doch mal den Code des Asynchronen Ladens

LG Pascal //It's not a bug, it's a feature. :) ;)

Antworten
gr8
  • Forum-Beiträge: 14

23.04.2014, 12:30:34 via Website

Pascal P.

Jo, zuerst mal muss man wissen, wie der Asynchrone Vorgang gestartet wird. z.b. ob es ein Thread ist oder ein AsyncTask etc. Aus dem anderen Thread heraus kannst du dann entweder eine Callback Methode im UI Thread aufrufen oder in deiner Fragment Activity waren, bis der Thread beendet ist bzw. das laden der Daten abgeschlossen ist. Bitte poste doch mal den Code des Asynchronen Ladens

macht das nicht der Loader? - siehe PersonLoader bzw. die loadInBackground-Methode, wo mehr oder weniger lediglich ein SQL-Statement als Cursor zurückgeliefert wird...

Nachdem das Problem jetzt in den letzten zig Testfällen nicht mehr aufgetreten ist, sind für mich nur mehr folgende Fragen offen:

  • Gibt es einen besseren Aufbau/Struktur? - Wie würde das aussehen? Ist der CursorLoader hier überhaupt ideal (benötige ein Suchfeld, deswegen hätte ich diesen verwendet - ist aber noch nicht implementiert)
  • Aufgrund der verwendeten Klassen (zB FragmentStatePagerAdapter) muss ich auf die Support Lib v4 setzen - ist das klug? - Die Applikation wurde für Android v. 4+ spezifziert.

Antworten
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

23.04.2014, 13:09:40 via Website

Die Art des loaders ist eigentlich egal, bis auf die Suchfunktion die du einbauen willst. Theoretisch kannst du auch nen AsyncTask nehmen. In beiden Fällen musst du wissen, wann der Hintergrundthread seine Arbeit beendet hat. Warum sollte sie Supportv4 Lib nicht ideal sein. Kennst du irgendwelche negativen Aspekte? Mir sind keine Bekannt.

LG Pascal //It's not a bug, it's a feature. :) ;)

Antworten
gr8
  • Forum-Beiträge: 14

23.04.2014, 13:40:36 via Website

Pascal P.

Die Art des loaders ist eigentlich egal, bis auf die Suchfunktion die du einbauen willst. Theoretisch kannst du auch nen AsyncTask nehmen. In beiden Fällen musst du wissen, wann der Hintergrundthread seine Arbeit beendet hat. Warum sollte sie Supportv4 Lib nicht ideal sein. Kennst du irgendwelche negativen Aspekte? Mir sind keine Bekannt.

In meinem Fall sollte das hierbei durch die LoaderManager-Callbacks (bzw. onLoadFinished) geregelt sein - sofern ich das richtig verstanden habe.

Support-V4-Lib -> Dachte da beispielsweise an erhöhten (unnötigen) RAM-Verbrauch, und demnach v.a. an Performance.

Antworten
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

23.04.2014, 18:37:03 via App

Bisher hab ich an eingebundenen Libs nichts auszusetzen. Und ja wenn du callback methoden benutzt sollte es funktionieren

LG Pascal //It's not a bug, it's a feature. :) ;)

Antworten