问题
在使用listview的时候,如果数据源来自数据库,我们可能会使用到cursorAdapter
listiew.setAdapter(cursorAdapter) 是常见的用法
但是,当我们使用RecyclerView的时候,Adapter为cursorAdapter是会报错的。
到目前为止,RecyclerView是不支持cursorAdapter的,但在github上有网友通过改写,已经实现了支持
1. 借鉴
2. 新建自定义Adapter (第3步)
3. RecyclerView.setAdapter (第4步)
3. 新建自定义Adapter
继承RecyclerViewCursorAdapter
注意:下面代码进行了Listener的优化
- public class ContactsAdapter extends RecyclerViewCursorAdapter<ContactsAdapter.MyViewHolder> {
- private static final String TAG = "ContactsAdapter";
- public ContactsAdapter(Context context, Cursor c, int flags) {
- super(context, c, flags);
- }
- // new view
- @Override
- public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- View view = LayoutInflater.from(parent.getContext())
- .inflate(R.layout.list_item, parent, false);
- MyViewHolder myViewHolder = new MyViewHolder(view);
- ClickListener clickListener = new ClickListener();
- myViewHolder.linearLayout.setOnClickListener(clickListener);
- view.setTag(myViewHolder.linearLayout.getId(),clickListener);
- return myViewHolder ;
- }
- // bind view
- @Override
- public void onBindViewHolder(final MyViewHolder holder, Cursor cursor) {
- String name = cursor.getString(cursor.getColumnIndex(
- ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
- String number =cursor.getString(cursor.getColumnIndex(
- ContactsContract.CommonDataKinds.Phone.NUMBER));
- long contactsId = Long.parseLong(cursor.getString(cursor.getColumnIndex(
- ContactsContract.CommonDataKinds.Phone.RAW_CONTACT_ID)));
- holder.contactName.setText(name);
- ClickListener clickListener = (ClickListener) holder.itemView.getTag(
- holder.linearLayout.getId());
- clickListener.setContactsId(contactsId);
- }
- @Override
- protected void onContentChanged() {
- }
- public static class MyViewHolder extends RecyclerView.ViewHolder{
- private TextView contactName;
- private LinearLayout linearLayout;
- public MyViewHolder(View itemView) {
- super(itemView);
- contactName = (TextView) itemView.findViewById(R.id.contactNameText);
- linearLayout = (LinearLayout)itemView.findViewById(R.id.linearlayout);
- }
- }
- private class ClickListener implements View.OnClickListener {
- private long contactsId;
- public void setContactsId(long contactsId) {
- this.contactsId = contactsId;
- }
- @Override
- public void onClick(View v) {
- Log.i(TAG,"contactsId:"+contactsId); }
- }
- }
4. Activty中使用
mRecyclerView为RecyclerView实例
cusror为数据返回的cursor
5. 加入类
主要是引入2个类:RecyclerViewCursorAdapter 和 CursorFilter
RecyclerViewCursorAdapter:
- /*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- /*
- * Copyright (C) 2014 flzyup@ligux.com
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
- import android.content.Context;
- import android.database.ContentObserver;
- import android.database.Cursor;
- import android.database.DataSetObserver;
- import android.os.Handler;
- import android.support.v7.widget.RecyclerView;
- import android.widget.Filter;
- import android.widget.FilterQueryProvider;
- import android.widget.Filterable;
- /**
- * Version 1.0
- *
- * Date: 2014-07-07 19:53
- * Author: flzyup@ligux.com
- *
- * Copyright © 2009-2014 LiGux.com.
- *
- */
- public abstract class RecyclerViewCursorAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> implements Filterable,
- CursorFilter.CursorFilterClient {
- /**
- * Call when bind view with the cursor
- * @param holder
- * @param cursor
- */
- public abstract void onBindViewHolder(VH holder, Cursor cursor);
- /**
- * This field should be made private, so it is hidden from the SDK.
- * {@hide}
- */
- protected boolean mDataValid;
- /**
- * The current cursor
- */
- protected Cursor mCursor;
- /**
- * This field should be made private, so it is hidden from the SDK.
- * {@hide}
- */
- protected Context mContext;
- /**
- * The row id column
- */
- protected int mRowIDColumn;
- /**
- * This field should be made private, so it is hidden from the SDK.
- * {@hide}
- */
- protected ChangeObserver mChangeObserver;
- /**
- * This field should be made private, so it is hidden from the SDK.
- * {@hide}
- */
- protected DataSetObserver mDataSetObserver;
- /**
- * This field should be made private, so it is hidden from the SDK.
- * {@hide}
- */
- protected CursorFilter mCursorFilter;
- /**
- * This field should be made private, so it is hidden from the SDK.
- * {@hide}
- */
- protected FilterQueryProvider mFilterQueryProvider;
- /**
- * If set the adapter will register a content observer on the cursor and will call
- * {@link #onContentChanged()} when a notification comes in. Be careful when
- * using this flag: you will need to unset the current Cursor from the adapter
- * to avoid leaks due to its registered observers. This flag is not needed
- * when using a CursorAdapter with a
- * {@link android.content.CursorLoader}.
- */
- public static final int FLAG_REGISTER_CONTENT_OBSERVER = 0x02;
- /**
- * Recommended constructor.
- *
- * @param c The cursor from which to get the data.
- * @param context The context
- * @param flags Flags used to determine the behavior of the adapter;
- * Currently it accept {@link #FLAG_REGISTER_CONTENT_OBSERVER}.
- */
- public RecyclerViewCursorAdapter(Context context, Cursor c, int flags) {
- init(context, c, flags);
- }
- void init(Context context, Cursor c, int flags) {
- 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);
- }
- setHasStableIds(true);
- }
- /**
- * Returns the cursor.
- * @return the cursor.
- */
- @Override
- public Cursor getCursor() {
- return mCursor;
- }
- /**
- * @see android.support.v7.widget.RecyclerView.Adapter#getItemCount()
- */
- @Override
- public int getItemCount() {
- if (mDataValid && mCursor != null) {
- return mCursor.getCount();
- } else {
- return 0;
- }
- }
- /**
- * @see android.support.v7.widget.RecyclerView.Adapter#getItemId(int)
- *
- * @param position Adapter position to query
- * @return
- */
- @Override
- public long getItemId(int position) {
- if (mDataValid && mCursor != null) {
- if (mCursor.moveToPosition(position)) {
- return mCursor.getLong(mRowIDColumn);
- } else {
- return 0;
- }
- } else {
- return 0;
- }
- }
- @Override
- public void onBindViewHolder(VH holder, int position) {
- if (!mDataValid) {
- throw new IllegalStateException("this should only be called when the cursor is valid");
- }
- if (!mCursor.moveToPosition(position)) {
- throw new IllegalStateException("couldn't move cursor to position " + position);
- }
- onBindViewHolder(holder, mCursor);
- }
- /**
- * 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 old = swapCursor(cursor);
- if (old != null) {
- old.close();
- }
- }
- /**
- * Swap in a new Cursor, returning the old Cursor. Unlike
- * {@link #changeCursor(Cursor)}, the returned old Cursor is <em>not</em>
- * closed.
- *
- * @param newCursor The new cursor to be used.
- * @return Returns the previously set Cursor, or null if there wasa not one.
- * If the given new Cursor is the same instance is the previously set
- * Cursor, null is also returned.
- */
- 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
- notifyDataSetChanged();
- // notifyDataSetInvalidated();
- }
- return oldCursor;
- }
- /**
- * <p>Converts the cursor into a CharSequence. Subclasses should override this
- * method to convert their results. The default implementation returns an
- * empty String for null values or the default String representation of
- * the value.</p>
- *
- * @param cursor the cursor to convert to a CharSequence
- * @return a CharSequence representing the value
- */
- public CharSequence convertToString(Cursor cursor) {
- return cursor == null ? "" : cursor.toString();
- }
- /**
- * Runs a query with the specified constraint. This query is requested
- * by the filter attached to this adapter.
- *
- * The query is provided by a
- * {@link android.widget.FilterQueryProvider}.
- * If no provider is specified, the current cursor is not filtered and returned.
- *
- * After this method returns the resulting cursor is passed to {@link #changeCursor(Cursor)}
- * and the previous cursor is closed.
- *
- * This method is always executed on a background thread, not on the
- * application's main thread (or UI thread.)
- *
- * Contract: when constraint is null or empty, the original results,
- * prior to any filtering, must be returned.
- *
- * @param constraint the constraint with which the query must be filtered
- *
- * @return a Cursor representing the results of the new query
- *
- * @see #getFilter()
- * @see #getFilterQueryProvider()
- * @see #setFilterQueryProvider(android.widget.FilterQueryProvider)
- */
- public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
- if (mFilterQueryProvider != null) {
- return mFilterQueryProvider.runQuery(constraint);
- }
- return mCursor;
- }
- public Filter getFilter() {
- if (mCursorFilter == null) {
- mCursorFilter = new CursorFilter(this);
- }
- return mCursorFilter;
- }
- /**
- * Returns the query filter provider used for filtering. When the
- * provider is null, no filtering occurs.
- *
- * @return the current filter query provider or null if it does not exist
- *
- * @see #setFilterQueryProvider(android.widget.FilterQueryProvider)
- * @see #runQueryOnBackgroundThread(CharSequence)
- */
- public FilterQueryProvider getFilterQueryProvider() {
- return mFilterQueryProvider;
- }
- /**
- * Sets the query filter provider used to filter the current Cursor.
- * The provider's
- * {@link android.widget.FilterQueryProvider#runQuery(CharSequence)}
- * method is invoked when filtering is requested by a client of
- * this adapter.
- *
- * @param filterQueryProvider the filter query provider or null to remove it
- *
- * @see #getFilterQueryProvider()
- * @see #runQueryOnBackgroundThread(CharSequence)
- */
- public void setFilterQueryProvider(FilterQueryProvider filterQueryProvider) {
- mFilterQueryProvider = filterQueryProvider;
- }
- /**
- * Called when the {@link ContentObserver} on the cursor receives a change notification.
- * The default implementation provides the auto-requery logic, but may be overridden by
- * sub classes.
- *
- * @see ContentObserver#onChange(boolean)
- */
- protected abstract void onContentChanged();
- 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;
- notifyDataSetChanged();
- // notifyDataSetInvalidated();
- }
- }
- }
CursorFilter:
- /*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- import android.widget.Filter;
- import android.database.Cursor;
- /**
- * The CursorFilter delegates most of the work to the
- * {@link android.widget.CursorAdapter}. Subclasses should override these
- * delegate methods to run the queries and convert the results into String
- * that can be used by auto-completion widgets.
- */
- class CursorFilter extends Filter {
- CursorFilterClient mClient;
- interface CursorFilterClient {
- CharSequence convertToString(Cursor cursor);
- Cursor runQueryOnBackgroundThread(CharSequence constraint);
- Cursor getCursor();
- void changeCursor(Cursor cursor);
- }
- CursorFilter(CursorFilterClient client) {
- mClient = client;
- }
- @Override
- public CharSequence convertResultToString(Object resultValue) {
- return mClient.convertToString((Cursor) resultValue);
- }
- @Override
- protected Filter.FilterResults performFiltering(CharSequence constraint) {
- Cursor cursor = mClient.runQueryOnBackgroundThread(constraint);
- FilterResults results = new FilterResults();
- if (cursor != null) {
- results.count = cursor.getCount();
- results.values = cursor;
- } else {
- results.count = 0;
- results.values = null;
- }
- return results;
- }
- @Override
- protected void publishResults(CharSequence constraint, FilterResults results) {
- Cursor oldCursor = mClient.getCursor();
- if (results.values != null && results.values != oldCursor) {
- mClient.changeCursor((Cursor) results.values);
- }
- }
- }
- 转载地址:http://blog.csdn.net/qqgrid/article/details/42144029