32

Sto configurando la mia app in modo che le persone possano creare gruppi di amici. Quando viene creato un gruppo, scrive 2 tabelle nel database SQL. La prima tabella ha un nome di gruppo e un identificativo di gruppo. La seconda tabella ha 2 colonne, un id di gruppo e un id utente. Funziona beneCome leggere un DB SQLite in Android con un cursore di caricamento?

Tuttavia, ora voglio essere in grado di leggere dal database. Sto usando un frammento listview con un cursorloader ma ho problemi a visualizzare le informazioni. Voglio elencare tutti i nomi dei gruppi dalla prima tabella nella mia visualizzazione elenco.

mio problema è che, quando ho usato il cursorloader per elencare i miei contatti, stavo usando un Uri dal content provider nel metodo onCreateLoader. Nello specifico ho avuto CONTENT_URI dalla classe ContactsContracts.Contacts.

Esempio di cursorloader con contentprovider:

@Override 
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) { 
    Uri contentUri = ContactsContract.Contacts.CONTENT_URI; 
    return new CursorLoader(getActivity(),contentUri,PROJECTION,SELECTION,ARGS,ORDER); 
} 

Tuttavia, senza l'utilizzo di un fornitore di contenuti, non so cosa mettere nel metodo onCreateLoader perché return new CursorLoader(...) richiede un Uri nel secondo argomento.

Qualche suggerimento su come potrei essere in grado di visualizzare i dati del mio database in una lista?

frammento di codice classe:

public class GroupListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> { 

CursorAdapter mAdapter; 
private OnItemSelectedListener listener; 
private static final String[] PROJECTION ={GroupContract.GroupDetails.COLUMN_NAME_GROUP_NAME}; 
private static final String SELECTION = null; 
final String[] FROM = {GroupContract.GroupDetails.COLUMN_NAME_GROUP_NAME}; 
final int[] TO = {android.R.id.text1}; 
private static final String[] ARGS = null; 
private static final String ORDER = null; 
private Cursor c; 


@Override 
public void onCreate(Bundle savedInstanceState){ 
    super.onCreate(savedInstanceState); 
    mAdapter = new SimpleCursorAdapter(getActivity(), android.R.layout.simple_list_item_1,null,FROM,TO,0); 
    ReadDBAsync readDB = new ReadDBAsync(); 
    readDB.execute(); 
} 

@Override 
public void onActivityCreated(Bundle savedInstanceState){ 
    super.onActivityCreated(savedInstanceState); 
    setListAdapter(mAdapter); 
    getLoaderManager().initLoader(0,null,this); 
} 

@Override 
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) { 
    Uri contenturi = Uri.parse("content://preamble.oneapp"); 
    Uri tableuri = Uri.withAppendedPath(contenturi,GroupContract.GroupDetails.TABLE_NAME); 
    return new CursorLoader(getActivity(),tableuri,PROJECTION,SELECTION,ARGS,ORDER); 
} 

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

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


private class ReadDBAsync extends AsyncTask<Void,Void,String> { 

    @Override 
    protected String doInBackground(Void... voids) { 

     ContractDBHelpers mDBHelper = new ContractDBHelpers(getActivity()); 
     SQLiteDatabase db = mDBHelper.getReadableDatabase(); 
     String returnvalue = "database read"; 
     c = db.query(GroupContract.GroupDetails.TABLE_NAME,PROJECTION,null,null,null,null,null); 
     return returnvalue; 
    } 

    @Override 
    protected void onPostExecute(String result){ 
     Toast.makeText(getActivity(), result, Toast.LENGTH_LONG).show(); 
    } 
} 

} 
+0

Avete creato un fornitore di contenuti per gestire il database? – user936414

+0

@ user936414 no, è necessario? quali sono i vantaggi/gli svantaggi di farlo? Modifica: basta leggere su di esso, non ho bisogno di condividere i dati su più applicazioni, quindi non ho bisogno di un fornitore di contenuti –

risposta

35

Questi sono i passaggi per creare un cursorloader in un elenco frammento

1) Creare una classe che estende SQLiteOpenHelper e sovrascrivere onCreate e onUpgrade per creare le tabelle.

2) Creare una classe che estende ContentProvider e creare gli URI per accedere al database. Fare riferimento a http://developer.android.com/guide/topics/providers/content-providers.html. Aggiungi gli URI allo URIMatcher che utilizzi in onCreate, onUpdate, query, ecc. (Metodi sovrascritti) per abbinare l'URI. Vedere http://developer.android.com/reference/android/content/UriMatcher.html

3) Nel metodo di inserimento chiamare getContext().getContentResolver().notifyChange(uri, null). Nel metodo della query, chiamare il numero setNotificationUri(ContentResolver cr, Uri uri) prima di restituire il provider di contenuti affinché la modifica di inserimento si rifletta automaticamente sul caricatore. (https://stackoverflow.com/a/7915117/936414).

4) Dare quell'URI in onCreateLoader.

Nota: Senza un fornitore di contenuti, l'aggiornamento automatico delle modifiche all'elenco non è fattibile a partire dalla versione Android corrente. Se non vuoi che il tuo contentprovider sia visibile, imposta l'attributo esportato in manifest su false. Oppure puoi implementare il tuo CursorLoader personalizzato come in https://stackoverflow.com/a/7422343/936414 per recuperare i dati dal database. Ma in questo caso l'aggiornamento automatico dei dati non è possibile

+9

grazie per le vostre informazioni ci guarderò dentro. Trovo strano che le mie opzioni siano usare un ContentProvider (che altrimenti non ho bisogno) o creare il mio personalizzato 'CursorLoader' (che sembra essere un lavoro aggiuntivo) ... Non interrogherei al database senza un 'fornitore di contenuti 'è abbastanza comune da aver fatto un modo per farlo già senza fare altro lavoro? –

+0

Uno dei motivi per cui contentprovider è che possono notificare tutti gli osservatori registrati (registrati in CursorLoader) quando il contenuto nel database cambia. – user936414

+5

cosa succede se questo è un tipo di dati che non cambia? Trovo anche molto strano essere costretti a usare un ContentProvider. Non esiste una soluzione più veloce/leggera per i dati statici SQLite? –

62

La Guida Android suggerisce di creare un ContentProvider quando si desidera condividere i dati con altre applicazioni. Se non ti serve, puoi semplicemente eseguire l'override del metodo loadInBackgroud() della classe CursorLoader. Per esempio scrivere come questo nel tuo onCreateLoader:

return new CursorLoader(YourContext, null, YourProjection, YourSelection, YourSelectionArgs, YourOrder) 
      { 
       @Override 
       public Cursor loadInBackground() 
       { 
        // You better know how to get your database. 
        SQLiteDatabase DB = getReadableDatabase(); 
        // You can use any query that returns a cursor. 
        return DB.query(YourTableName, getProjection(), getSelection(), getSelectionArgs(), null, null, getSortOrder(), null); 
       } 
      }; 
+12

Questo è quello che uso perché non ho bisogno di condividere i miei dati, e non voglio refactoring mio API di database per essere un ContentProvider. Il rovescio della medaglia è che senza il contenuto di ContentProvider i miei listview non sanno automaticamente quando i dati sono cambiati, che è una delle cose belle di ContentProviders. Per aggirare il problema ricreare i miei caricatori quando so che è stata apportata una modifica, cioè anziché chiamare 'Cursor.requery()' Ora utilizzo 'getLoaderManager(). RestartLoader (MY_LOADER_ID, null, this);'. Questo è scomodo, ma è il modo più semplice che ho trovato per sostituire l'approccio 'startManagingCursor()'. – Fish

+0

@ Fish, Grazie per queste informazioni, ma quello che mi interessa sapere ora è ** quanto costoso 'restartLoader()' può essere ..? ** Che importa? – eRaisedToX

+0

Qualsiasi motivo non dovrei restituire un AsyncTaskLoader se non mi interessa specificare YourProjection, YourSelection, YourSelectionArgs e YourOrder? – steamer25

Problemi correlati