Get cursor by using Realm library
Asked Answered
B

3

8

I am using cursor adapter for my list view. I was planning to use content resolvers to get a cursor. Now i changed my mind to give it a chance to learn Realm.

How can i get "Cursor" object by using realm? By the way, i would also be appreciate give a few snippet to sort cursor.

Thanks.

final class Contact extends RealmObject {

private CharSequence mName;


public CharSequence getName() {
    return mName;
}

public void setName(CharSequence name) {
    this.mName = name;
}

}

final class ContactListAdapter extends CursorRecyclerViewAdapter<ContactListAdapter.ContactHolder> implements View.OnClickListener {

private OnContactClickListener mListener;


public ContactListAdapter(Context context, Cursor cursor) {
    super(context, cursor);
}

@Override
public ContactHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View itemView = getLayoutInflater().inflate(R.layout.cell_contact, parent, false);

    ContactHolder contactHolder = new ContactHolder(itemView);
    contactHolder.mContactPhotoImageView.setOnClickListener(this);

    return contactHolder;
}

@Override
public void onBindViewHolder(ContactHolder viewHolder, Cursor cursor) {
    viewHolder.mNameTextView.setText("Emre Akturk");

    int position = cursor.getPosition();
    viewHolder.mContactPhotoImageView.setImageResource(R.mipmap.ic_launcher);
    viewHolder.mContactPhotoImageView.setTag(position);
}

@Override
public int getItemCount() {
    return 5;
}

public void setOnContactClickListener(OnContactClickListener callback) {
    this.mListener = callback;
}

@Override
public void onClick(View v) {
    if (mListener == null) {
        return;
    }

    int position = (int) v.getTag();
    if (position == -1) {
        mListener.onContactCallClicked(v, getCursor(), position);
    } else {
        mListener.onContactAddClicked(v, position);
    }
}

public interface OnContactClickListener {
    void onContactCallClicked(View v, Cursor cursor, int position);

    void onContactAddClicked(View v, int position);
}

protected class ContactHolder extends RecyclerView.ViewHolder {
    private TextView mNameTextView;
    private CircleImageView mContactPhotoImageView;

    protected ContactHolder(View itemView) {
        super(itemView);
        mNameTextView = (TextView) itemView.findViewById(R.id.cell_contact_name_textview);
        mContactPhotoImageView = (CircleImageView) itemView.findViewById(R.id.cell_contact_photo_imageview);
    }
}

}

Basilica answered 12/4, 2015 at 7:47 Comment(0)
D
7

Christian from Realm here. Realm currently doesn't expose a Cursor although it is something we want to do: https://github.com/realm/realm-java/issues/438

If you want to use a RecyclerView with Realm I would recommend this blogpost for advice on how to integrate them: http://gradlewhy.ghost.io/realm-results-with-recyclerview/

Denisse answered 15/4, 2015 at 4:52 Comment(6)
Its nice to hear future updates :) Where can we can try out snapshot builds?Apologia
See this for info on how to set it up: github.com/realm/realm-java/issues/936. Latest -SNAPSHOT is 0.80.1Denisse
is it in MavenCentral? or Snapshot server?Apologia
Doh, forgot to post the link. It is Bintrays snapshot server. I have updated the first comment.Denisse
Question, any reason why you left out cursors? Isn't this suppose to be your top priority when using realm in Android? No? I somehow demand for an explanation.Adama
The only required usage for Cursors are in ContentProviders for sharing data with other apps. All other use cases can be solved easier without Cursors, but if you feel strongly about please add your use case here: github.com/realm/realm-java/issues/438 so we can prioritise accordingly.Denisse
P
3

Currently, i think that Realm doesn't support the ContentProvider architecture.

But, just to match on the Cursor interface, you can fetch a list result from Realm and create a MatrixCursor from this data.

Sure, is not the best solution for RAM performance. Cursor support is really a deep needed feature on Realm.

Protomorphic answered 8/6, 2015 at 0:47 Comment(0)
K
0

Try this. It's not perfect (I don't even know what all Cursor methods do...), but it works for me:

open class DetachedRealmCursor<T : RealmModel>(val clazz: Class<T>, fn: (Realm) -> RealmResults<T>) : Cursor {
    open val result: List<T> by lazy {
        val realm = Realm.getDefaultInstance()
        val list = realm.copyFromRealm(fn(realm))
        //val list = fn(realm)
        realm.close()
        closed = false
        list
    }
    var current = 0
    protected var notUri: Uri? = null
    protected var contencontObservable = ContentObservable()
    protected var datasetObservable = DataSetObservable()

    private val columnTypes: List<Int>
    private val columnNames: List<String>
    private var extras : Bundle = Bundle.EMPTY
    private var closed = true

    val FIELD_TYPE_REALMLIST = Cursor.FIELD_TYPE_BLOB+1

    init {
        columnTypes = getFieldTypes()
        columnNames = getFieldNames()
    }

    private fun getFieldNames(): List<String> {
        return clazz.declaredFields?.map {
            it.name
        } ?: listOf("col1")
    }

    private fun getFieldTypes(): List<Int> {
        return clazz.declaredFields?.map {
            when (it.type) {
                String::class.java -> Cursor.FIELD_TYPE_STRING
                Float::class.java -> Cursor.FIELD_TYPE_FLOAT
                Int::class.java -> Cursor.FIELD_TYPE_INTEGER
                RealmList::class.java -> FIELD_TYPE_REALMLIST
                else -> Cursor.FIELD_TYPE_NULL
            }
        } ?: listOf(Cursor.FIELD_TYPE_STRING)
    }

    private fun getValueFromColumn(col: Int, pos: Int): Any? {
        val field = result[pos].javaClass.getDeclaredField(getColumnName(col))
        field.isAccessible = true
        return field.get(result[pos])
    }

    override fun moveToPosition(p0: Int): Boolean =
            if (p0 >= -1 && p0 <= result.size) {
                current = p0
                true
            } else
                false

    override fun moveToFirst(): Boolean =
            if (result.isNotEmpty()) {
                current = 0
                true
            } else false

    override fun move(p0: Int): Boolean = if (p0 >= -1 && p0 <= result.size) {
        current = p0
        true
    } else {
        false
    }

    override fun moveToPrevious(): Boolean = if (current > -1) {
        current--
        true
    } else false

    override fun moveToNext(): Boolean = if (current < result.size) {
        current++
        true
    } else false

    override fun isBeforeFirst(): Boolean = current == -1

    override fun moveToLast(): Boolean = if (result.isNotEmpty()) {
        current = result.size - 1
        true
    } else false

    override fun isAfterLast(): Boolean = current >= result.size

    override fun getColumnIndexOrThrow(p0: String): Int {
        val found = columnNames.indexOf(p0)
        return if (found == -1) throw IllegalArgumentException() else found
    }

    override fun getColumnNames(): Array<String> = columnNames.toTypedArray()

    override fun getType(p0: Int): Int = columnTypes[p0]

    override fun getColumnName(p0: Int): String = columnNames[p0]

    override fun getColumnIndex(p0: String?): Int = columnNames.indexOf(p0)

    override fun getColumnCount(): Int = columnNames.size

    override fun getNotificationUri(): Uri? = notUri

    override fun deactivate() {}

    override fun requery(): Boolean = true

    override fun registerContentObserver(p0: ContentObserver) {
        // Register an observer that is called when changes happen to the content backing this cursor.
        // Typically the data set won't change until requery() is called.
        contencontObservable.registerObserver(p0)
    }

    override fun registerDataSetObserver(p0: DataSetObserver) {
        // Register an observer that is called when changes happen to the contents of the this
        // cursors data set, for example, when the data set is changed via requery(), deactivate(), or close().
        datasetObservable.registerObserver(p0)
    }

    override fun unregisterContentObserver(p0: ContentObserver?) {
        if(!closed) {
            contencontObservable.unregisterObserver(p0)
        }
    }

    override fun unregisterDataSetObserver(p0: DataSetObserver?) {
        datasetObservable.unregisterObserver(p0)
    }

    override fun getWantsAllOnMoveCalls(): Boolean = false

    override fun getPosition(): Int = current

    override fun close() {
        closed = true
        contencontObservable.unregisterAll()
        datasetObservable.notifyInvalidated()
    }

    override fun isClosed() = closed

    override fun getCount(): Int = result.size

    override fun isFirst(): Boolean = current == 1

    override fun isLast(): Boolean {
        return current == result.size - 1
    }

    override fun isNull(p0: Int): Boolean = getValueFromColumn(p0, current) == null

    // poniższe można zamienić na getValueFromColumn

    override fun getLong(p0: Int): Long = getValueFromColumn(p0, current) as Long

    override fun getFloat(p0: Int): Float = getValueFromColumn(p0, current) as Float

    override fun getInt(p0: Int): Int = getValueFromColumn(p0, current) as Int

    override fun getBlob(p0: Int): ByteArray = getValueFromColumn(p0, current) as ByteArray

    override fun getShort(p0: Int): Short = getValueFromColumn(p0, current) as Short

    override fun getString(p0: Int): String = getValueFromColumn(p0, current) as String

    override fun getDouble(p0: Int): Double = getValueFromColumn(p0, current) as Double

    fun getList(p0: Int): RealmList<*> = getValueFromColumn(p0, current) as RealmList<*>

    override fun setNotificationUri(p0: ContentResolver?, p1: Uri?) {
        notUri = p1
    }

    override fun copyStringToBuffer(p0: Int, p1: CharArrayBuffer?) {}

    override fun respond(extras: Bundle): Bundle = Bundle.EMPTY

    override fun getExtras(): Bundle = extras

    override fun setExtras(p0: Bundle) {
        extras = p0
    }
}

Then you can use it like:

val cursor = RealmCursor(RAlbum::class.java) { it.where(RAlbum::class.java).distinct("sTitle") }

Of course if you do all your work on the same thread you can directly use realm instance instead of doing realm.copyFromRealm(fn(realm))

Kuhn answered 26/10, 2017 at 9:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.