日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

RecyclerView.Adapter:全能notify解决方案

發(fā)布時(shí)間:2025/4/16 编程问答 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 RecyclerView.Adapter:全能notify解决方案 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

原文鏈接: https://loshine.me/2016/08/25/a-universal-solution-of-recyclerview-adapter-notify/

在之前我們用 ListView 或者 GridView 的時(shí)候,通知適配器刷新是這樣的:

adapter.notifyDataSetChanged();

但是當(dāng)我們使用了更強(qiáng)大的 RecyclerView 之后,如果直接這樣通知適配器刷新將不會(huì)顯示動(dòng)畫效果。它會(huì)直接將所有的 item 重新繪制。

我們需要使用如下的方法來通知適配器刷新,這樣 RecyclerView 才會(huì)顯示對(duì)應(yīng)的動(dòng)畫效果:

adapter.notifyItemInserted(); adapter.notifyItemChanged(); adapter.notifyItemMoved(); adapter.notifyItemRemoved(); adapter.notifyItemRangeChanged(); adapter.notifyItemRangeInserted(); adapter.notifyItemRangeRemoved();

在這次更新的 Support Library 24.2.0 中添加了一個(gè)新的工具類,可以用來方便快捷的處理 RecyclerView.Adapter 的通知刷新。

DiffUtil

DifUtil 就是這次引入的工具類,它會(huì)找出 Adapter 中每一個(gè) Item 對(duì)應(yīng)發(fā)生的變化,然后對(duì)每一個(gè)變化給予對(duì)應(yīng)的刷新。

最重要的就是如下的兩個(gè)重載方法

DifUtil.calculateDiff(Callback cb, boolean detectMoves); DifUtil.calculateDiff(Callback cb);

其中DifUtil.calculateDiff(Callback cb);實(shí)際上就是DifUtil.calculateDiff(callback, true);所以我們著重研究第一個(gè)方法即可。

該方法會(huì)接收兩個(gè)參數(shù),其中第二個(gè)參數(shù)是一個(gè) boolean 值,查看源碼注釋我們知道這個(gè)參數(shù)有如下作用:

True if DiffUtil should try to detect moved items, false otherwise.
如果 DiffUtil 嘗試檢測(cè)移動(dòng)的項(xiàng)目就設(shè)為 true,否則設(shè)為 false。
這個(gè)參數(shù)實(shí)際上是指定是否需要項(xiàng)目移動(dòng)的檢測(cè),如果設(shè)為 false ,那么一個(gè)項(xiàng)目移動(dòng)了會(huì)先判定為 remove,再判定為 insert。

而Callback是一個(gè)抽象類,它有四個(gè)方法需要實(shí)現(xiàn):

public abstract static class Callback {/*** 舊的數(shù)據(jù)源的大小*/public abstract int getOldListSize();/*** 新的數(shù)據(jù)源的大小*/public abstract int getNewListSize();/*** 該方法用于判斷兩個(gè) Object 是否是相同的 Item,比如有唯一標(biāo)識(shí)的時(shí)候應(yīng)該比較唯一標(biāo)識(shí)是否相等*/public abstract boolean areItemsTheSame(int oldItemPosition, int newItemPosition);/*** 當(dāng) areItemsTheSame 返回 true 時(shí)調(diào)用該方法,返回顯示的 Item 的內(nèi)容是否一致*/public abstract boolean areContentsTheSame(int oldItemPosition, int newItemPosition); }

如上所述,我們四個(gè)需要實(shí)現(xiàn)的方法的作用都在注釋中寫出了。前兩個(gè)方法都很好理解,需要重點(diǎn)說明的是后兩個(gè)

areItemsTheSame:這個(gè)方法用來判斷兩個(gè) Object 是否是相同的 Item,此處最好不要簡(jiǎn)單的用equals方法判斷,我們可以根據(jù) Object 的唯一標(biāo)識(shí)或者自己指定一個(gè)規(guī)則來判斷兩個(gè) Object 是否是展示的相同的 Item。
areContentsTheSame:該方法只有在areItemsTheSame返回true之后才會(huì)被調(diào)用,我們?cè)谥貙懺摲椒ǖ臅r(shí)候,只需要判斷兩個(gè) Object 顯示的元素是否一致即可。如我們有兩個(gè) Object,它們可能擁有很多屬性,但是其中只有兩個(gè)屬性需要被顯示出來,那只要這兩個(gè)屬性一致我們這個(gè)方法就要返回true。
使用 DiffUtils 通知刷新

下面我們寫一個(gè)簡(jiǎn)單的例子來學(xué)習(xí)使用 DiffUtil

首先我們來一個(gè) Item 對(duì)應(yīng)的數(shù)據(jù)類:

public class Student {public String id; // 學(xué)號(hào)是唯一的public String name; // 名字可能重復(fù)public Student(String id, String name) {this.id = id;this.name = name;} }

然后寫一個(gè) Adapter:

class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {private final List<Student> datas;public MyAdapter(List<Student> datas) {this.datas = datas;}@Overridepublic ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recycler, parent, false);return new ViewHolder(view);}@Overridepublic void onBindViewHolder(ViewHolder holder, int position) {holder.setData(datas.get(position));}@Overridepublic int getItemCount() {return datas.size();}class ViewHolder extends RecyclerView.ViewHolder {public ViewHolder(View itemView) {super(itemView);}public void setData(Student student) {TextView textView = (TextView) this.itemView.findViewById(R.id.text);textView.setText(student.name);}} }

其對(duì)應(yīng)的布局文件就是一個(gè)簡(jiǎn)單的 TextView:

<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/text"android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:orientation="vertical"android:padding="10dp"tools:text="content"/>

然后我們?cè)?Activity 里使用它們并顯示出來:

class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {// ...mRandom = new Random();datas = new ArrayList<>();for (int i = 0; i < 10; i++) {datas.add(new Student(mRandom.nextInt(3000) + "", "Students: " + i));}mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);mRecyclerView.setLayoutManager(new LinearLayoutManager(this));mAdapter = new MyAdapter(datas);mRecyclerView.setAdapter(mAdapter);// ...} }

這樣我們就獲得了一個(gè)簡(jiǎn)單的展示學(xué)生數(shù)據(jù)的 RecyclerView 了。

然后我們對(duì) Adapter 的數(shù)據(jù)源進(jìn)行更改,并通知刷新:

mFab.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {// 創(chuàng)建一個(gè)原來的 List 的副本final ArrayList<Student> oldTemp = new ArrayList<>(datas);// 更改原數(shù)據(jù)源datas.remove(mRandom.nextInt(mAdapter.getItemCount()));for (int i = 0; i < mRandom.nextInt(3); i++) {datas.add(mRandom.nextInt(mAdapter.getItemCount() - 1),new Student(mRandom.nextInt(3000) + "", "Students: " + mRandom.nextDouble()));}// 實(shí)現(xiàn) CallbackDiffUtil.Callback callback = new DiffUtil.Callback() {@Overridepublic int getOldListSize() {return oldTemp.size();}@Overridepublic int getNewListSize() {return datas.size();}@Overridepublic boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {return oldTemp.get(oldItemPosition).id.equals(datas.get(newItemPosition).id);}@Overridepublic boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {return oldTemp.get(oldItemPosition).name.equals(datas.get(newItemPosition).name);}};DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(callback);// 把結(jié)果應(yīng)用到 adapterdiffResult.dispatchUpdatesTo(mAdapter);}});

效果如下:

DiffUtil 的使用就是這樣,根據(jù) DiffUtil.Callback 計(jì)算出 Result,然后應(yīng)用更新到 Adapter。

封裝

有的人可能說了,這樣其實(shí)并不好用啊,我們?cè)瓉頂?shù)據(jù)的改變就直接使用對(duì)應(yīng)的方法就可以了,你這里每次還要寫得這么麻煩。那么我們就使用 DiffUtil 和 Adapter 結(jié)合再進(jìn)行一次封裝吧。

我們抽取一個(gè) BaseAdapter 出來:

public abstract class BaseAdapter<T, V extends RecyclerView.ViewHolder>extends RecyclerView.Adapter<V>{protected final List<T> temp; // 用于保存修改之前的數(shù)據(jù)源的副本protected final List<T> datas; // 數(shù)據(jù)源public BaseAdapter(List<T> datas) {this.datas = datas;temp = new ArrayList<>(datas);}protected abstract boolean areItemsTheSame(T oldItem, T newItem);protected abstract boolean areContentsTheSame(T oldItem, T newItem);@Overridepublic int getItemCount() {return datas.size();}public void notifyDiff() {DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffUtil.Callback() {@Overridepublic int getOldListSize() {return temp.size();}@Overridepublic int getNewListSize() {return datas.size();}// 判斷是否是同一個(gè) item@Overridepublic boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {return BaseAdapter.this.areItemsTheSame(temp.get(oldItemPosition), datas.get(newItemPosition));}// 如果是同一個(gè) item 判斷內(nèi)容是否相同@Overridepublic boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {return BaseAdapter.this.areContentsTheSame(temp.get(oldItemPosition), datas.get(newItemPosition));}});diffResult.dispatchUpdatesTo(this);// 通知刷新了之后,要更新副本數(shù)據(jù)到最新temp.clear();temp.addAll(datas);} }

然后我們只需要令 Adapter 實(shí)現(xiàn) BaseAdapter即可:

class MyAdapter extends BaseAdapter<Student, MyAdapter.ViewHolder> {public MyAdapter(List<Student> datas) {super(datas);}@Overridepublic ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recycler, parent, false);return new ViewHolder(view);}@Overridepublic void onBindViewHolder(ViewHolder holder, int position) {holder.setData(datas.get(position));}@Overridepublic boolean areItemsTheSame(Student oldItem, Student newItem) {return oldItem.id.equals(newItem.id);}@Overridepublic boolean areContentsTheSame(Student oldItem, Student newItem) {return oldItem.name.equals(newItem.name);}class ViewHolder extends RecyclerView.ViewHolder {public ViewHolder(View itemView) {super(itemView);}public void setData(Student student) {TextView textView = (TextView) this.itemView.findViewById(R.id.text);textView.setText(student.name);}} }

之后我們?nèi)绻麛?shù)據(jù)源 List 中的數(shù)據(jù)有任何改動(dòng),我們只需要調(diào)用notifyDiff()就可以了:

mFab.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {datas.remove(mRandom.nextInt(mAdapter.getItemCount()));for (int i = 0; i < mRandom.nextInt(3); i++) {datas.add(mRandom.nextInt(mAdapter.getItemCount() - 1),new Student(mRandom.nextInt(3000) + "", "Students: " + mRandom.nextDouble()));}mAdapter.notifyDiff();}});

總結(jié)

最新 Support 包中的 DiffUtil 類給我們帶來了一個(gè)對(duì) RecyclerView 的不同數(shù)據(jù)變化的統(tǒng)一處理方案,可以對(duì)所有數(shù)據(jù)變化之后的通知刷新簡(jiǎn)化,非常好用,強(qiáng)烈推薦使用。

參考

Android開發(fā)學(xué)習(xí)之路-DiffUtil使用教程

Android 7.0帶來的新工具類:DiffUtil

RecyclerView:使用DiffUtil刷新錯(cuò)位

【Android】你可能不知道的Support(一) 0步自動(dòng)定向刷新:SortedList

原文鏈接:http://www.sxrczx.com/pages/kohoh1992.github.io/cursor-auto-sync/index_1431878338570.html

在Android日常開發(fā)中,時(shí)常會(huì)請(qǐng)求數(shù)據(jù)到Cursor,然后再通過Cursor獲取數(shù)據(jù)。像SQLiteDatabase和ContentProvider都使用了Cursor。在這些應(yīng)用中,往往希望當(dāng)數(shù)據(jù)發(fā)生改變時(shí),Cursor也會(huì)自動(dòng)的更新數(shù)據(jù)。這篇文章,我就會(huì)向你闡述如何通過Android自身的API實(shí)現(xiàn)Cursor的自動(dòng)更新。另外我還將向你闡述這背后的原理。通過這些原理你可以舉一反三的實(shí)現(xiàn)更為廣泛的自動(dòng)跟新。

文章中的代碼

可以在https://github.com/KOHOH1992/CursorSyncDemo中找到文章中出現(xiàn)的代碼

該項(xiàng)目共有4個(gè)分支。use_provider分支介紹了使用ContentProvider實(shí)現(xiàn)Cursor同步更新的方法。use_database分支介紹了不使用ContentProvider實(shí)現(xiàn)Cursor同步更新的方法。use_adapter分支介紹了不使用Loader實(shí)現(xiàn)Cursor同步更新的方法。

Cursor自動(dòng)更新的實(shí)現(xiàn)

前提

首先假設(shè)項(xiàng)目使用了如下的前提

  • 數(shù)據(jù)存儲(chǔ)在SqliteDataBase當(dāng)中
  • 通對(duì)ContentProvider的請(qǐng)求,獲取封裝了數(shù)據(jù)的Cursor
  • 使用CursorLoader加載數(shù)據(jù)
  • 使用AdapterView和CursorAdapter顯示數(shù)據(jù)

定義同步標(biāo)志

static final Uri SYNC_SIGNAL_URI = Uri.parse("content://com.kohoh.cursorsyncdemo/SYNC_SIGNAL");

在ContentProvider的query中設(shè)置NotificationUri

@Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder) {SQLiteDatabase database = sqLiteOpenHelper.getReadableDatabase();Cursor cursor = database.query(ContactContract.CONTACT_TABLE, projection,selection,selectionArgs, null, null, sortOrder);//設(shè)置NotificationUricursor.setNotificationUri(contentResolver, ContactContract.SYNC_SIGNAL_URI);return cursor; }

在ContentProvider的insert,update,delete中觸發(fā)NotificationUri

@Override public Uri insert(Uri uri, ContentValues values) {SQLiteDatabase database = sqLiteOpenHelper.getWritableDatabase();long id = database.insert(ContactContract.CONTACT_TABLE, null, values);if (id >= 0) {//觸發(fā)NotificationUricontentResolver.notifyChange(ContactContract.SYNC_SIGNAL_URI, null);}return uri.withAppendedPath(ContactContract.CONTACT_URI, String.valueOf(id)); } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {SQLiteDatabase database = sqLiteOpenHelper.getWritableDatabase();int result = database.update(ContactContract.CONTACT_TABLE, values, selection, selectionArgs);if (result > 0) {//觸發(fā)NotificationUricontentResolver.notifyChange(ContactContract.SYNC_SIGNAL_URI, null);}return result; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) {SQLiteDatabase database = sqLiteOpenHelper.getWritableDatabase();int result = database.delete(ContactContract.CONTACT_TABLE, selection, selectionArgs);if (result > 0) {//觸發(fā)NotificationUricontentResolver.notifyChange(ContactContract.SYNC_SIGNAL_URI, null);}return result; }

CursorLoader

ForceLoadContentObserver mObserver;public Cursor loadInBackground() {...try {//不過多解釋,耗時(shí)的查詢操作Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection,mSelectionArgs, mSortOrder, mCancellationSignal);if (cursor != null) {try {// Ensure the cursor window is filled.cursor.getCount();//給Cursor設(shè)置觀察者;ContentProvider通知Cursor的觀察者數(shù)據(jù)發(fā)生了改變,//Cursor通知CursorLoader的觀察者數(shù)據(jù)發(fā)生了改變,CursorLoader通過ContentProvider重新加載新的數(shù)據(jù)cursor.registerContentObserver(mObserver);//cursor.setNotificationUri(getContext().getContentResolver(), otificationUri);//給Cursor設(shè)置要觀察的URI} catch (RuntimeException ex) {cursor.close();throw ex;}}return cursor;} }

Cursor的實(shí)現(xiàn)原理

Android的Cursor自動(dòng)更新是通過觀察者模式實(shí)現(xiàn)的,整個(gè)過程如下圖所示

  • 通過ContentPorvider和ContentResolver使得數(shù)據(jù)發(fā)生了改變
  • ContentProvider通知Cursor的觀察者數(shù)據(jù)發(fā)生了改變
  • Cursor通知CursorLoader的觀察者數(shù)據(jù)發(fā)生了改變
  • CursorLoader通過ContentProvider加載新的數(shù)據(jù)
  • ContentPovider向DataBase請(qǐng)求新的數(shù)據(jù)
  • CursorLoader調(diào)用CursorAdapter# changeCursor,用封裝了新數(shù)據(jù)的Cursor替換舊的Cursor
  • CursorAdapter告知AdapterView的觀察者有新的數(shù)據(jù)
  • AdapterView重新加載并顯示數(shù)據(jù)

在Android的android.database包下,有一個(gè)ContentObserver。Android正是通過他來實(shí)現(xiàn)觀察者模式的。當(dāng)數(shù)據(jù)改變之后,觀察者會(huì)將數(shù)據(jù)改變的消息通知相應(yīng)的對(duì)象,進(jìn)而做出反饋。在代碼中,當(dāng)數(shù)據(jù)改變之后,我會(huì)調(diào)用ContentResolver# notifyChange,發(fā)出ContactContract.SYNC_SIGNAL_URI信號(hào),通知數(shù)據(jù)發(fā)生了改變。而在此之前,從ContentProvider# query中獲得的Cursor已經(jīng)通過Cursor# setNotificationUri對(duì)ContactContract.SYNC_SIGNAL_URI信號(hào)進(jìn)行了監(jiān)視。當(dāng)該信號(hào)出現(xiàn),Cursor就會(huì)將信息改變的消息告訴CursorLoader的觀察者(在此之前CursorLoader已經(jīng)對(duì)該Cursor設(shè)立了觀察者)。CursorLoader會(huì)開始重新開始加載數(shù)據(jù)。當(dāng)數(shù)據(jù)加載成功,CursorLoader會(huì)通過CursorAdapter# changeCursor設(shè)置封裝了新數(shù)據(jù)的Cursor。而后CursorAdapter又會(huì)通知AdapterView的觀察者數(shù)據(jù)發(fā)生了改變(在此之前AdapterView已經(jīng)對(duì)CursorAdapter設(shè)立了觀察者)。最后AdapterView就會(huì)重新加載并顯示新的數(shù)據(jù)。

在整個(gè)過程當(dāng)中,我要做的就是在改變數(shù)據(jù)時(shí)發(fā)出信號(hào),對(duì)封裝數(shù)據(jù)的Cursor設(shè)置需要監(jiān)視的信號(hào)。具體的說就是在query中調(diào)用Cursor# setNotificationUri,在insert、update、delete中調(diào)用ContentResolver# notifyChange。這里需要補(bǔ)充的是Cursor和ContentResolver的信號(hào)機(jī)制同樣是通過觀察者模式實(shí)現(xiàn)的。

其他的實(shí)現(xiàn)方式

這里要介紹的其他的實(shí)現(xiàn)方式,依舊是通過觀察者模式實(shí)現(xiàn)的。區(qū)別在于是否使用ContentProvider和CursorLoader

不使用ContentProvider

在開發(fā)過程中,如果數(shù)據(jù)不用于應(yīng)用之間的共享,使用ContentProvider似乎有一些多余。然而Android提供的CursorLoader的API必須通過ContentProvider才能實(shí)現(xiàn)數(shù)據(jù)加載和數(shù)據(jù)同步更新。但是你任然可以在不使用ContentProvider的情況下實(shí)現(xiàn)Cursor的自動(dòng)更新。你需要做的只是在你的Loader中加入下面的代碼

// 實(shí)例化一個(gè)全局的ForceLoadContentObserver ForceLoadContentObserver mObserver = new ForceLoadContentObserver(); @Override public Cursor loadInBackground() {SQLiteDatabase database = mSqLiteOpenHelper.getReadableDatabase();Cursor cursor = database.query(mTable, mColumns, mSelection, mSelectionArgs, mGroupBy,mHaving, mOrderBy);if (cursor != null) {cursor.getCount();// 對(duì)Cursor設(shè)立觀察者cursor.registerContentObserver(mObserver);// 設(shè)置Cursor的觀察信號(hào)cursor.setNotificationUri(getContext().getContentResolver(), mNotificationUri);}return cursor; }

ForceLoadContentObserver是Loader的內(nèi)部類。當(dāng)觀察到數(shù)據(jù)發(fā)生變化之后,該類會(huì)調(diào)用Loader# forceLoad,進(jìn)而開始重新加載數(shù)據(jù)。另外你也可以直接使用我項(xiàng)目中的DatabaseLoader。該類是我參照CursorLoader編寫的一個(gè)工具,通過它你可以繞過ContentProvider,直接請(qǐng)求Database。

不使用Loader

如果你不想要使用Loader(我非常不贊成你這么做),你可以通過如下的代碼實(shí)現(xiàn)Cursor的同步更新。

// 使用CursorAdapter.FLAG_AUTO_REQUERY標(biāo)志 adapter = new SimpleCursorAdapter(this, R.layout.contact_item, null, from, to,CursorAdapter.FLAG_AUTO_REQUERY); private void loadData() {SQLiteOpenHelper sqliteOpenHelper = ContactContract.getSqliteOpenHelper(this);SQLiteDatabase database = sqliteOpenHelper.getReadableDatabase();String[] columns = {ContactContract._ID, ContactContract.NAME, ContactContract.PHONE};Cursor cursor = database.query(ContactContract.CONTACT_TABLE, columns, null, null, null,null, null);//設(shè)置NotificationUricursor.setNotificationUri(this.getContentResolver(), ContactContract.SYNC_SIGNAL_URI);adapter.changeCursor(cursor); }

這里的關(guān)鍵在于,在實(shí)例化CursorAdapter時(shí)使用了CursorAdapter.FLAGAUTOREQUERY標(biāo)志。當(dāng)使用該標(biāo)志后,每當(dāng)收到數(shù)據(jù)更新的消息,CursorAdapter就會(huì)自己調(diào)用CursorAdapter# requery重新加載數(shù)據(jù)。然而整個(gè)加載過程會(huì)再UI線程中發(fā)生,這很有可能會(huì)使得程序運(yùn)行部流暢。正是因?yàn)檫@個(gè)原因該方法以及被Android設(shè)置為Deprecated了。因此如果有可能,我還是推薦你使用Loader。

ContactContract.java

package com.kohoh.cursorsyncdemo;import android.content.ContentValues; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.net.Uri; import android.provider.BaseColumns;import com.google.common.base.Preconditions; import com.google.common.base.Strings;/*** Created by kohoh on 14-11-3.*/ public class ContactContract implements BaseColumns {static final int DATABSE_VERSION = 1;static final String DATABASE_NAME = "contact.db";static final String CONTACT_TABLE = "contact";static final String NAME = "name";static final String PHONE = "phone";static final String AUTHORITY = "com.kohoh.cursorsyncdemo";static final Uri BASE_URI = Uri.parse("content://" + AUTHORITY);static final Uri CONTACT_URI = Uri.withAppendedPath(BASE_URI, "contact");static final Uri SYNC_SIGNAL_URI = Uri.withAppendedPath(BASE_URI, "SYNC_SIGNAL_URI");static public ContactDatabaseHelper getSqliteOpenHelper(Context context) {return new ContactDatabaseHelper(context);}static class ContactDatabaseHelper extends SQLiteOpenHelper {public ContactDatabaseHelper(Context context) {super(context, DATABASE_NAME, null, DATABSE_VERSION);}static public long addContact(SQLiteDatabase database, String name, int phone) {Preconditions.checkNotNull(database);Preconditions.checkNotNull(phone);Preconditions.checkArgument(!Strings.isNullOrEmpty(name));ContentValues contentValues = new ContentValues();contentValues.put(NAME, name);contentValues.put(PHONE, phone);return database.insert(CONTACT_TABLE, null, contentValues);}static public void deleteContact(Context context, long id) {Preconditions.checkNotNull(context);Preconditions.checkArgument(id >= 0);ContactContract.ContactDatabaseHelper databaseHelper = ContactContract.getSqliteOpenHelper(context);SQLiteDatabase databasea = databaseHelper.getWritableDatabase();String where = ContactContract._ID + " = ?";String[] whereArgs = {String.valueOf(id)};databasea.delete(ContactContract.CONTACT_TABLE, where, whereArgs);context.getContentResolver().notifyChange(ContactContract.SYNC_SIGNAL_URI, null);}@Overridepublic void onCreate(SQLiteDatabase db) {db.execSQL("CREATE TABLE " + CONTACT_TABLE + "( " +_ID + " INTEGER PRIMARY KEY," +NAME + " TEXT," +PHONE + " INTERGER)");addContact(db, "aaa", 111);addContact(db, "bbb", 222);addContact(db, "ccc", 333);addContact(db, "ddd", 444);}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}} }

ContactProvider.java

package com.kohoh.cursorsyncdemo;import android.content.ContentProvider; import android.content.ContentResolver; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.net.Uri;/*** Created by kohoh on 14-11-3.*/ public class ContactProvider extends ContentProvider {private SQLiteOpenHelper sqLiteOpenHelper;private ContentResolver contentResolver;private UriMatcher uriMatcher;final private int DIR = 0;final private int ITEM = 1;@Overridepublic boolean onCreate() {sqLiteOpenHelper = ContactContract.getSqliteOpenHelper(getContext());contentResolver = getContext().getContentResolver();uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);uriMatcher.addURI(ContactContract.AUTHORITY, "contact", DIR);uriMatcher.addURI(ContactContract.AUTHORITY, "contact/#", ITEM);return true;}@Overridepublic Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder) {if (uriMatcher.match(uri) == ITEM) {return null;}SQLiteDatabase database = sqLiteOpenHelper.getReadableDatabase();Cursor cursor = database.query(ContactContract.CONTACT_TABLE, projection, selection,selectionArgs, null, null, sortOrder);cursor.setNotificationUri(contentResolver, ContactContract.SYNC_SIGNAL_URI);return cursor;}@Overridepublic String getType(Uri uri) {switch (uriMatcher.match(uri)) {case ITEM:return "vnd.android.cursor.item/vnd.con.kohoh.cursorsyncdemo";case DIR:return "vnd.android.cursor.dir/vnd.con.kohoh.cursorsyncdemo";default:return null;}}@Overridepublic Uri insert(Uri uri, ContentValues values) {if (uriMatcher.match(uri) == ITEM) {return null;}SQLiteDatabase database = sqLiteOpenHelper.getWritableDatabase();long id = database.insert(ContactContract.CONTACT_TABLE, null, values);if (id >= 0) {contentResolver.notifyChange(ContactContract.SYNC_SIGNAL_URI, null);}return uri.withAppendedPath(ContactContract.CONTACT_URI, String.valueOf(id));}@Overridepublic int delete(Uri uri, String selection, String[] selectionArgs) {if (uriMatcher.match(uri) == ITEM) {return 0;}SQLiteDatabase database = sqLiteOpenHelper.getWritableDatabase();int result = database.delete(ContactContract.CONTACT_TABLE, selection, selectionArgs);if (result > 0) {contentResolver.notifyChange(ContactContract.SYNC_SIGNAL_URI, null);}return result;}@Overridepublic int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {if (uriMatcher.match(uri) == ITEM) {return 0;}SQLiteDatabase database = sqLiteOpenHelper.getWritableDatabase();int result = database.update(ContactContract.CONTACT_TABLE, values, selection, selectionArgs);if (result > 0) {contentResolver.notifyChange(ContactContract.SYNC_SIGNAL_URI, null);}return result;} }

DatabaseLoader.java

package com.kohoh.cursorsyncdemo;import android.content.AsyncTaskLoader; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.net.Uri;/*** Created by kohoh on 14-11-3.*/ public class DatabaseLoader extends AsyncTaskLoader<Cursor> {final ForceLoadContentObserver mObserver;Uri mNotificationUri;String mTable;String[] mColumns;String mSelection;String[] mSelectionArgs;String mGroupBy;String mHaving;String mOrderBy;SQLiteOpenHelper mSqLiteOpenHelper;Cursor mCursor;public DatabaseLoader(Context context) {super(context);this.mObserver = new ForceLoadContentObserver();}public DatabaseLoader(Context context, SQLiteOpenHelper sqLiteOpenHelper, Uri mNotificationUri,String mTable, String[] mColumns, String mSelection, String[] mSelectionArgs,String mGroupBy, String mHaving, String mOrderBy) {super(context);this.mNotificationUri = mNotificationUri;this.mTable = mTable;this.mColumns = mColumns;this.mSelection = mSelection;this.mSelectionArgs = mSelectionArgs;this.mGroupBy = mGroupBy;this.mHaving = mHaving;this.mOrderBy = mOrderBy;this.mSqLiteOpenHelper = sqLiteOpenHelper;this.mObserver = new ForceLoadContentObserver();}@Overridepublic Cursor loadInBackground() {SQLiteDatabase database = mSqLiteOpenHelper.getReadableDatabase();Cursor cursor = database.query(mTable, mColumns, mSelection, mSelectionArgs, mGroupBy,mHaving, mOrderBy);if (cursor != null) {cursor.getCount();cursor.registerContentObserver(mObserver);cursor.setNotificationUri(getContext().getContentResolver(), mNotificationUri);}return cursor;}@Overridepublic void deliverResult(Cursor cursor) {if (isReset()) {if (cursor != null) {cursor.close();}return;}Cursor oldCursor = mCursor;mCursor = cursor;if (isStarted()) {super.deliverResult(cursor);}if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {oldCursor.close();}}@Overrideprotected void onStartLoading() {if (mCursor != null) {deliverResult(mCursor);}if (takeContentChanged() || mCursor == null) {forceLoad();}}@Overrideprotected void onStopLoading() {// Attempt to cancel the current load task if possible.cancelLoad();}@Overridepublic void onCanceled(Cursor cursor) {if (cursor != null && !cursor.isClosed()) {cursor.close();}}@Overrideprotected void onReset() {super.onReset();onStopLoading();if (mCursor != null && !mCursor.isClosed()) {mCursor.close();}mCursor = null;}public SQLiteOpenHelper getSqLiteOpenHelper() {return mSqLiteOpenHelper;}public void setSqLiteOpenHelper(SQLiteOpenHelper mSqLiteOpenHelper) {this.mSqLiteOpenHelper = mSqLiteOpenHelper;}public Uri getNotificationUri() {return mNotificationUri;}public void setNotificationUri(Uri mNotificationUri) {this.mNotificationUri = mNotificationUri;}public String getTable() {return mTable;}public void setTable(String mTable) {this.mTable = mTable;}public String[] getColumns() {return mColumns;}public void setColumns(String[] mColumns) {this.mColumns = mColumns;}public String getSelection() {return mSelection;}public void setSelection(String mSelection) {this.mSelection = mSelection;}public String[] getSelectionArgs() {return mSelectionArgs;}public void setSelectionArgs(String[] mSelectionArgs) {this.mSelectionArgs = mSelectionArgs;}public String getGroupBy() {return mGroupBy;}public void setGroupBy(String mGroupBy) {this.mGroupBy = mGroupBy;}public String getHaving() {return mHaving;}public void setHaving(String mHaving) {this.mHaving = mHaving;}public String getOrderBy() {return mOrderBy;}public void setOrderBy(String mOrderBy) {this.mOrderBy = mOrderBy;} }

use provider

package com.kohoh.cursorsyncdemo;import android.app.Activity; import android.app.LoaderManager; import android.content.CursorLoader; import android.content.Loader; import android.database.Cursor; import android.os.Bundle; import android.view.ContextMenu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.CursorAdapter; import android.widget.ListView; import android.widget.SimpleCursorAdapter;public class CursorSyncDemo extends Activity implements LoaderManager.LoaderCallbacks<Cursor> {private ListView listView;private SimpleCursorAdapter adapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_cursor_sync_demo);String[] from = {ContactContract.NAME, ContactContract.PHONE};int[] to = {R.id.name, R.id.phone};listView = (ListView) findViewById(R.id.lv);adapter = new SimpleCursorAdapter(this, R.layout.contact_item, null, from, to,CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);listView.setAdapter(adapter);getLoaderManager().initLoader(0, null, this);registerForContextMenu(listView);}@Overridepublic boolean onContextItemSelected(MenuItem item) {switch (item.getItemId()) {case R.id.delete:AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();ContactContract.ContactDatabaseHelper.deleteContact(this, menuInfo.id);return true;default:return super.onContextItemSelected(item);}}@Overridepublic void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {MenuInflater inflater = getMenuInflater();inflater.inflate(R.menu.contact_item_menu, menu);}@Overridepublic Loader<Cursor> onCreateLoader(int id, Bundle args) {String[] projection = {ContactContract._ID, ContactContract.NAME, ContactContract.PHONE};return new CursorLoader(this, ContactContract.CONTACT_URI, projection, null, null, null);}@Overridepublic void onLoadFinished(Loader<Cursor> loader, Cursor data) {adapter.changeCursor(data);}@Overridepublic void onLoaderReset(Loader<Cursor> loader) {} }

use database

package com.kohoh.cursorsyncdemo;import android.app.Activity; import android.app.LoaderManager; import android.content.Loader; import android.database.Cursor; import android.os.Bundle; import android.view.ContextMenu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.CursorAdapter; import android.widget.ListView; import android.widget.SimpleCursorAdapter;public class CursorSyncDemo extends Activity implements LoaderManager.LoaderCallbacks<Cursor> {private ListView listView;private SimpleCursorAdapter adapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_cursor_sync_demo);String[] from = {ContactContract.NAME, ContactContract.PHONE};int[] to = {R.id.name, R.id.phone};listView = (ListView) findViewById(R.id.lv);adapter = new SimpleCursorAdapter(this, R.layout.contact_item, null, from, to,CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);listView.setAdapter(adapter);getLoaderManager().initLoader(0, null, this);registerForContextMenu(listView);}@Overridepublic Loader<Cursor> onCreateLoader(int id, Bundle args) {String[] columns = {ContactContract._ID, ContactContract.NAME, ContactContract.PHONE};return new DatabaseLoader(this,ContactContract.getSqliteOpenHelper(this),ContactContract.SYNC_SIGNAL_URI,ContactContract.CONTACT_TABLE,columns,null,null,null,null,null);}@Overridepublic void onLoadFinished(Loader<Cursor> loader, Cursor data) {adapter.changeCursor(data);}@Overridepublic void onLoaderReset(Loader<Cursor> loader) {}@Overridepublic boolean onContextItemSelected(MenuItem item) {switch (item.getItemId()) {case R.id.delete:AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();ContactContract.ContactDatabaseHelper.deleteContact(this, menuInfo.id);return true;default:return super.onContextItemSelected(item);}}@Overridepublic void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {MenuInflater inflater = getMenuInflater();inflater.inflate(R.menu.contact_item_menu, menu);} }

use adapter

package com.kohoh.cursorsyncdemo;import android.app.Activity; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.os.Bundle; import android.view.ContextMenu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.CursorAdapter; import android.widget.ListView; import android.widget.SimpleCursorAdapter;public class CursorSyncDemo extends Activity {private ListView listView;private SimpleCursorAdapter adapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_cursor_sync_demo);String[] from = {ContactContract.NAME, ContactContract.PHONE};int[] to = {R.id.name, R.id.phone};listView = (ListView) findViewById(R.id.lv);adapter = new SimpleCursorAdapter(this, R.layout.contact_item, null, from, to,CursorAdapter.FLAG_AUTO_REQUERY);listView.setAdapter(adapter);loadData();registerForContextMenu(listView);}private void loadData() {SQLiteOpenHelper sqliteOpenHelper = ContactContract.getSqliteOpenHelper(this);SQLiteDatabase database = sqliteOpenHelper.getReadableDatabase();String[] columns = {ContactContract._ID, ContactContract.NAME, ContactContract.PHONE};Cursor cursor = database.query(ContactContract.CONTACT_TABLE, columns, null, null, null,null, null);cursor.setNotificationUri(this.getContentResolver(), ContactContract.SYNC_SIGNAL_URI);adapter.changeCursor(cursor);}@Overridepublic boolean onContextItemSelected(MenuItem item) {switch (item.getItemId()) {case R.id.delete:AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();ContactContract.ContactDatabaseHelper.deleteContact(this, menuInfo.id);return true;default:return super.onContextItemSelected(item);}}@Overridepublic void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {MenuInflater inflater = getMenuInflater();inflater.inflate(R.menu.contact_item_menu, menu);} }

總結(jié)

以上是生活随笔為你收集整理的RecyclerView.Adapter:全能notify解决方案的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。