Utilisation de la vue de recyclage avec une base de données


127

Actuellement, aucune implémentation par défaut de RecyclerView.Adapter n'est disponible.

Peut-être avec la version officielle, Google l'ajoutera.

Comme il n'y a pas de support pour CursorAdapterle RecyclerViewmoment, comment pouvons - nous utiliser RecyclerViewavec une base de données? Aucune suggestion ?

Réponses:


101

Si vous exécutez une requête avec CursorLoaderet que vous souhaitez à la RecyclerViewplace de ListView.

Vous pouvez essayer mon CursorRecyclerViewAdapter: CursorAdapter dans RecyclerView


12
Le code fonctionne mais pas les animations lors de la suppression et de l'insertion, car la méthode registerDataSetObserver de la classe Cursor n'identifie pas exactement l'élément qui a changé. Ainsi, chaque fois que le fournisseur de contenu change, la vue de recyclage est complètement chargée via notifyDataSetChanged , et c'est l'essence même de RecyclerView.
francas

1
Quel serait l'inconvénient de simplement passer le curseur à l'adaptateur via le constructeur et d'utiliser simplement cursor.moveToPosition () dans onBindViewHolder (), pour obtenir les données pertinentes?
Gautam

2
Suggestion: Appelez swapCursor () à partir du constructeur, pour ne pas avoir à vous répéter là-bas
Greg Ennis

2
@francas Animations fonctionne bien si vous définissezsetHasStableIds(true)
aulnes

2
@alders avez-vous effectué d'autres modifications en plus de setHasTableIds? J'ai essayé cela aussi, les identifiants correspondent et chaque fois que j'échange le curseur, la liste revient en haut.
dwbrito

89

Ma solution consistait à contenir un membre CursorAdapter dans mon implémentation recyclerView.Adapter. Puis en passant toute la gestion de la création de la nouvelle vue et en la liant à l'adaptateur de curseur, quelque chose comme ceci:

public class MyRecyclerAdapter extends Adapter<MyRecyclerAdapter.ViewHolder> {

    // Because RecyclerView.Adapter in its current form doesn't natively 
    // support cursors, we wrap a CursorAdapter that will do all the job
    // for us.
    CursorAdapter mCursorAdapter;

    Context mContext;

    public MyRecyclerAdapter(Context context, Cursor c) {

        mContext = context;

        mCursorAdapter = new CursorAdapter(mContext, c, 0) {

            @Override
            public View newView(Context context, Cursor cursor, ViewGroup parent) {
                // Inflate the view here
            }

            @Override
            public void bindView(View view, Context context, Cursor cursor) {
                // Binding operations
            }
        };
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        View v1;

        public ViewHolder(View itemView) {
            super(itemView);
            v1 = itemView.findViewById(R.id.v1);
        }
    }

    @Override
    public int getItemCount() {
        return mCursorAdapter.getCount();
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        // Passing the binding operation to cursor loader
        mCursorAdapter.getCursor().moveToPosition(position); //EDITED: added this line as suggested in the comments below, thanks :)
        mCursorAdapter.bindView(holder.itemView, mContext, mCursorAdapter.getCursor());

    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // Passing the inflater job to the cursor-adapter
        View v = mCursorAdapter.newView(mContext, mCursorAdapter.getCursor(), parent);
        return new ViewHolder(v);
    }
}

17
Merci pour cette solution, cela fonctionne pour moi, je devais juste ajouter une ligne dans onBindViewHolder: 'mCursorAdapter.getCursor (). MoveToPosition (position)' avant d'appeler bindView () sur mCursorAdapter
user1071762

8
@nbtk Pourquoi ne pas contenir un curseur au lieu d'un CursorAdapter? Si je comprends bien, vous n'utilisez aucune des fonctionnalités de CursorAdapter.
MyDogTom

14
@MyDogTom - Je n'ai pas de curseur, car vous devez l'effacer lorsque vous avez terminé, et le définir sur NULL n'est pas suffisant. Le CursorAdapter s'en charge parfaitement.
nbtk

3
@nbtk J'ai vraiment aimé cette idée, et je joue avec depuis. J'ai essayé de l'abstraire un peu et j'ai construit une bibliothèque, ce n'est pas encore à 100% où je veux qu'elle soit, mais cela fait le travail. Seriez-vous intéressé à le vérifier? Si vous avez un github, je serais heureux de vous en attribuer l'inspiration. github.com/androidessence/RecyclerViewCursorAdapter
AdamMc331

5
@nbtk Procédez comme suit pour actualiser votre vue avec de nouvelles données:public void changeCursor(Cursor cursor){ mCursorAdapter.changeCursor(cursor); notifyDataSetChanged(); }
Cijo

48

Puisque votre question dit "Comment utiliser RecyclerViewavec une base de données" et que vous ne spécifiez pas si vous voulez SQLite ou quoi que ce soit d'autre avec le RecyclerView, je vais vous donner une solution hautement optimale. J'utiliserai Realm comme base de données et vous laisserai afficher toutes les données dans votre fichier RecyclerView. Il prend également en charge les requêtes asynchrones sans utiliser Loadersni AsyncTask.

Pourquoi royaume? realm.io performances Android

Étape 1

Ajoutez la dépendance gradle pour Realm, la dépendance pour la dernière version se trouve ici

Étape 2

Créez votre classe de modèle, par exemple, disons quelque chose de simple comme Dataqui a 2 champs, une chaîne à afficher à l'intérieur de la RecyclerViewligne et un horodatage qui sera utilisé comme itemId pour permettre RecyclerViewà l'animation des éléments. Notez que j'étends RealmObjectci - dessous en raison de laquelle votre Dataclasse sera stockée sous forme de table et toutes vos propriétés seront stockées sous forme de colonnes de cette table Data. J'ai marqué le texte de données comme clé primaire dans mon cas car je ne veux pas qu'une chaîne soit ajoutée plus d'une fois. Mais si vous préférez avoir des doublons, créez l'horodatage sous la forme @PrimaryKey. Vous pouvez avoir une table sans clé primaire, mais cela posera des problèmes si vous essayez de mettre à jour la ligne après l'avoir créée. Une clé primaire composite au moment de la rédaction de cette réponse n'est pas prise en charge par Realm.

import io.realm.RealmObject;
import io.realm.annotations.PrimaryKey;

public class Data extends RealmObject {
@PrimaryKey
private String data;

//The time when this item was added to the database
private long timestamp;

public String getData() {
    return data;
}

public void setData(String data) {
    this.data = data;
}

public long getTimestamp() {
    return timestamp;
}

public void setTimestamp(long timestamp) {
    this.timestamp = timestamp;
}
}

Étape 3

Créez votre mise en page pour savoir comment une seule ligne doit apparaître dans le RecyclerView. La disposition d'un élément à une seule ligne à l'intérieur de notre Adapterest la suivante

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

<TextView
    android:id="@+id/area"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:background="@android:color/white"
    android:padding="16dp"
    android:text="Data"
    android:visibility="visible" />

</FrameLayout>

Remarquez que j'ai gardé un en FrameLayouttant que racine même si j'ai un TextViewintérieur. Je prévois d'ajouter plus d'éléments dans cette mise en page et donc de la rendre flexible pour le moment :)

Pour les curieux, voici à quoi ressemble actuellement un seul élément. disposition de ligne d'élément unique dans RecyclerView

Étape 4

Créez votre RecyclerView.Adapterimplémentation. Dans ce cas, l'objet de source de données est un objet spécial appelé RealmResultsqui est essentiellement un LIVE ArrayList, en d'autres termes, lorsque des éléments sont ajoutés ou supprimés de votre table, cet RealmResultsobjet se met à jour automatiquement.

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import io.realm.Realm;
import io.realm.RealmResults;
import slidenerd.vivz.realmrecycler.R;
import slidenerd.vivz.realmrecycler.model.Data;

public class DataAdapter extends RecyclerView.Adapter<DataAdapter.DataHolder> {
private LayoutInflater mInflater;
private Realm mRealm;
private RealmResults<Data> mResults;

public DataAdapter(Context context, Realm realm, RealmResults<Data> results) {
    mRealm = realm;
    mInflater = LayoutInflater.from(context);
    setResults(results);
}

public Data getItem(int position) {
    return mResults.get(position);
}

@Override
public DataHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = mInflater.inflate(R.layout.row_data, parent, false);
    DataHolder dataHolder = new DataHolder(view);
    return dataHolder;
}

@Override
public void onBindViewHolder(DataHolder holder, int position) {
    Data data = mResults.get(position);
    holder.setData(data.getData());
}

public void setResults(RealmResults<Data> results) {
    mResults = results;
    notifyDataSetChanged();
}

@Override
public long getItemId(int position) {
    return mResults.get(position).getTimestamp();
}

@Override
public int getItemCount() {
    return mResults.size();
}

public void add(String text) {

    //Create a new object that contains the data we want to add
    Data data = new Data();
    data.setData(text);

    //Set the timestamp of creation of this object as the current time
    data.setTimestamp(System.currentTimeMillis());

    //Start a transaction
    mRealm.beginTransaction();

    //Copy or update the object if it already exists, update is possible only if your table has a primary key
    mRealm.copyToRealmOrUpdate(data);

    //Commit the transaction
    mRealm.commitTransaction();

    //Tell the Adapter to update what it shows.
    notifyDataSetChanged();
}

public void remove(int position) {

    //Start a transaction
    mRealm.beginTransaction();

    //Remove the item from the desired position
    mResults.remove(position);

    //Commit the transaction
    mRealm.commitTransaction();

    //Tell the Adapter to update what it shows
    notifyItemRemoved(position);
}

public static class DataHolder extends RecyclerView.ViewHolder {
    TextView area;

    public DataHolder(View itemView) {
        super(itemView);
        area = (TextView) itemView.findViewById(R.id.area);
    }

    public void setData(String text) {
        area.setText(text);
    }
}
}

Notez que notifyItemRemovedj'appelle avec la position à laquelle la suppression a eu lieu mais je n'appelle PAS notifyItemInsertedou notifyItemRangeChangedparce qu'il n'y a pas de moyen direct de savoir à quelle position l'élément a été inséré dans la base de données puisque les entrées de royaume ne sont pas stockées de manière ordonnée. L' RealmResultsobjet se met à jour automatiquement chaque fois qu'un nouvel élément est ajouté, modifié ou supprimé de la base de données, nous l'appelons notifyDataSetChangedlors de l'ajout et de l'insertion d'entrées en bloc. À ce stade, vous êtes probablement préoccupé par les animations qui ne seront pas déclenchées car vous appelez notifyDataSetChangedà la place des notifyXXXméthodes. C'est exactement pourquoi la getItemIdméthode renvoie l'horodatage de chaque ligne à partir de l'objet de résultats. L'animation est réalisée en 2 étapes avec notifyDataSetChangedsi vous appelez setHasStableIds(true)puis remplacezgetItemId pour fournir autre chose que juste la position.

Étape 5

Ajoutons le RecyclerViewà notre Activityou Fragment. Dans mon cas, j'utilise un Activity. Le fichier de mise en page contenant le RecyclerViewest assez simple et ressemblerait à quelque chose comme ça.

<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="@dimen/text_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />

J'ai ajouté un app:layout_behaviordepuis mon RecyclerViewentrée dans un CoordinatorLayoutque je n'ai pas posté dans cette réponse par souci de concision.

Étape 6

Construisez le RecyclerViewcode in et fournissez les données dont il a besoin. Créez et initialisez un objet Realm à l'intérieur onCreateet fermez-le à l'intérieur onDestroyun peu comme la fermeture d'une SQLiteOpenHelperinstance. Au plus simple, votre onCreateintérieur Activityressemblera à ceci. La initUiméthode est l'endroit où toute la magie se produit. J'ouvre une instance de Realm à l'intérieur onCreate.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mRealm = Realm.getInstance(this);
    initUi();
}

private void initUi() {

    //Asynchronous query
    RealmResults<Data> mResults = mRealm.where(Data.class).findAllSortedAsync("data");

    //Tell me when the results are loaded so that I can tell my Adapter to update what it shows
    mResults.addChangeListener(new RealmChangeListener() {
        @Override
        public void onChange() {
            mAdapter.notifyDataSetChanged();
            Toast.makeText(ActivityMain.this, "onChange triggered", Toast.LENGTH_SHORT).show();
        }
    });
    mRecycler = (RecyclerView) findViewById(R.id.recycler);
    mRecycler.setLayoutManager(new LinearLayoutManager(this));
    mAdapter = new DataAdapter(this, mRealm, mResults);

    //Set the Adapter to use timestamp as the item id for each row from our database
    mAdapter.setHasStableIds(true);
    mRecycler.setAdapter(mAdapter);
}

Notez que dans la première étape, je demande à Realm de me donner tous les objets de la Dataclasse triés par leur nom de variable appelé data de manière asynchrone. Cela me donne un RealmResultsobjet avec 0 éléments sur le thread principal que je mets sur le Adapter. J'ai ajouté un RealmChangeListenerpour être averti lorsque les données ont fini de charger à partir du fil d'arrière-plan où j'appelle notifyDataSetChangedavec mon Adapter. J'ai également appelé setHasStableIdstrue pour permettre à l' RecyclerView.Adapterimplémentation de suivre les éléments ajoutés, supprimés ou modifiés. Le onDestroyfor my Activityferme l'instance Realm

@Override
protected void onDestroy() {
    super.onDestroy();
    mRealm.close();
}

Cette méthode initUipeut être appelée à l'intérieur onCreatede votre Activityou onCreateViewou onViewCreatedde votre Fragment. Notez les choses suivantes.

Étape 7

BAM! L' intérieur de votre Il y a des données de base de données RecyclerViewasynchonously chargée sans CursorLoader, CursorAdapter, SQLiteOpenHelperavec des animations. L'image GIF présentée ici est un peu lente, mais les animations se produisent lorsque vous ajoutez des éléments ou les supprimez.

données de la base de données dans RecyclerView


Merci d'avoir fourni un exemple complet. J'ai quelques questions. Je veux stocker des bitmap dans le royaume est-ce possible? si oui, quel serait le type de colonne et comment le stocker et le récupérer?
Rakesh

1
@Rakesh ne stocke jamais d'images dans une base de données, stocke-la toujours dans votre système de fichiers et stocke l'URI de l'image dans la base de données
PirateApp

Merci pour la réponse rapide. Je crée une application dans laquelle je souhaite stocker des images pour mon contact. Si je stocke sur un système de fichiers, il pourrait y avoir des centaines d'images. Comment gérer ces images sur le système de fichiers?
Rakesh

9
FWIW, y compris un graphique des performances de Realm sur l'insert n'est pas pertinent pour une question sur les recyclerviews (qui concernera inévitablement la récupération)
JRaymond

Étant donné que la barre d'outils indique Vivz , est-ce une solution @slidenerd?
SymbolixAU

1

Vous pouvez implémenter vous-même toutes les méthodes requises. J'ai récemment fait ma propre implémentation en copiant simplement le code de CursorAdapter.

public class MyAdapter extends RecyclerView.Adapter<ViewHolder> {

        protected boolean mDataValid;
        protected boolean mAutoRequery;
        protected Cursor mCursor;
        protected Context mContext;
        protected int mRowIDColumn;
        protected ChangeObserver mChangeObserver;
        protected DataSetObserver mDataSetObserver;
        protected FilterQueryProvider mFilterQueryProvider;
        public static final int FLAG_AUTO_REQUERY = 0x01;
        public static final int FLAG_REGISTER_CONTENT_OBSERVER = 0x02;

        public Cursor getCursor() {
            return mCursor;
        }

        //Recommended
        public MyAdapter(Context context, Cursor c, int flags) {
            init(context, c, flags);
        }

        public MyAdapter(Context context, Cursor c) {
            init(context, c, FLAG_AUTO_REQUERY);
        }

        public MyAdapter(Context context, Cursor c, boolean autoRequery) {
            init(context, c, autoRequery ? FLAG_AUTO_REQUERY : FLAG_REGISTER_CONTENT_OBSERVER);
        }

        void init(Context context, Cursor c, int flags) {
            if ((flags & FLAG_AUTO_REQUERY) == FLAG_AUTO_REQUERY) {
                flags |= FLAG_REGISTER_CONTENT_OBSERVER;
                mAutoRequery = true;
            } else {
                mAutoRequery = false;
            }
            boolean cursorPresent = c != null;
            mCursor = c;
            mDataValid = cursorPresent;
            mContext = context;
            mRowIDColumn = cursorPresent ? c.getColumnIndexOrThrow("_id") : -1;
            if ((flags & FLAG_REGISTER_CONTENT_OBSERVER) == FLAG_REGISTER_CONTENT_OBSERVER) {
                mChangeObserver = new ChangeObserver();
                mDataSetObserver = new MyDataSetObserver();
            } else {
                mChangeObserver = null;
                mDataSetObserver = null;
            }

            if (cursorPresent) {
                if (mChangeObserver != null) c.registerContentObserver(mChangeObserver);
                if (mDataSetObserver != null) c.registerDataSetObserver(mDataSetObserver);
            }
        }

        // Create new views (invoked by the layout manager)
        @Override
        public ViewHolder onCreateViewHolder(final ViewGroup parent,
                                             int viewType) {
            // create a new view
            final View view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.list_item, parent, false);
            // set the view's size, margins, paddings and layout parameters

            ViewHolder vh = new ViewHolder(view, mCursor, new ViewHolder.IMyViewHolderClicks() {

                @SuppressLint("NewApi")
                @Override
                public void onClick(Cursor cursor) {
                    Log.e("Item :", cursor.getString(cursor.getColumnIndex(MyDatabaseHelper.MW_NAAM)));
                    Intent intent = new Intent(TasksListFragment.this.getActivity(), DetailActivity.class);
                    intent.putExtra(DetailActivity.EXTRA_PARAM_ID, cursor.getLong(cursor.getColumnIndex(MyDatabaseHelper.MW_ID)));

                    ActivityOptions activityOptions = ActivityOptions.makeSceneTransitionAnimation(
                            TasksListFragment.this.getActivity(),

                            // Now we provide a list of Pair items which contain the view we can transitioning
                            // from, and the name of the view it is transitioning to, in the launched activity
                            new Pair<View, String>(
                                    view.findViewById(R.id.imageview_item),
                                    DetailActivity.VIEW_NAME_HEADER_IMAGE),
                            new Pair<View, String>(
                                    view.findViewById(R.id.textview_name),
                                    DetailActivity.VIEW_NAME_HEADER_TITLE)
                    );

                    // Now we can start the Activity, providing the activity options as a bundle
                    startActivity(intent, activityOptions.toBundle());
                    // END_INCLUDE(start_activity)
                }
            });
            return vh;
        }

        // Replace the contents of a view (invoked by the layout manager)
        @SuppressLint("NewApi")
        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            // - get element from your dataset at this position
            // - replace the contents of the view with that element
            final Cursor cursor = getItem(position);

            holder.mTextView.setText(cursor.getString(cursor.getColumnIndex(MyDatabaseHelper.MW_NAAM)));
            holder.mImageView.setTransitionName("grid:image:" + cursor.getLong(cursor.getColumnIndex(MyDatabaseHelper.MW_ID)));
            holder.mTextView.setTransitionName("grid:name:" + cursor.getLong(cursor.getColumnIndex(MyDatabaseHelper.MW_ID)));
        }

        //@Override
        // public View getView(int position, View view, ViewGroup viewGroup) {
        //     return view;
        // }

        // Return the size of your dataset (invoked by the layout manager)
        @Override
        public int getItemCount() {
            return getCount();
        }

        public int getCount() {
            if (mDataValid && mCursor != null) {
                return mCursor.getCount();
            } else {
                return 0;
            }
        }

        public Cursor getItem(int position) {
            if (mDataValid && mCursor != null) {
                mCursor.moveToPosition(position);
                return mCursor;
            } else {
                return null;
            }
        }

        @Override
        public long getItemId(int position) {
            if (mDataValid && mCursor != null) {
                if (mCursor.moveToPosition(position)) {
                    return mCursor.getLong(mRowIDColumn);
                } else {
                    return 0;
                }
            } else {
                return 0;
            }
        }

        public Cursor swapCursor(Cursor newCursor) {
            if (newCursor == mCursor) {
                return null;
            }
            Cursor oldCursor = mCursor;
            if (oldCursor != null) {
                if (mChangeObserver != null) oldCursor.unregisterContentObserver(mChangeObserver);
                if (mDataSetObserver != null) oldCursor.unregisterDataSetObserver(mDataSetObserver);
            }
            mCursor = newCursor;
            if (newCursor != null) {
                if (mChangeObserver != null) newCursor.registerContentObserver(mChangeObserver);
                if (mDataSetObserver != null) newCursor.registerDataSetObserver(mDataSetObserver);
                mRowIDColumn = newCursor.getColumnIndexOrThrow("_id");
                mDataValid = true;
                // notify the observers about the new cursor
                notifyDataSetChanged();
            } else {
                mRowIDColumn = -1;
                mDataValid = false;
                // notify the observers about the lack of a data set
                notifyDataSetInvalidated();
            }
            return oldCursor;
        }

        public void changeCursor(Cursor cursor) {
            Cursor old = swapCursor(cursor);
            if (old != null) {
                old.close();
            }
        }

        public CharSequence convertToString(Cursor cursor) {
            return cursor == null ? "" : cursor.toString();
        }

        public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
            if (mFilterQueryProvider != null) {
                return mFilterQueryProvider.runQuery(constraint);
            }
            return mCursor;
        }


        public FilterQueryProvider getFilterQueryProvider() {
            return mFilterQueryProvider;
        }

        public void setFilterQueryProvider(FilterQueryProvider filterQueryProvider) {
            mFilterQueryProvider = filterQueryProvider;
        }

        protected void onContentChanged() {
            if (mAutoRequery && mCursor != null && !mCursor.isClosed()) {
                if (false) Log.v("Cursor", "Auto requerying " + mCursor + " due to update");
                mDataValid = mCursor.requery();
            }
        }

        private class ChangeObserver extends ContentObserver {
            public ChangeObserver() {
                super(new Handler());
            }

            @Override
            public boolean deliverSelfNotifications() {
                return true;
            }

            @Override
            public void onChange(boolean selfChange) {
                onContentChanged();
            }
        }

        private class MyDataSetObserver extends DataSetObserver {
            @Override
            public void onChanged() {
                mDataValid = true;
                notifyDataSetChanged();
            }

            @Override
            public void onInvalidated() {
                mDataValid = false;
                notifyDataSetInvalidated();
            }
        }


        private final DataSetObservable mDataSetObservable = new DataSetObservable();

        public void registerDataSetObserver(DataSetObserver observer) {
            mDataSetObservable.registerObserver(observer);
        }

        public void unregisterDataSetObserver(DataSetObserver observer) {
            mDataSetObservable.unregisterObserver(observer);
        }

        public void notifyDataSetInvalidated() {
            mDataSetObservable.notifyInvalidated();
        }
    }

Alors, où est le chargeur dans cette classe?
IgorGanapolsky

Le chargeur utilisera cet adaptateur. Voir developer.android.com/guide/components/loaders.html pour un exemple d'utilisation d'un chargeur. Dans cet exemple, SimpleCursorAdapter serait MyAdapter
Arno

Que voulez-vous dire SimpleCursorAdapter? Vous étendez RecyclerView.Adapter.
IgorGanapolsky

1
Je veux dire que si vous voyez SimpleCursorAdapter dans l'exemple, vous pouvez mettre MyAdapter à la place de cela. L'adaptateur que vous étendez n'a pas d'importance tant qu'il dispose de la méthode swapcursor et de certaines autres méthodes.
Arno

Cela semble un peu trop codé par rapport à la réponse de @nbtk ci-dessus.
eRaisedToX


1

Juste une autre réponse, puisque je n'aimais pas celle acceptée (dont imo n'a pas d'utilisation intuitive).

Ce qui suit est ma propre implémentation, qui est très similaire à (et partiellement inspirée par) SimpleCursorAdapter:

public class RecyclerViewSimpleCursorAdapter extends RecyclerView.Adapter {
    private int mLayout;
    private Cursor mCursor;
    private String[] mFrom;
    private int[] mTo;

    private boolean mAutoRequery;
    private ContentObserver mContentObserver;

    /**
     * Standard constructor.
     *
     * @param layout resource identifier of a layout file that defines the views for this list item. The layout file should include at least those named views defined in "to"
     * @param c      The database cursor. Can be null if the cursor is not available yet.
     * @param from   A list of column names representing the data to bind to the UI. Can be null if the cursor is not available yet.
     * @param to     The views that should display column in the "from" parameter. These should all be TextViews and ImageViews. The first N views in this list are given the values of the first N columns in the from parameter. Can be null if the cursor is not available yet.
     */
    public RecyclerViewSimpleCursorAdapter(int layout, Cursor c, String[] from, int[] to, boolean autoRequery) {
        mLayout = layout;
        mCursor = c;
        mFrom = from;
        mTo = to;
        mAutoRequery = autoRequery;

        if (mAutoRequery) {
            initializeContentObserver();
        }
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new RecyclerView.ViewHolder(
                LayoutInflater.from(parent.getContext())
                        .inflate(mLayout, parent, false)
        ) {
        };
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        mCursor.moveToPosition(position);

        if (mFrom == null || mTo == null)
            return;

        for (int i = 0; i < mFrom.length && i < mTo.length; i++) {
            String from = mFrom[i];
            int columnIndex = mCursor.getColumnIndex(from);
            String value = mCursor.getString(columnIndex);
            View view = holder.itemView.findViewById(mTo[i]);

            if (view instanceof TextView) {
                ((TextView) view).setText(value);
            } else if (view instanceof ImageView) {
                try {
                    ((ImageView) view).setImageResource(Integer.parseInt(value));
                } catch (NumberFormatException nfe) {
                    ((ImageView) view).setImageURI(Uri.parse(value));
                }
            } else {
                throw new IllegalStateException(view.getClass().getName() + " is not a view that can be bound by this RecyclerViewSimpleCursorAdapter");
            }
        }
    }

    @Override
    public int getItemCount() {
        return mCursor  != null ? mCursor.getCount() : 0;
    }

    private void initializeContentObserver() {
        mContentObserver = new ContentObserver(new Handler()) {
            @Override
            public boolean deliverSelfNotifications() {
                return true;
            }

            @Override
            public void onChange(boolean selfChange) {
                notifyDataSetChanged();
            }
        };
        mCursor.registerContentObserver(mContentObserver);
    }

    /**
     * Change the underlying cursor to a new cursor. If there is an existing cursor it will be closed.
     *
     * @param cursor The new cursor to be used
     */
    public void changeCursor(Cursor cursor) {
        Cursor oldCursor = mCursor;
        if (mAutoRequery) {
            if (mCursor != null) {
                mCursor.unregisterContentObserver(mContentObserver);
            }

            mContentObserver = new ContentObserver(new Handler()) {
                @Override
                public boolean deliverSelfNotifications() {
                    return true;
                }

                @Override
                public void onChange(boolean selfChange) {
                    notifyDataSetChanged();
                }
            };

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

        notifyDataSetChanged();

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

    /**
     * Change the cursor and change the column-to-view mappings at the same time.
     *
     * @param cursor The database cursor. Can be null if the cursor is not available yet.
     * @param from A list of column names representing the data to bind to the UI. Can be null if the cursor is not available yet.
     * @param to The views that should display column in the "from" parameter. These should all be TextViews or ImageViews. The first N views in this list are given the values of the first N columns in the from parameter. Can be null if the cursor is not available yet.
     */
    public void changeCursorAndColumns(Cursor cursor, String[] from, int[] to) {
        mFrom = from;
        mTo = to;
        changeCursor(cursor);
    }

    /**
     * Returns the cursor.
     * @return the cursor
     */
    public Cursor getCursor() {
        return mCursor;
    }
}

Vous pouvez le modifier pour d'autres usages particuliers, mais avec un curseur, il fonctionne comme le SimpleCursorAdapterfait, uniquement avec un fichier RecyclerView.


0

Après avoir récupéré les données de la base de données et les stocker dans une liste, cela devrait peut-être ressembler à ceci:

    dbHelper = new BooksDbAdapter(this);
    dbHelper.open();
    //Clean all data
    dbHelper.deleteAllZist();
    //Add some data
    dbHelper.insertSomeRecords();
    List<String> mylist = dbHelper.getArrayColumn(3);

ajouter des données à recyclerview

list = new ArrayList<DataObject>();
 Integer i=0;
    for (String lst : mylist) {
        list.add(i++, new DataObject(lst,
                "The RecyclerView widget is a more advanced and flexible 
               version of ListView."));
    }

    recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
    recyclerView.setHasFixedSize(true);
    myRecAdapter = new RecyclerviewAdapter(list, Zist1ChapterActivity.this);

0

Voici mon implémentation de cursoradapter pour une vue de recyclage. Il prend en charge OnItemClickListener, OnLongItemClickListener, OnfooterClickListener, les sections et le pied de page. Ne prend pas en charge l'en -tête, onHeaderClickListner, le défilement rapide, l'en-tête collant ou les sections collantes.

Étendez simplement cet adaptateur et créez votre propre adaptateur. Remplacez les méthodes fournies. et passez le curseur de la méthode OnCursorLoadFinished.Si l'adaptateur est déjà créé, swapCursor ()

package com.tracker.paisa;

import android.database.Cursor;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;


/**
 * @author Rahul Upadhyay (https://github.com/devDroidRaul)
 * Supports footer
 * Onitemclicklistener, OnItemLongClickListener, OnFooterClickListener
 * Supports Sections.
 *
 * Does Not support,Header, OnHeaderClickListener, FastScroller, StickySection (this can b done with item decor)
 * Pull requests are welcome for improvements.
 *
 * Override this to give logic to place subheaders between items.
 * public abstract boolean onPlaceSubheaderBetweenItems(int position);
 *
 * create seperate viewHolders for item, subheaders and footer. and return required views.
 *
 * @Override below methods for individual item type.
 * public abstract VH onCreateItemViewHolder(ViewGroup parent, int viewType);
 * public abstract SH onCreateSubheaderViewHolder(ViewGroup parent, int viewType);
 * public abstract FH onCreateFooterViewHolder(ViewGroup parent, int viewType);
 *
 * Bind your views with data here.
 * @Override below methods to bind data to individual item types.
 * public abstract void onBindSubHeaderViewHolder(SH holder, Cursor cursor);
 * public abstract void onBindItemViewHolder(VH holder, Cursor cursor);
 * public abstract void onBindFooterViewHolder(FH holder, int itemPosition);
 *
 * Item type -1,-2,-3 are reserved, kindly do not pass them in getItemViewType.
 */

public abstract class RecyclerViewCursorAdapter<SH extends RecyclerView.ViewHolder, VH extends RecyclerView.ViewHolder, FH extends RecyclerView.ViewHolder>
        extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    public static final String TAG = RecyclerViewCursorAdapter.class.getSimpleName();

    private static final int TYPE_SECTION_HEADER = -1;

    private static final int TYPE_MAIN_HEADER = -2;
    private static final int TYPE_FOOTER = -3;

   // private int headerLayout=0,viewLayout=0;
    boolean createHeader;

    private List<Integer> subheaderPositions = new ArrayList<>();

    private Cursor mCursor;
    private boolean mDataValid,footerRequired=false;
    private int mRowIDColumn;
    private SparseBooleanArray mSelectedItemsIds;



   // public RecyclerViewCursorAdapter() { }

    //constructor
    public RecyclerViewCursorAdapter(Cursor c,boolean footerRequired) {
        setHasStableIds(true);
        swapCursor(c);
        this.footerRequired = footerRequired;

        this.mSelectedItemsIds = new SparseBooleanArray();
    }



    // interface for listning click on recycler view;
    public interface OnItemClickedListener{
        void OnItemClick(int id, Object data);

        void onItemLongClick(int id);
    }

    OnItemClickedListener onItemClickedListener;

    public void setOnItemClickedListener(OnItemClickedListener onItemClickedListener) {
        this.onItemClickedListener = onItemClickedListener;
    }

    public interface OnFooterClickedListener{
        void onFooterClick(Object data);
    }

    OnFooterClickedListener onFooterClickedListener;
    public void setOnFooterClickedListener( OnFooterClickedListener onFooterClickedListener){
        this.onFooterClickedListener = onFooterClickedListener;
    }

    public interface OnHeaderClickedListener{
        void onHeaderClick(Object data);
    }

    OnHeaderClickedListener onHeaderClickedListener;
    public void setOnHeaderClickedListener( OnHeaderClickedListener onHeaderClickedListener){
        this.onHeaderClickedListener = onHeaderClickedListener;
    }

    private void initSubheaderPositions() {
        subheaderPositions.clear();

        if(getItemSize() != 0) {
            //TODO:Handle This please.
            //subheaderPositions.add(0);
        } else {
            return;
        }

        for(int i = 1; i < getItemSize(); i++) {
            if(onPlaceSubheaderBetweenItems(i - 1)) {
                subheaderPositions.add(i + subheaderPositions.size());
            }
        }
    }

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {

            initSubheaderPositions();



    }

    /**
     * Called when adapter needs to know whether to place subheader between two neighboring
     * items.
     *
     * @return true if you want to place subheader between two neighboring
     * items.
     */
    public abstract boolean onPlaceSubheaderBetweenItems(int position);

    public abstract VH onCreateItemViewHolder(ViewGroup parent, int viewType);

    public abstract SH onCreateSubheaderViewHolder(ViewGroup parent, int viewType);

    public abstract FH onCreateFooterViewHolder(ViewGroup parent, int viewType);

    public abstract void onBindSubHeaderViewHolder(SH holder, Cursor cursor);

    public abstract void onBindItemViewHolder(VH holder, Cursor cursor);

    public abstract void onBindFooterViewHolder(FH holder, int itemPosition);






    public abstract int getItemSize();

    /**
     * Return the view type of the item at position for the purposes
     * of view recycling.
     * Don't return -1. It's reserved for subheader view type.
     */
    public int getViewType(int position) {
        return 0;
    }

    @Override
    public final int getItemViewType(int position) {

        if(isSubheaderOnPosition(position)) {
            return TYPE_SECTION_HEADER;
        } if(footerRequired && getCursor().getPosition()==(getCursor().getCount()-1)){
            return TYPE_FOOTER;
        }else {
            return getViewType(position);
        }
    }

    public boolean isFooterAdded(){
        return footerRequired;
    }
    @Override
    public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        Log.d("RVCA-OCVH","create viewholder");
        if(viewType == TYPE_SECTION_HEADER) {
            return onCreateSubheaderViewHolder(parent, viewType);
        } if(footerRequired&&viewType == TYPE_FOOTER){
            return onCreateFooterViewHolder(parent, viewType);
        }else {
            return onCreateItemViewHolder(parent, viewType);
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        Log.d("RVCA-OBVH","bind viewholder");
        Log.d("RVCA-OBVH","subheader position:"+isSubheaderOnPosition(position));
        if(isSubheaderOnPosition(position)) {
            if (!mDataValid) {
                throw new IllegalStateException("Cannot bind viewholder when cursor is in invalid state.");
            }
            if (!mCursor.moveToPosition(getItemPositionForViewHolder(position))) {
                throw new IllegalStateException("Could not move cursor to position " + getItemPositionForViewHolder(position) + " when trying to bind viewholder");
            }

            onBindSubHeaderViewHolder((SH)holder, mCursor);
        }if(footerRequired && position==getItemCount()-1){
            Log.d("RVCA-OBVH","bind footerHolder");
            onBindFooterViewHolder((FH) holder,position);

        } else {
            if (!mDataValid) {
                throw new IllegalStateException("Cannot bind viewholder when cursor is in invalid state.");
            }
            if (!mCursor.moveToPosition(getItemPositionForViewHolder(position))) {
                throw new IllegalStateException("Could not move cursor to position " + getItemPositionForViewHolder(position) + " when trying to bind viewholder");
            }
            // if(!mCursor.isAfterLast()) {
            //   mCursor.moveToPosition(position);
            onBindItemViewHolder((VH)holder, mCursor);

        }
    }

    @Override
    public final int getItemCount() {
        return getItemSize() + subheaderPositions.size()+(footerRequired?1:0);
    }

    public void notifyDataChanged() {
        initSubheaderPositions();
        notifyDataSetChanged();
    }

    public void notifyItemInsertedAtPosition(int itemPosition) {
        if (itemPosition == 0) {
            if (getItemCount() == 1 || onPlaceSubheaderBetweenItems(itemPosition)) {
                subheaderPositions.add(0, 0);
                increaseSubheaderPositions(1, 2);
                notifyItemRangeInserted(0, 2);
            } else {
                increaseSubheaderPositions(1, 1);
                notifyItemInserted(1);
            }
        } else if (itemPosition == getItemSize() - 1) {
            if (onPlaceSubheaderBetweenItems(itemPosition - 1)) {
                subheaderPositions.add(getItemCount() - 1);
                notifyItemRangeInserted(getItemCount() - 1, 2);
            } else {
                notifyItemInserted(getItemPositionInRecyclerView(itemPosition));
            }
        } else {
            if (onPlaceSubheaderBetweenItems(itemPosition - 1) && onPlaceSubheaderBetweenItems(itemPosition)) {
                final int itemPositionInRv = getItemPositionInRecyclerView(itemPosition - 1);
                final int countOfSubheadersBeforePosition = getCountOfSubheadersBeforePosition(itemPositionInRv);
                subheaderPositions.add(countOfSubheadersBeforePosition, itemPositionInRv + 1);
                increaseSubheaderPositions(countOfSubheadersBeforePosition + 1, 2);
                notifyItemRangeInserted(itemPositionInRv + 1, 2);
            } else if (onPlaceSubheaderBetweenItems(itemPosition)){
                final int itemPositionInRv = getItemPositionInRecyclerView(itemPosition - 1);
                increaseSubheaderPositions(getCountOfSubheadersBeforePosition(itemPositionInRv), 1);
                notifyItemInserted(itemPositionInRv + 1);
            } else if (onPlaceSubheaderBetweenItems(itemPosition - 1)) {
                final int itemPositionInRv = getItemPositionInRecyclerView(itemPosition);
                increaseSubheaderPositions(getCountOfSubheadersBeforePosition(itemPositionInRv), 1);
                notifyItemInserted(itemPositionInRv);
            } else {
                final int itemPositionInRv = getItemPositionInRecyclerView(itemPosition);
                increaseSubheaderPositions(getCountOfSubheadersBeforePosition(itemPositionInRv), 1);
                notifyItemInserted(itemPositionInRv);
            }
        }
    }

    public void notifyItemChangedAtPosition(int itemPosition) {
        final int itemPositionInRv = getItemPositionInRecyclerView(itemPosition);
        notifyItemChanged(itemPositionInRv);
    }

    public void notifyItemRemovedAtPosition(int itemPosition) {

        final int itemPositionInRv = getItemPositionInRecyclerView(itemPosition);

        for (int i = 1; i < subheaderPositions.size(); i++) {
            final int subheaderPosition = subheaderPositions.get(i);
            if (subheaderPosition > itemPositionInRv) {
                final int previousSubheaderPosition = subheaderPositions.get(i - 1);
                if (subheaderPosition - previousSubheaderPosition == 2) {
                    subheaderPositions.remove(subheaderPositions.indexOf(previousSubheaderPosition));
                    decreaseSubheaderPositions(subheaderPositions.indexOf(subheaderPosition), 2);
                    notifyItemRangeRemoved(itemPositionInRv - 1, 2);
                } else {
                    decreaseSubheaderPositions(subheaderPositions.indexOf(subheaderPosition), 1);
                    notifyItemRemoved(itemPositionInRv);
                }
                return;
            }
        }

        final int lastSubheaderPosition = subheaderPositions.get(subheaderPositions.size() - 1);
        if (itemPositionInRv - lastSubheaderPosition == 1 && getItemCount() == itemPosition + subheaderPositions.size()) {
            subheaderPositions.remove(subheaderPositions.size() - 1);
            notifyItemRangeRemoved(itemPositionInRv - 1, 2);
        } else {
            notifyItemRemoved(itemPositionInRv);
        }
    }

    public void setGridLayoutManager(final GridLayoutManager gridLayoutManager) {
        gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
            @Override
            public int getSpanSize(int position) {
                if(subheaderPositions.contains(position)) {
                    return gridLayoutManager.getSpanCount();
                } else {
                    return 1;
                }
            }
        });
    }

    public boolean isSubheaderOnPosition(int position) {
        return subheaderPositions.contains(position);
    }

    public int getCountOfSubheadersBeforePosition(int position) {
        int count = 0;
        for(int subheaderPosition : subheaderPositions) {
            if(subheaderPosition < position) {
                count++;
            }
        }
        return count;
    }

    public int getItemPositionInRecyclerView(int position) {
        int countOfItems = 0;
        for (int i = 1; i < subheaderPositions.size(); i++) {
            final int previousSubheaderPosition = subheaderPositions.get(i - 1);
            final int nextSubheaderPosition = subheaderPositions.get(i);
            countOfItems += nextSubheaderPosition - previousSubheaderPosition - 1;
            if (countOfItems > position) {
                return position + i;
            }
        }
        return position + subheaderPositions.size();
    }

    public int getItemPositionForViewHolder(int viewHolderPosition) {
        return viewHolderPosition - getCountOfSubheadersBeforePosition(viewHolderPosition);
    }

    private void decreaseSubheaderPositions(int startSubheaderPosition, int decreaseNum) {
        for (int i = startSubheaderPosition; i < subheaderPositions.size(); i++) {
            final int subheaderPosition = subheaderPositions.get(i);
            subheaderPositions.set(i, subheaderPosition - decreaseNum);
        }
    }

    private void increaseSubheaderPositions(int startSubheaderPosition, int increaseNum) {
        for (int i = startSubheaderPosition; i < subheaderPositions.size(); i++) {
            final int subheaderPosition = subheaderPositions.get(i);
            subheaderPositions.set(i, subheaderPosition + increaseNum);
        }
    }

    private List<Integer> getSubheaderPositions() {
        return subheaderPositions;
    }

    public int getSubheaderCount() {
        return subheaderPositions.size();
    }

    public void swapCursor(Cursor newCursor) {
        Log.d("RVCA-SC","swap cursor");
        if (newCursor == mCursor) {
            Log.d("RVCA-SC","same cursor doing nothing");
            return;
        }

        if (newCursor != null) {
            Log.d("RVCA-SC","swap cursor not null");
            mCursor = newCursor;
            mRowIDColumn = mCursor.getColumnIndexOrThrow("_id");
            mDataValid = true;

            // notify the observers about the new cursor
            notifyDataChanged();
        } else {
            Log.d("RVCA-SC","swap cursor null");
            notifyItemRangeRemoved(0, getItemCount());
            mCursor = null;
            mRowIDColumn = -1;
            mDataValid = false;
        }
    }

    public Cursor getCursor(){
        return mCursor ;
    }

    @Override
    public long getItemId(int position) {
        if (isSubheaderOnPosition(position))
            return position;
        else {
            int cursorPosition = getItemPositionForViewHolder(position);
            Cursor cursor = getCursor();
            if (hasOpenCursor() && cursor.moveToPosition(cursorPosition)) {
                return cursor.getLong(cursor.getColumnIndex("_id"));
            }
            return NO_CURSOR_POSITION;
        }
    }
    public static final int NO_CURSOR_POSITION = -99;

    protected boolean hasOpenCursor() {
        Cursor cursor = getCursor();
        if (cursor == null || cursor.isClosed()) {
            swapCursor(null);
            return false;
        }
        return true;
    }

    //Methods to do Selection

    public void toggleSelection(int position) {
        selectView(position, !mSelectedItemsIds.get(position));
    }


    //Remove selected selections
    public void removeSelection() {
        mSelectedItemsIds = new SparseBooleanArray();
        notifyDataSetChanged();
    }


    //Put or delete selected position into SparseBooleanArray
    public void selectView(int position, boolean value) {
        if (value)
            mSelectedItemsIds.put(position, value);
        else
            mSelectedItemsIds.delete(position);

        // notifyItemChangedAtPosition(position);
        notifyDataSetChanged();
    }

    //Get total selected count
    public int getSelectedCount() {
        return mSelectedItemsIds.size();
    }

    //Return all selected ids
    public SparseBooleanArray getSelectedIds() {
        return mSelectedItemsIds;
    }

    public void setSelectedItemIds(SparseBooleanArray selectedItemIds){
        this.mSelectedItemsIds=selectedItemIds;
    }


}

0

L'implémentation la plus simple consiste à utiliser un objet curseur dans l'adaptateur et à passer le curseur au constructeur de viewholder pour modifier les vues et changer la position du curseur dans onBindViewholder.

Si vous utilisez le curseur Loader, appelez la méthode setCursor onLoadfinished()et passez null dansonLoadReset()

public class PetCursorRecyclerViewAdapter extends RecyclerView.Adapter<PetCursorRecyclerViewAdapter.ViewHolder> {
Cursor cursor = null;
Context context;

public PetCursorRecyclerViewAdapter(Context context) {
    this.context = context;

}

public void setCursor(Cursor cursor) {
    this.cursor = cursor;
    notifyDataSetChanged();
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View v = LayoutInflater.from(context).inflate(R.layout.catalog_item, parent, false);
    return new ViewHolder(v);
}

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    this.cursor.moveToPosition(position);
    holder.bindModel(this.cursor);
}

/*   getItemCount() returns the count of videos from the Cursor, or 0 if the
   Cursor is null (mimicking the behavior of CursorAdapter, which also treats
   a null Cursor as merely being one that has no rows)*/
@Override
public int getItemCount() {
    if (cursor == null) {
        return 0;
    } else {
        return cursor.getCount();
    }
}

public static class ViewHolder extends RecyclerView.ViewHolder {
    private TextView name_tv, breed_tv;

    public ViewHolder(View itemView) {
        super(itemView);
        name_tv = (TextView) itemView.findViewById(R.id.name_tv);
        breed_tv = (TextView) itemView.findViewById(R.id.breed_tv);
    }

    public void bindModel(Cursor cursor) {
        int name_index = cursor.getColumnIndexOrThrow(PetsContract.PetEntry.COLUMN_PET_NAME);
        int breed_index = cursor.getColumnIndexOrThrow(PetsContract.PetEntry.COLUMN_PET_BREED);
        name_tv.setText(cursor.getString(name_index));
        String breed = cursor.getString(breed_index);
        if (TextUtils.isEmpty(breed)) {
            breed = "Unknown Breed";
        }
        breed_tv.setText(breed);
    }
}    

}


0

Enfin, nous avons une implémentation de RecyclerView.Adapter pour base de données / réseau.

Il a réalisé avec une architecture Android Composants :

Base de données de la salle: couche de base de données au-dessus de la base de données SQLite qui prend en charge les tâches banales que vous avez l'habitude de gérer avec un SQLiteOpenHelper. Support de base de données qui sert de point d'accès à la base de données SQLite sous-jacente. La base de données Room utilise le DAO pour émettre des requêtes vers la base de données SQLite.

ViewModel: fournit des données à l'interface utilisateur. Agit comme un centre de communication entre le référentiel et l'interface utilisateur. Masque l'origine des données de l'interface utilisateur. Les instances ViewModel survivent à la récréation Activité / Fragment.

LiveData: une classe de détenteurs de données qui peut être observée. Contient / met toujours en cache la dernière version des données. Avertit ses observateurs lorsque les données ont changé. LiveData est conscient du cycle de vie. Les composants de l'interface utilisateur observent simplement les données pertinentes et n'arrêtent ni ne reprennent l'observation. LiveData gère automatiquement tout cela car il est conscient des changements d'état du cycle de vie pertinents tout en observant.


-2

Vous pouvez utiliser la base de données SQLite pour stocker les détails. Il est facile d'accéder aux données. Vous pouvez vérifier mon code sur github. https://github.com/thiru-wta/ToDo

     database = new Database(this);
    getSupportActionBar().setTitle("To Do List");
    etAddItems = (EditText) findViewById(R.id.etAddItem);
    btnAdd = (Button) findViewById(R.id.btnAdd);
    mRecyclerView = (RecyclerView) findViewById(R.id.listView);
    mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
    adapter = new RecyclerAdapter(this, listData);
    mRecyclerView.setAdapter(adapter);

    btnAdd.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            event_name = etAddItems.getText().toString();
            String getname="";

              database.storeEventDetails(event_name);

                 getname = database.getEventDetails(event_name);


            if (getname.length() != 0) {
                listData.add(getname);
                etAddItems.setText("");

                adapter.notifyData(listData);
            }

Méthodes de base de données:

    public void storeEventDetails(String event_name, long timerStart) {
    SQLiteDatabase db1 = getWritableDatabase();
    db1.execSQL("insert into '"+event_details_tbl+"' values('" + event_name + "')");
    db1.close();
}

méthode get:

     public String getEventDetails(String event_name) {
    SQLiteDatabase db1 = getReadableDatabase();
    Cursor cur = db1.rawQuery("select * from '"+event_details_tbl+"' where     event_name ='" + event_name + "'", null);
    cur.moveToFirst();
    String evName = null;
    if (cur != null) {
        do {
            int eventName = cur.getColumnIndex("event_name");

            String ev_name = cur.getString(eventName);

            evName = ev_name;
        } while (cur.moveToNext());

    }
    cur.close();
    db1.close();
    return evName;

}     

-4

Il vous suffit de suivre ces étapes:

  • Dans l'outil de recyclage view.adapter, créez une ArrayList ou un Iterator initialisé avec une valeur nulle.
  • Créez une méthode comme l'ex swapcursor, par exemple swapdata(Arraylist<T> data). À l'intérieur, vous donnez une nouvelle valeur à la liste de tableaux, à l'itérateur ou à toute structure que le système peut itérer en utilisant la position entière dans la vue de liaison. Les valeurs de cette méthode sont transmises dans le onloaderfinished(). Appelez ensuite juste après l'affectation notifydatachange(); ceux-ci demanderont au RecyclerViewde redessiner toute la liste avec les nouvelles données de l'ArrayList.

Dans l'activité ou le fragment où vous utilisez le loadercalback, créez une méthode qui transforme le curseur en arraylist, ou itérateur, en fonction de la structure de données choisie dans l'adaptateur.

De cette façon, vous pouvez toujours utiliser le loadercalback sans bloquer le thread principal.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.