RecyclerView使用:深入 CursorAdapter

问题

在使用listview的时候,如果数据源来自数据库,我们可能会使用到cursorAdapter
listiew.setAdapter(cursorAdapter) 是常见的用法
但是,当我们使用RecyclerView的时候,Adapter为cursorAdapter是会报错的。
到目前为止,RecyclerView是不支持cursorAdapter的,但在github上有网友通过改写,已经实现了支持




1. 借鉴





2. 使用

1. 添加下面加入类中的RecyclerViewCursorAdapter 和 CursorFilter到工程中 (第5步)
2. 新建自定义Adapter   (第3步)
3. RecyclerView.setAdapter  (第4步)



3. 新建自定义Adapter

继承RecyclerViewCursorAdapter
注意:下面代码进行了Listener的优化
[java]  view plain  copy
  1. public class ContactsAdapter extends RecyclerViewCursorAdapter<ContactsAdapter.MyViewHolder> {  
  2.   
  3.   
  4.   
  5.     private static final String TAG = "ContactsAdapter";  
  6.     public ContactsAdapter(Context context, Cursor c, int flags) {  
  7.         super(context, c, flags);  
  8.     }  
  9.   
  10.   
  11.   
  12.   
  13.     // new view   
  14.     @Override  
  15.     public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {  
  16.         View view = LayoutInflater.from(parent.getContext())  
  17.                 .inflate(R.layout.list_item, parent, false);  
  18.   
  19.         MyViewHolder myViewHolder = new MyViewHolder(view);  
  20.         ClickListener clickListener = new ClickListener();  
  21.         myViewHolder.linearLayout.setOnClickListener(clickListener);  
  22.         view.setTag(myViewHolder.linearLayout.getId(),clickListener);  
  23.         return myViewHolder ;  
  24.     }  
  25.   
  26.     // bind view   
  27.     @Override  
  28.     public void onBindViewHolder(final MyViewHolder holder, Cursor cursor) {  
  29.         String name = cursor.getString(cursor.getColumnIndex(  
  30.                 ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));  
  31.         String number =cursor.getString(cursor.getColumnIndex(  
  32.                 ContactsContract.CommonDataKinds.Phone.NUMBER));  
  33.         long contactsId = Long.parseLong(cursor.getString(cursor.getColumnIndex(  
  34.                 ContactsContract.CommonDataKinds.Phone.RAW_CONTACT_ID)));  
  35.   
  36.         holder.contactName.setText(name);  
  37.         ClickListener clickListener = (ClickListener) holder.itemView.getTag(  
  38.                 holder.linearLayout.getId());  
  39.         clickListener.setContactsId(contactsId);  
  40.     }  
  41.   
  42.     @Override  
  43.     protected void onContentChanged() {  
  44.   
  45.     }  
  46.   
  47.   
  48.   
  49.     public static class MyViewHolder extends RecyclerView.ViewHolder{  
  50.         private TextView contactName;  
  51.         private LinearLayout linearLayout;  
  52.         public MyViewHolder(View itemView) {  
  53.             super(itemView);  
  54.             contactName = (TextView) itemView.findViewById(R.id.contactNameText);  
  55.             linearLayout  = (LinearLayout)itemView.findViewById(R.id.linearlayout);  
  56.         }  
  57.     }  
  58.   
  59.   
  60.     private class ClickListener implements View.OnClickListener {  
  61.   
  62.         private long contactsId;  
  63.   
  64.         public void setContactsId(long contactsId) {  
  65.             this.contactsId = contactsId;  
  66.         }  
  67.   
  68.         @Override  
  69.         public void onClick(View v) {  
  70.  Log.i(TAG,"contactsId:"+contactsId); }  
  71.     }  
  72. }  






4. Activty中使用

mRecyclerView为RecyclerView实例
cusror为数据返回的cursor

[java]  view plain  copy
  1. mRecyclerView.setAdapter(new ContactsAdapter(getActivity(), cursor,0));  








5. 加入类

主要是引入2个类:RecyclerViewCursorAdapter 和 CursorFilter
RecyclerViewCursorAdapter:
[java]  view plain  copy
  1. /* 
  2.  * Copyright (C) 2013 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. /* 
  18.  * Copyright (C) 2014 flzyup@ligux.com 
  19.  * 
  20.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  21.  * you may not use this file except in compliance with the License. 
  22.  * You may obtain a copy of the License at 
  23.  * 
  24.  *      http://www.apache.org/licenses/LICENSE-2.0 
  25.  * 
  26.  * Unless required by applicable law or agreed to in writing, software 
  27.  * distributed under the License is distributed on an "AS IS" BASIS, 
  28.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  29.  * See the License for the specific language governing permissions and 
  30.  * limitations under the License. 
  31.  * 
  32.  */  
  33.   
  34.   
  35. import android.content.Context;  
  36. import android.database.ContentObserver;  
  37. import android.database.Cursor;  
  38. import android.database.DataSetObserver;  
  39. import android.os.Handler;  
  40. import android.support.v7.widget.RecyclerView;  
  41. import android.widget.Filter;  
  42. import android.widget.FilterQueryProvider;  
  43. import android.widget.Filterable;  
  44.   
  45. /** 
  46.  * Version 1.0 
  47.  * 
  48.  * Date: 2014-07-07 19:53 
  49.  * Author: flzyup@ligux.com 
  50.  * 
  51.  * Copyright © 2009-2014 LiGux.com. 
  52.  * 
  53.  */  
  54. public abstract class RecyclerViewCursorAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH>  implements Filterable,  
  55.         CursorFilter.CursorFilterClient {  
  56.   
  57.     /** 
  58.      * Call when bind view with the cursor 
  59.      * @param holder 
  60.      * @param cursor 
  61.      */  
  62.     public abstract void onBindViewHolder(VH holder, Cursor cursor);  
  63.   
  64.     /** 
  65.      * This field should be made private, so it is hidden from the SDK. 
  66.      * {@hide} 
  67.      */  
  68.     protected boolean mDataValid;  
  69.   
  70.     /** 
  71.      * The current cursor 
  72.      */  
  73.     protected Cursor mCursor;  
  74.   
  75.     /** 
  76.      * This field should be made private, so it is hidden from the SDK. 
  77.      * {@hide} 
  78.      */  
  79.     protected Context mContext;  
  80.   
  81.     /** 
  82.      * The row id column 
  83.      */  
  84.     protected int mRowIDColumn;  
  85.   
  86.     /** 
  87.      * This field should be made private, so it is hidden from the SDK. 
  88.      * {@hide} 
  89.      */  
  90.     protected ChangeObserver mChangeObserver;  
  91.     /** 
  92.      * This field should be made private, so it is hidden from the SDK. 
  93.      * {@hide} 
  94.      */  
  95.     protected DataSetObserver mDataSetObserver;  
  96.   
  97.     /** 
  98.      * This field should be made private, so it is hidden from the SDK. 
  99.      * {@hide} 
  100.      */  
  101.     protected CursorFilter mCursorFilter;  
  102.   
  103.     /** 
  104.      * This field should be made private, so it is hidden from the SDK. 
  105.      * {@hide} 
  106.      */  
  107.     protected FilterQueryProvider mFilterQueryProvider;  
  108.   
  109.     /** 
  110.      * If set the adapter will register a content observer on the cursor and will call 
  111.      * {@link #onContentChanged()} when a notification comes in.  Be careful when 
  112.      * using this flag: you will need to unset the current Cursor from the adapter 
  113.      * to avoid leaks due to its registered observers.  This flag is not needed 
  114.      * when using a CursorAdapter with a 
  115.      * {@link android.content.CursorLoader}. 
  116.      */  
  117.     public static final int FLAG_REGISTER_CONTENT_OBSERVER = 0x02;  
  118.   
  119.     /** 
  120.      * Recommended constructor. 
  121.      * 
  122.      * @param c The cursor from which to get the data. 
  123.      * @param context The context 
  124.      * @param flags Flags used to determine the behavior of the adapter; 
  125.      *              Currently it accept {@link #FLAG_REGISTER_CONTENT_OBSERVER}. 
  126.      */  
  127.     public RecyclerViewCursorAdapter(Context context, Cursor c, int flags) {  
  128.         init(context, c, flags);  
  129.     }  
  130.   
  131.     void init(Context context, Cursor c, int flags) {  
  132.   
  133.         boolean cursorPresent = c != null;  
  134.         mCursor = c;  
  135.         mDataValid = cursorPresent;  
  136.         mContext = context;  
  137.         mRowIDColumn = cursorPresent ? c.getColumnIndexOrThrow("_id") : -1;  
  138.         if ((flags & FLAG_REGISTER_CONTENT_OBSERVER) == FLAG_REGISTER_CONTENT_OBSERVER) {  
  139.             mChangeObserver = new ChangeObserver();  
  140.             mDataSetObserver = new MyDataSetObserver();  
  141.         } else {  
  142.             mChangeObserver = null;  
  143.             mDataSetObserver = null;  
  144.         }  
  145.   
  146.         if (cursorPresent) {  
  147.             if (mChangeObserver != null) c.registerContentObserver(mChangeObserver);  
  148.             if (mDataSetObserver != null) c.registerDataSetObserver(mDataSetObserver);  
  149.         }  
  150.         setHasStableIds(true);  
  151.     }  
  152.   
  153.     /** 
  154.      * Returns the cursor. 
  155.      * @return the cursor. 
  156.      */  
  157.     @Override  
  158.     public Cursor getCursor() {  
  159.         return mCursor;  
  160.     }  
  161.   
  162.     /** 
  163.      * @see android.support.v7.widget.RecyclerView.Adapter#getItemCount() 
  164.      */  
  165.     @Override  
  166.     public int getItemCount() {  
  167.         if (mDataValid && mCursor != null) {  
  168.             return mCursor.getCount();  
  169.         } else {  
  170.             return 0;  
  171.         }  
  172.     }  
  173.   
  174.     /** 
  175.      * @see android.support.v7.widget.RecyclerView.Adapter#getItemId(int) 
  176.      * 
  177.      * @param position Adapter position to query 
  178.      * @return 
  179.      */  
  180.     @Override  
  181.     public long getItemId(int position) {  
  182.         if (mDataValid && mCursor != null) {  
  183.             if (mCursor.moveToPosition(position)) {  
  184.                 return mCursor.getLong(mRowIDColumn);  
  185.             } else {  
  186.                 return 0;  
  187.             }  
  188.         } else {  
  189.             return 0;  
  190.         }  
  191.     }  
  192.   
  193.     @Override  
  194.     public void onBindViewHolder(VH holder, int position) {  
  195.         if (!mDataValid) {  
  196.             throw new IllegalStateException("this should only be called when the cursor is valid");  
  197.         }  
  198.         if (!mCursor.moveToPosition(position)) {  
  199.             throw new IllegalStateException("couldn't move cursor to position " + position);  
  200.         }  
  201.         onBindViewHolder(holder, mCursor);  
  202.     }  
  203.   
  204.     /** 
  205.      * Change the underlying cursor to a new cursor. If there is an existing cursor it will be 
  206.      * closed. 
  207.      * 
  208.      * @param cursor The new cursor to be used 
  209.      */  
  210.     public void changeCursor(Cursor cursor) {  
  211.         Cursor old = swapCursor(cursor);  
  212.         if (old != null) {  
  213.             old.close();  
  214.         }  
  215.     }  
  216.   
  217.     /** 
  218.      * Swap in a new Cursor, returning the old Cursor.  Unlike 
  219.      * {@link #changeCursor(Cursor)}, the returned old Cursor is <em>not</em> 
  220.      * closed. 
  221.      * 
  222.      * @param newCursor The new cursor to be used. 
  223.      * @return Returns the previously set Cursor, or null if there wasa not one. 
  224.      * If the given new Cursor is the same instance is the previously set 
  225.      * Cursor, null is also returned. 
  226.      */  
  227.     public Cursor swapCursor(Cursor newCursor) {  
  228.         if (newCursor == mCursor) {  
  229.             return null;  
  230.         }  
  231.         Cursor oldCursor = mCursor;  
  232.         if (oldCursor != null) {  
  233.             if (mChangeObserver != null) oldCursor.unregisterContentObserver(mChangeObserver);  
  234.             if (mDataSetObserver != null) oldCursor.unregisterDataSetObserver(mDataSetObserver);  
  235.         }  
  236.         mCursor = newCursor;  
  237.         if (newCursor != null) {  
  238.             if (mChangeObserver != null) newCursor.registerContentObserver(mChangeObserver);  
  239.             if (mDataSetObserver != null) newCursor.registerDataSetObserver(mDataSetObserver);  
  240.             mRowIDColumn = newCursor.getColumnIndexOrThrow("_id");  
  241.             mDataValid = true;  
  242.             // notify the observers about the new cursor  
  243.             notifyDataSetChanged();  
  244.         } else {  
  245.             mRowIDColumn = -1;  
  246.             mDataValid = false;  
  247.             // notify the observers about the lack of a data set  
  248.             notifyDataSetChanged();  
  249. //            notifyDataSetInvalidated();  
  250.         }  
  251.         return oldCursor;  
  252.     }  
  253.   
  254.     /** 
  255.      * <p>Converts the cursor into a CharSequence. Subclasses should override this 
  256.      * method to convert their results. The default implementation returns an 
  257.      * empty String for null values or the default String representation of 
  258.      * the value.</p> 
  259.      * 
  260.      * @param cursor the cursor to convert to a CharSequence 
  261.      * @return a CharSequence representing the value 
  262.      */  
  263.     public CharSequence convertToString(Cursor cursor) {  
  264.         return cursor == null ? "" : cursor.toString();  
  265.     }  
  266.   
  267.     /** 
  268.      * Runs a query with the specified constraint. This query is requested 
  269.      * by the filter attached to this adapter. 
  270.      * 
  271.      * The query is provided by a 
  272.      * {@link android.widget.FilterQueryProvider}. 
  273.      * If no provider is specified, the current cursor is not filtered and returned. 
  274.      * 
  275.      * After this method returns the resulting cursor is passed to {@link #changeCursor(Cursor)} 
  276.      * and the previous cursor is closed. 
  277.      * 
  278.      * This method is always executed on a background thread, not on the 
  279.      * application's main thread (or UI thread.) 
  280.      * 
  281.      * Contract: when constraint is null or empty, the original results, 
  282.      * prior to any filtering, must be returned. 
  283.      * 
  284.      * @param constraint the constraint with which the query must be filtered 
  285.      * 
  286.      * @return a Cursor representing the results of the new query 
  287.      * 
  288.      * @see #getFilter() 
  289.      * @see #getFilterQueryProvider() 
  290.      * @see #setFilterQueryProvider(android.widget.FilterQueryProvider) 
  291.      */  
  292.     public Cursor runQueryOnBackgroundThread(CharSequence constraint) {  
  293.         if (mFilterQueryProvider != null) {  
  294.             return mFilterQueryProvider.runQuery(constraint);  
  295.         }  
  296.   
  297.         return mCursor;  
  298.     }  
  299.   
  300.     public Filter getFilter() {  
  301.         if (mCursorFilter == null) {  
  302.             mCursorFilter = new CursorFilter(this);  
  303.         }  
  304.         return mCursorFilter;  
  305.     }  
  306.   
  307.     /** 
  308.      * Returns the query filter provider used for filtering. When the 
  309.      * provider is null, no filtering occurs. 
  310.      * 
  311.      * @return the current filter query provider or null if it does not exist 
  312.      * 
  313.      * @see #setFilterQueryProvider(android.widget.FilterQueryProvider) 
  314.      * @see #runQueryOnBackgroundThread(CharSequence) 
  315.      */  
  316.     public FilterQueryProvider getFilterQueryProvider() {  
  317.         return mFilterQueryProvider;  
  318.     }  
  319.   
  320.     /** 
  321.      * Sets the query filter provider used to filter the current Cursor. 
  322.      * The provider's 
  323.      * {@link android.widget.FilterQueryProvider#runQuery(CharSequence)} 
  324.      * method is invoked when filtering is requested by a client of 
  325.      * this adapter. 
  326.      * 
  327.      * @param filterQueryProvider the filter query provider or null to remove it 
  328.      * 
  329.      * @see #getFilterQueryProvider() 
  330.      * @see #runQueryOnBackgroundThread(CharSequence) 
  331.      */  
  332.     public void setFilterQueryProvider(FilterQueryProvider filterQueryProvider) {  
  333.         mFilterQueryProvider = filterQueryProvider;  
  334.     }  
  335.   
  336.     /** 
  337.      * Called when the {@link ContentObserver} on the cursor receives a change notification. 
  338.      * The default implementation provides the auto-requery logic, but may be overridden by 
  339.      * sub classes. 
  340.      * 
  341.      * @see ContentObserver#onChange(boolean) 
  342.      */  
  343.     protected abstract void onContentChanged();  
  344.   
  345.     private class ChangeObserver extends ContentObserver {  
  346.         public ChangeObserver() {  
  347.             super(new Handler());  
  348.         }  
  349.   
  350.         @Override  
  351.         public boolean deliverSelfNotifications() {  
  352.             return true;  
  353.         }  
  354.   
  355.         @Override  
  356.         public void onChange(boolean selfChange) {  
  357.             onContentChanged();  
  358.         }  
  359.     }  
  360.   
  361.     private class MyDataSetObserver extends DataSetObserver {  
  362.         @Override  
  363.         public void onChanged() {  
  364.             mDataValid = true;  
  365.             notifyDataSetChanged();  
  366.         }  
  367.   
  368.         @Override  
  369.         public void onInvalidated() {  
  370.             mDataValid = false;  
  371.             notifyDataSetChanged();  
  372. //            notifyDataSetInvalidated();  
  373.         }  
  374.     }  
  375. }  


CursorFilter:
[java]  view plain  copy
  1. /* 
  2.  * Copyright (C) 2011 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. import android.widget.Filter;  
  18. import android.database.Cursor;  
  19.   
  20.   
  21. /** 
  22.  * The CursorFilter delegates most of the work to the 
  23.  * {@link android.widget.CursorAdapter}. Subclasses should override these 
  24.  * delegate methods to run the queries and convert the results into String 
  25.  * that can be used by auto-completion widgets. 
  26.  */  
  27. class CursorFilter extends Filter {  
  28.   
  29.     CursorFilterClient mClient;  
  30.   
  31.     interface CursorFilterClient {  
  32.         CharSequence convertToString(Cursor cursor);  
  33.         Cursor runQueryOnBackgroundThread(CharSequence constraint);  
  34.         Cursor getCursor();  
  35.         void changeCursor(Cursor cursor);  
  36.     }  
  37.   
  38.     CursorFilter(CursorFilterClient client) {  
  39.         mClient = client;  
  40.     }  
  41.   
  42.     @Override  
  43.     public CharSequence convertResultToString(Object resultValue) {  
  44.         return mClient.convertToString((Cursor) resultValue);  
  45.     }  
  46.   
  47.     @Override  
  48.     protected Filter.FilterResults performFiltering(CharSequence constraint) {  
  49.         Cursor cursor = mClient.runQueryOnBackgroundThread(constraint);  
  50.   
  51.         FilterResults results = new FilterResults();  
  52.         if (cursor != null) {  
  53.             results.count = cursor.getCount();  
  54.             results.values = cursor;  
  55.         } else {  
  56.             results.count = 0;  
  57.             results.values = null;  
  58.         }  
  59.         return results;  
  60.     }  
  61.   
  62.     @Override  
  63.     protected void publishResults(CharSequence constraint, FilterResults results) {  
  64.         Cursor oldCursor = mClient.getCursor();  
  65.   
  66.         if (results.values != null && results.values != oldCursor) {  
  67.             mClient.changeCursor((Cursor) results.values);  
  68.         }  
  69.     }  
  70. }  
  71. 转载地址:http://blog.csdn.net/qqgrid/article/details/42144029
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值