Skip to content

Instantly share code, notes, and snippets.

@dGorod
Created December 22, 2015 22:11
Show Gist options
  • Select an option

  • Save dGorod/25eb232b72d06eaef1ba to your computer and use it in GitHub Desktop.

Select an option

Save dGorod/25eb232b72d06eaef1ba to your computer and use it in GitHub Desktop.
Experimental approach to implement expandable RecycleView that works with cursors.
package com.exsoft.musarium.adapter;
/*
* Copyright (C) 2015 [email protected]
*
* 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.database.Cursor;
import android.database.DataSetObserver;
import com.h6ah4i.android.widget.advrecyclerview.utils.AbstractExpandableItemAdapter;
import com.h6ah4i.android.widget.advrecyclerview.utils.AbstractExpandableItemViewHolder;
/**
* Abstract realization of expandable adapter for RecycleView that works with cursor.
* Expansion feature itself is base on:
* https://github.com/h6ah4i/android-advancedrecyclerview
*
* The main drawback: in case of changes in list collection itself (ex. implementing some
* sorting) or background changes in database, code will work unexpectedly. Because it relays on
* stable data.
*
* Created by Dmitriy Gorodnytskiy on 12/19/15.
*/
public abstract class CursorExpandableRecyclerViewAdapter<GVH extends AbstractExpandableItemViewHolder,
CVH extends AbstractExpandableItemViewHolder> extends AbstractExpandableItemAdapter<GVH, CVH> {
private Cursor mCursor;
private boolean mDataValid;
private int mRowIdColumn;
private DataSetObserver mDataSetObserver;
public CursorExpandableRecyclerViewAdapter(Cursor cursor) {
mCursor = cursor;
mDataValid = cursor != null;
mRowIdColumn = mDataValid ? mCursor.getColumnIndex("_id") : -1;
mDataSetObserver = new NotifyingDataSetObserver();
if (mCursor != null) {
mCursor.registerDataSetObserver(mDataSetObserver);
}
}
public Cursor getCursor() {
return mCursor;
}
public abstract Cursor getChildrenCursor(Cursor groupCursor);
public abstract boolean onCheckGroupExpandable(GVH groupViewHolder, Cursor groupCursor);
public abstract void onBindGroupViewHolder(GVH groupViewHolder, Cursor groupCursor);
public abstract void onBindChildViewHolder(CVH childViewHolder, Cursor childCursor);
@Override
public final void onBindGroupViewHolder(GVH holder, int groupPosition, int viewType) {
if (!mDataValid) {
throw new IllegalStateException("this should only be called when the cursor is valid");
}
if (!mCursor.moveToPosition(groupPosition)) {
throw new IllegalStateException("couldn't move cursor to position " + groupPosition);
}
onBindGroupViewHolder(holder, mCursor);
}
@Override
public final void onBindChildViewHolder(CVH holder, int groupPosition, int childPosition, int viewType) {
if (!mDataValid) {
throw new IllegalStateException("this should only be called when the cursor is valid");
}
if (!mCursor.moveToPosition(groupPosition)) {
throw new IllegalStateException("couldn't move cursor to position " + groupPosition);
}
Cursor childrenCursor = getChildrenCursor(mCursor);
if (!childrenCursor.moveToPosition(childPosition)) {
throw new IllegalStateException("couldn't move children cursor to position " + childPosition);
}
onBindChildViewHolder(holder, childrenCursor);
}
@Override
public int getGroupCount() {
if (mDataValid && mCursor != null) {
return mCursor.getCount();
}
return 0;
}
@Override
public int getChildCount(int groupPosition) {
int count = 0;
if (mDataValid && mCursor != null && mCursor.moveToPosition(groupPosition)) {
Cursor c = getChildrenCursor(mCursor);
count = c.getCount();
c.close();
}
return count;
}
@Override
public long getGroupId(int groupPosition) {
if (mDataValid && mCursor != null && mCursor.moveToPosition(groupPosition)) {
return mCursor.getLong(mRowIdColumn);
}
return 0;
}
@Override
public long getChildId(int groupPosition, int childPosition) {
long id = 0L;
if (mDataValid && mCursor != null && mCursor.moveToPosition(groupPosition)) {
Cursor childrenCursor = getChildrenCursor(mCursor);
if(childrenCursor.moveToPosition(childPosition)) {
id = childrenCursor.getLong(mRowIdColumn);
}
childrenCursor.close();
}
return id;
}
@Override
public final boolean onCheckCanExpandOrCollapseGroup(GVH holder, int groupPosition, int x, int y, boolean expand) {
return mDataValid && mCursor != null && mCursor.moveToPosition(groupPosition) &&
onCheckGroupExpandable(holder, mCursor);
}
@Override
public void setHasStableIds(boolean hasStableIds) {
super.setHasStableIds(true);
}
/**
* Change the underlying cursor to a new cursor. If there is an existing cursor it will be
* closed.
*/
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.
*/
public Cursor swapCursor(Cursor newCursor) {
if (newCursor == mCursor) {
return null;
}
final Cursor oldCursor = mCursor;
if (oldCursor != null && mDataSetObserver != null) {
oldCursor.unregisterDataSetObserver(mDataSetObserver);
}
mCursor = newCursor;
if (mCursor != null) {
if (mDataSetObserver != null) {
mCursor.registerDataSetObserver(mDataSetObserver);
}
mRowIdColumn = newCursor.getColumnIndexOrThrow("_id");
mDataValid = true;
notifyDataSetChanged();
} else {
mRowIdColumn = -1;
mDataValid = false;
notifyDataSetChanged();
//There is no notifyDataSetInvalidated() method in RecyclerView.Adapter
}
return oldCursor;
}
private class NotifyingDataSetObserver extends DataSetObserver {
@Override
public void onChanged() {
super.onChanged();
mDataValid = true;
notifyDataSetChanged();
}
@Override
public void onInvalidated() {
super.onInvalidated();
mDataValid = false;
notifyDataSetChanged();
//There is no notifyDataSetInvalidated() method in RecyclerView.Adapter
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment