Code Example

Kotlin Android Realm Snippets

Realm is a mobile database.

Realm is a mobile database that runs directly inside phones, tablets or wearables.

Here are its features:

  • Mobile-first: Realm is the first database built from the ground up to run directly inside phones, tablets, and wearables.
  • Simple: Data is directly exposed as objects and queryable by code, removing the need for ORM’s riddled with performance & maintenance issues. Plus, we’ve worked hard to keep our API down to very few classes: most of our users pick it up intuitively, getting simple apps up & running in minutes.
  • Modern: Realm supports easy thread-safety, relationships & encryption.
  • Fast: Realm is faster than even raw SQLite on common operations while maintaining an extremely rich feature set.

Example 1: Simple Realm Example

Let us look at a full android sample project.

Step 1. Design Layouts

We need to design our XML layouts as follows:

(a). activity_realm_basic_example.xml

<ScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:gravity="center_horizontal"
            android:text="@string/status_output"
            android:textStyle="bold"
            android:textSize="18sp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
        <LinearLayout
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:paddingTop="10sp"
            android:orientation="vertical"
            tools:context=".RealmIntroExample"/>
    </LinearLayout>
</ScrollView>

Step 2. Write Code

Finally we need to write our code as follows:

(a). Cat.java


package io.realm.examples.intro.model;

import io.realm.RealmObject;
import io.realm.RealmResults;
import io.realm.annotations.LinkingObjects;

public class Cat extends RealmObject {
    // It is possible to also use public fields, instead of getters/setters.
    public String name;

    // You can define inverse relationships.
    @LinkingObjects("cats")
    public final RealmResults<Person> owners = null;
}

(b). Dog.java


package io.realm.examples.intro.model;

import io.realm.RealmModel;
import io.realm.RealmResults;
import io.realm.annotations.LinkingObjects;
import io.realm.annotations.RealmClass;

// It is possible to use @RealmClass and implement RealmModel, instead of extending RealmObject.
@RealmClass
public class Dog implements RealmModel {
    // It is possible to also use public fields, instead of getters/setters.
    public String name;

    // You can define inverse relationships.
    @LinkingObjects("dog")
    public final RealmResults<Person> owners = null;
}

(c). Person.java

package io.realm.examples.intro.model;

import io.realm.RealmList;
import io.realm.RealmObject;
import io.realm.annotations.Ignore;
import io.realm.annotations.Index;
import io.realm.annotations.PrimaryKey;

// Your model just have to extend RealmObject.
// This will inherit an annotation which produces proxy getters and setters for all fields.
// It is also possible to use @RealmClass annotation, and implement RealmModel interface.
public class Person extends RealmObject {

    // All fields are by default persisted.
    private int age;

    // Adding an index makes queries execute faster on that field.
    @Index
    private String name;

    // Primary keys are optional, but it allows identifying a specific object
    // when Realm writes are instructed to update if the object already exists in the Realm
    @PrimaryKey
    private long id;

    // Other objects in a one-to-one relation must also implement RealmModel, or extend RealmObject
    private Dog dog;

    // One-to-many relations is simply a RealmList of the objects which also implements RealmModel
    private RealmList<Cat> cats;

    // It is also possible to have list of primitive types (long, String, Date, byte[], etc.)
    private RealmList<String> phoneNumbers;

    // You can instruct Realm to ignore a field and not persist it.
    @Ignore
    private int tempReference;

    // Let your IDE generate getters and setters for you!
    // Or if you like you can even have public fields and no accessors! See Dog.java and Cat.java
    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    public RealmList<Cat> getCats() {
        return cats;
    }

    public void setCats(RealmList<Cat> cats) {
        this.cats = cats;
    }

    public int getTempReference() {
        return tempReference;
    }

    public void setTempReference(int tempReference) {
        this.tempReference = tempReference;
    }

    public RealmList<String> getPhoneNumbers() {
        return phoneNumbers;
    }

    public void setPhoneNumbers(RealmList<String> phoneNumbers) {
        this.phoneNumbers = phoneNumbers;
    }
}

(d). IntroExampleActivity.java


package io.realm.examples.intro;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.widget.LinearLayout;
import android.widget.TextView;

import java.lang.ref.WeakReference;
import java.util.Arrays;

import io.realm.OrderedRealmCollectionChangeListener;
import io.realm.Realm;
import io.realm.RealmResults;
import io.realm.Sort;
import io.realm.examples.intro.model.Cat;
import io.realm.examples.intro.model.Dog;
import io.realm.examples.intro.model.Person;

public class IntroExampleActivity extends Activity {
    public static final String TAG = "IntroExampleActivity";

    private LinearLayout rootLayout;

    private Realm realm;

    // Results obtained from a Realm are live, and can be observed on looper threads (like the UI thread).
    // Note that if you want to observe the RealmResults for a long time, then it should be a field reference.
    // Otherwise, the RealmResults can no longer be notified if the GC has cleared the reference to it.
    private RealmResults<Person> persons;

    // OrderedRealmCollectionChangeListener receives fine-grained changes - insertions, deletions, and changes.
    // If the change set isn't needed, then RealmChangeListener can also be used.
    private final OrderedRealmCollectionChangeListener<RealmResults<Person>> realmChangeListener = (people, changeSet) -> {
        String insertions = changeSet.getInsertions().length == 0 ? "" : "n - Insertions: " + Arrays.toString(changeSet.getInsertions());
        String deletions = changeSet.getDeletions().length == 0 ? "" : "n - Deletions: " + Arrays.toString(changeSet.getDeletions());
        String changes = changeSet.getChanges().length == 0 ? "" : "n - Changes: " + Arrays.toString(changeSet.getChanges());
        showStatus("Person was loaded, or written to. " + insertions + deletions + changes);
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_realm_basic_example);
        rootLayout = findViewById(R.id.container);
        rootLayout.removeAllViews();

        // Clear the Realm if the example was previously run.
        Realm.deleteRealm(Realm.getDefaultConfiguration());

        // Create the Realm instance
        realm = Realm.getDefaultInstance();

        // Asynchronous queries are evaluated on a background thread,
        // and passed to the registered change listener when it's done.
        // The change listener is also called on any future writes that change the result set.
        persons = realm.where(Person.class).findAllAsync();

        // The change listener will be notified when the data is loaded,
        // or the Realm is written to from any threads (and the result set is modified).
        persons.addChangeListener(realmChangeListener);

        // These operations are small enough that
        // we can generally safely run them on the UI thread.
        basicCRUD(realm);
        basicQuery(realm);
        basicLinkQuery(realm);

        // More complex operations can be executed on another thread.
        new ComplexBackgroundOperations(this).execute();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        persons.removeAllChangeListeners(); // Remove the change listener when no longer needed.
        realm.close(); // Remember to close Realm when done.
    }

    private void showStatus(String text) {
        Log.i(TAG, text);
        TextView textView = new TextView(this);
        textView.setText(text);
        rootLayout.addView(textView);
    }

    private void basicCRUD(Realm realm) {
        showStatus("Perform basic Create/Read/Update/Delete (CRUD) operations...");

        // All writes must be wrapped in a transaction to facilitate safe multi threading
        realm.executeTransaction(r -> {
            // Add a person. 
            // RealmObjects with primary keys created with <code>createObject() must specify the primary key value as an argument.
            Person person = r.createObject(Person.class, 1);
            person.setName("Young Person");
            person.setAge(14);

            // Even young people have at least one phone in this day and age.
            // Please note that this is a RealmList that contains primitive values.
            person.getPhoneNumbers().add("+1 123 4567");
        });

        // Find the first person (no query conditions) and read a field
        final Person person = realm.where(Person.class).findFirst();
        showStatus(person.getName() + ":" + person.getAge());

        // Update person in a transaction
        realm.executeTransaction(r -> {
            // Managed objects can be modified inside transactions.
            person.setName("Senior Person");
            person.setAge(99);
            showStatus(person.getName() + " got older: " + person.getAge());
        });

        // Delete all persons
        showStatus("Deleting all persons");
        realm.executeTransaction(r -> r.delete(Person.class));
    }

    private void basicQuery(Realm realm) {
        showStatus("nPerforming basic Query operation...");

        // Let's add a person so that the query returns something.
        realm.executeTransaction(r -> {
            Person oldPerson = new Person();
            oldPerson.setId(99);
            oldPerson.setAge(99);
            oldPerson.setName("George");
            realm.insertOrUpdate(oldPerson);
        });

        showStatus("Number of persons: " + realm.where(Person.class).count());

        RealmResults<Person> results = realm.where(Person.class).equalTo("age", 99).findAll();

        showStatus("Size of result set: " + results.size());
    }

    private void basicLinkQuery(Realm realm) {
        showStatus("nPerforming basic Link Query operation...");

        // Let's add a person with a cat so that the query returns something.
        realm.executeTransaction(r -> {
            Person catLady = realm.createObject(Person.class, 24);
            catLady.setAge(52);
            catLady.setName("Mary");

            Cat tiger = realm.createObject(Cat.class);
            tiger.name = "Tiger";
            catLady.getCats().add(tiger);
        });

        showStatus("Number of persons: " + realm.where(Person.class).count());

        RealmResults<Person> results = realm.where(Person.class).equalTo("cats.name", "Tiger").findAll();

        showStatus("Size of result set: " + results.size());
    }

    // This AsyncTask shows how to use Realm in background thread operations.
    //
    // AsyncTasks should be static inner classes to avoid memory leaks.
    // In this example, WeakReference is used for the sake of simplicity.
    private static class ComplexBackgroundOperations extends AsyncTask<Void, Void, String> {
        private WeakReference<IntroExampleActivity> weakReference;

        public ComplexBackgroundOperations(IntroExampleActivity introExampleActivity) {
            this.weakReference = new WeakReference<>(introExampleActivity);
        }

        @Override
        protected void onPreExecute() {
            IntroExampleActivity activity = weakReference.get();
            if (activity == null) {
                return;
            }
            activity.showStatus("nnBeginning complex operations on background thread.");
        }

        @Override
        protected String doInBackground(Void... voids) {
            IntroExampleActivity activity = weakReference.get();
            if (activity == null) {
                return "";
            }
            // Open the default realm. Uses try-with-resources to automatically close Realm when done.
            // All threads must use their own reference to the realm.
            // Realm instances, RealmResults, and managed RealmObjects can not be transferred across threads.
            try (Realm realm = Realm.getDefaultInstance()) {
                String info;
                info = activity.complexReadWrite(realm);
                info += activity.complexQuery(realm);
                return info;
            }
        }

        @Override
        protected void onPostExecute(String result) {
            IntroExampleActivity activity = weakReference.get();
            if (activity == null) {
                return;
            }
            activity.showStatus(result);
        }
    }

    private String complexReadWrite(Realm realm) {
        String status = "nPerforming complex Read/Write operation...";

        // Add ten persons in one transaction
        realm.executeTransaction(r -> {
            Dog fido = r.createObject(Dog.class);
            fido.name = "fido";
            for (int i = 0; i < 10; i++) {
                Person person = r.createObject(Person.class, i);
                person.setName("Person no. " + i);
                person.setAge(i);
                person.setDog(fido);

                // The field tempReference is annotated with @Ignore.
                // This means setTempReference sets the Person tempReference
                // field directly. The tempReference is NOT saved as part of
                // the RealmObject:
                person.setTempReference(42);

                for (int j = 0; j < i; j++) {
                    Cat cat = r.createObject(Cat.class);
                    cat.name = "Cat_" + j;
                    person.getCats().add(cat);
                }
            }
        });

        // Implicit read transactions allow you to access your objects
        status += "nNumber of persons: " + realm.where(Person.class).count();

        // Iterate over all objects, with an iterator
        for (Person person : realm.where(Person.class).findAll()) {
            String dogName;
            if (person.getDog() == null) {
                dogName = "None";
            } else {
                dogName = person.getDog().name;
            }
            status += "n" + person.getName() + ":" + person.getAge() + " : " + dogName + " : " + person.getCats().size();
        }

        // Sorting
        RealmResults<Person> sortedPersons = realm.where(Person.class).sort("age", Sort.DESCENDING).findAll();
        status += "nSorting " + sortedPersons.last().getName() + " == " + realm.where(Person.class).findFirst()
                .getName();

        return status;
    }

    private String complexQuery(Realm realm) {
        String status = "nnPerforming complex Query operation...";
        status += "nNumber of persons: " + realm.where(Person.class).count();

        // Find all persons where age between 7 and 9 and name begins with "Person".
        RealmResults<Person> results = realm.where(Person.class)
                .between("age", 7, 9)       // Notice implicit "and" operation
                .beginsWith("name", "Person").findAll();

        status += "nSize of result set: " + results.size();

        return status;
    }
}

(e). MyApplication.java


package io.realm.examples.intro;

import android.app.Application;

import io.realm.Realm;

public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        // Initialize Realm. Should only be done once when the application starts.
        Realm.init(this);

        // In this example, no default configuration is set,
        // so by default, <code>RealmConfiguration.Builder().build() is used.
    }
}

Reference

You can DOWNLOAD FULL CODE.
You can also browse code or read more here.
Follow code author here.

Example 2: Kotlin Realm

Let us look at a full android sample project.

Step 1. Design Layouts

We need to design our XML layouts as follows:

(a). activity_realm_basic_example.xml

<ScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:gravity="center_horizontal"
            android:text="@string/status_output"
            android:textStyle="bold"
            android:textSize="18sp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
        <LinearLayout
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:paddingTop="10sp"
            android:orientation="vertical"
            tools:context=".RealmIntroExample"/>
    </LinearLayout>
</ScrollView>

Step 2. Write Code

Finally we need to write our code as follows:

(a). Cat.kt


package io.realm.examples.kotlin.model

import io.realm.RealmObject
import io.realm.RealmResults
import io.realm.annotations.LinkingObjects

open class Cat : RealmObject() {
    var name: String? = null

    @LinkingObjects("cats")
    val owners: RealmResults<Person>? = null
}

(b). Dog.kt


package io.realm.examples.kotlin.model

import io.realm.RealmObject
import io.realm.RealmResults
import io.realm.annotations.LinkingObjects

open class Dog : RealmObject() {
    var name: String? = null

    @LinkingObjects("dog")
    val owners: RealmResults<Person>? = null
}

(c). Person.kt


package io.realm.examples.kotlin.model

import io.realm.RealmList
import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey

// Your model has to extend RealmObject. Furthermore, the class must be annotated with open (Kotlin classes are final
// by default).
open class Person(
        // You can put properties in the constructor as long as all of them are initialized with
        // default values. This ensures that an empty constructor is generated.
        // All properties are by default persisted.
        // Properties can be annotated with PrimaryKey or Index.
        // If you use non-nullable types, properties must be initialized with non-null values.
        @PrimaryKey var id: Long = 0,

        var name: String = "",

        var age: Int = 0,

        // Other objects in a one-to-one relation must also subclass RealmObject
        var dog: Dog? = null,

        // One-to-many relations is simply a RealmList of the objects which also subclass RealmObject
        var cats: RealmList<Cat> = RealmList(),

        // You can instruct Realm to ignore a field and not persist it.
        @Ignore var tempReference: Int = 0

) : RealmObject() {
    // The Kotlin compiler generates standard getters and setters.
    // Realm will overload them and code inside them is ignored.
    // So if you prefer you can also just have empty abstract methods.
}

(d). KotlinExampleActivity.kt


package io.realm.examples.kotlin

import android.app.Activity
import android.os.Bundle
import android.util.Log
import android.widget.LinearLayout
import android.widget.TextView
import io.realm.Realm
import io.realm.Sort
import io.realm.examples.kotlin.model.Cat
import io.realm.examples.kotlin.model.Dog
import io.realm.examples.kotlin.model.Person
import io.realm.kotlin.createObject
import io.realm.kotlin.where
import org.jetbrains.anko.doAsync
import org.jetbrains.anko.uiThread

class KotlinExampleActivity : Activity() {
    companion object {
        const val TAG: String = "KotlinExampleActivity"
    }

    private lateinit var rootLayout: LinearLayout
    private lateinit var realm: Realm

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_realm_basic_example)

        rootLayout = findViewById(R.id.container)
        rootLayout.removeAllViews()

        // Open the realm for the UI thread.
        realm = Realm.getDefaultInstance()

        // Delete all persons
        // Using executeTransaction with a lambda reduces code size and makes it impossible
        // to forget to commit the transaction.
        realm.executeTransaction { realm ->
            realm.deleteAll()
        }

        // These operations are small enough that
        // we can generally safely run them on the UI thread.
        basicCRUD(realm)
        basicQuery(realm)
        basicLinkQuery(realm)

        // More complex operations can be executed on another thread, for example using
        // Anko's doAsync extension method.
        doAsync {
            var info = ""

            // Open the default realm. All threads must use its own reference to the realm.
            // Those can not be transferred across threads.

            // Realm implements the Closable interface, therefore
            // we can make use of Kotlin's built-in extension method 'use' (pun intended).
            Realm.getDefaultInstance().use { realm ->
                info += complexReadWrite(realm)
                info += complexQuery(realm)
            }
            uiThread {
                showStatus(info)
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        realm.close() // Remember to close Realm when done.
    }

    private fun showStatus(text: String) {
        Log.i(TAG, text)
        val textView = TextView(this)
        textView.text = text
        rootLayout.addView(textView)
    }

    @Suppress("NAME_SHADOWING")
    private fun basicCRUD(realm: Realm) {
        showStatus("Perform basic Create/Read/Update/Delete (CRUD) operations...")

        // All writes must be wrapped in a transaction to facilitate safe multi threading
        realm.executeTransaction { realm ->
            // Add a person
            val person = realm.createObject<Person>(0)
            person.name = "Young Person"
            person.age = 14
        }

        // Find the first person (no query conditions) and read a field
        val person = realm.where<Person>().findFirst()!!
        showStatus(person.name + ": " + person.age)

        // Update person in a transaction
        realm.executeTransaction { _ ->
            person.name = "Senior Person"
            person.age = 99
            showStatus(person.name + " got older: " + person.age)
        }
    }

    private fun basicQuery(realm: Realm) {
        showStatus("nPerforming basic Query operation...")
        showStatus("Number of persons: ${realm.where<Person>().count()}")

        val ageCriteria = 99
        val results = realm.where<Person>().equalTo("age", ageCriteria).findAll()

        showStatus("Size of result set: " + results.size)
    }

    private fun basicLinkQuery(realm: Realm) {
        showStatus("nPerforming basic Link Query operation...")
        showStatus("Number of persons: ${realm.where<Person>().count()}")

        val results = realm.where<Person>().equalTo("cats.name", "Tiger").findAll()

        showStatus("Size of result set: ${results.size}")
    }

    private fun complexReadWrite(realm: Realm): String {
        var status = "nPerforming complex Read/Write operation..."

        // Add ten persons in one transaction
        realm.executeTransaction {
            val fido = realm.createObject<Dog>()
            fido.name = "fido"
            for (i in 1..9) {
                val person = realm.createObject<Person>(i.toLong())
                person.name = "Person no. $i"
                person.age = i
                person.dog = fido

                // The field tempReference is annotated with @Ignore.
                // This means setTempReference sets the Person tempReference
                // field directly. The tempReference is NOT saved as part of
                // the RealmObject:
                person.tempReference = 42

                for (j in 0..i - 1) {
                    val cat = realm.createObject<Cat>()
                    cat.name = "Cat_$j"
                    person.cats.add(cat)
                }
            }
        }

        // Implicit read transactions allow you to access your objects
        status += "nNumber of persons: ${realm.where<Person>().count()}"

        // Iterate over all objects
        for (person in realm.where<Person>().findAll()) {
            val dogName: String = person?.dog?.name ?: "None"

            status += "n${person.name}: ${person.age} : $dogName : ${person.cats.size}"

            // The field tempReference is annotated with @Ignore
            // Though we initially set its value to 42, it has
            // not been saved as part of the Person RealmObject:
            check(person.tempReference == 0)
        }

        // Sorting
        val sortedPersons = realm.where<Person>().sort(Person::age.name, Sort.DESCENDING).findAll()
        status += "nSorting ${sortedPersons.last()?.name} == ${realm.where<Person>().findAll().first()?.name}"

        return status
    }

    private fun complexQuery(realm: Realm): String {
        var status = "nnPerforming complex Query operation..."

        status += "nNumber of persons: ${realm.where<Person>().count()}"

        // Find all persons where age between 7 and 9 and name begins with "Person".
        val results = realm.where<Person>()
            .between("age", 7, 9)       // Notice implicit "and" operation
            .beginsWith("name", "Person")
            .findAll()

        status += "nSize of result set: ${results.size}"

        return status
    }
}

(e). MyApplication.kt


package io.realm.examples.kotlin

import android.app.Application

import io.realm.Realm

class MyApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        // Initialize Realm. Should only be done once when the application starts.
        Realm.init(this)
    }
}

Reference

You can DOWNLOAD FULL CODE.
You can also browse code or read more here.
Follow code author here.

Example 3: Realm GridView Example

Awaiting below is a full android sample to demonstrate the concept:

Step 1. Design Layouts

We need to design our XML layouts as follows:

(a). city_listitem.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:tools="http://schemas.android.com/tools"
    android:gravity="center"
    android:orientation="horizontal"
    android:padding="4dp">

    <TextView
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="12dp"
        android:textColor="#121212"
        android:textSize="20sp"
        tools:text="Barcelona"/>

    <TextView
        android:id="@+id/votes"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="12dp"
        android:textSize="18sp"
        android:textColor="#3A3A3A"
        tools:text="19"/>
</LinearLayout>

(b). activity_realm_example.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".GridViewExampleActivity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:gravity="center_horizontal"
        android:text="@string/my_favorite_city"
        android:textSize="22sp" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="24dp"
        android:gravity="center_horizontal"
        android:padding="4dp"
        android:text="@string/add_vote"
        android:textSize="16sp"
        android:textStyle="bold" />

    <GridView
        android:id="@+id/cities_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:numColumns="2" />
</LinearLayout>

Step 3. Write Code

Finally we need to write our code as follows:

(a). City.java


package io.realm.examples.realmgridview;

import io.realm.RealmObject;
import io.realm.annotations.PrimaryKey;

public class City extends RealmObject {
    // If you are using GSON, field names should not be obfuscated.
    // Add either the proguard rule in proguard-rules.pro or the @SerializedName annotation.
    @PrimaryKey
    private String name;
    private long votes;

    public String getName() {
        return name;
    }

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

    public long getVotes() {
        return votes;
    }

    public void setVotes(long votes) {
        this.votes = votes;
    }

}

(b). CityAdapter.java


package io.realm.examples.realmgridview;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import java.util.Collections;
import java.util.List;
import java.util.Locale;

// This adapter is strictly to interface with the GridView and doesn't
// particular show much interesting Realm functionality.

// Alternatively from this example,
// a developer could update the getView() to pull items from the Realm.

public class CityAdapter extends BaseAdapter {
    private List<City> cities = Collections.emptyList();

    public CityAdapter() {
    }

    public void setData(List<City> details) {
        if (details == null) {
            details = Collections.emptyList();
        }
        this.cities = details;
        notifyDataSetChanged();
    }

    @Override
    public int getCount() {
        return cities.size();
    }

    @Override
    public City getItem(int position) {
        return cities.get(position);
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    // ViewHolder caches view resources so that <code>findViewById is not called for each row
    private static class ViewHolder {
        private TextView name;
        private TextView vote;

        public ViewHolder(View view) {
            name = view.findViewById(R.id.name);
            vote = view.findViewById(R.id.votes);
        }

        public void bind(City city) {
            name.setText(city.getName());
            vote.setText(String.format(Locale.US, "%d", city.getVotes()));
        }
    }

    @Override
    public View getView(int position, View currentView, ViewGroup parent) {
        // GridView requires ViewHolder pattern to ensure optimal performance
        ViewHolder viewHolder;
        if (currentView == null) {
            currentView = LayoutInflater.from(parent.getContext()).inflate(R.layout.city_listitem, parent, false);
            viewHolder = new ViewHolder(currentView);
            currentView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder)currentView.getTag();
        }

        City city = cities.get(position);
        viewHolder.bind(city);

        return currentView;
    }
}

(c). GridViewExampleActivity.java


package io.realm.examples.realmgridview;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.GridView;

import io.realm.Realm;
import io.realm.RealmChangeListener;
import io.realm.RealmResults;

public class GridViewExampleActivity extends Activity implements AdapterView.OnItemClickListener {

    private GridView gridView;
    private CityAdapter adapter;

    private Realm realm;
    private RealmResults<City> cities;
    private RealmChangeListener<RealmResults<City>> realmChangeListener = cities -> {
        // Set the cities to the adapter only when async query is loaded.
        // It will also be called for any future writes made to the Realm.
        adapter.setData(cities);
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_realm_example);

        // This is the GridView adapter
        adapter = new CityAdapter();

        //This is the GridView which will display the list of cities
        gridView = findViewById(R.id.cities_list);
        gridView.setAdapter(adapter);
        gridView.setOnItemClickListener(GridViewExampleActivity.this);

        // Clear the realm from last time
        //noinspection ConstantConditions
        Realm.deleteRealm(Realm.getDefaultConfiguration());

        // Create a new empty instance of Realm
        realm = Realm.getDefaultInstance();

        // Obtain the cities in the Realm with asynchronous query.
        cities = realm.where(City.class).findAllAsync();

        // The RealmChangeListener will be called when the results are asynchronously loaded, and available for use.
        cities.addChangeListener(realmChangeListener);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        cities.removeAllChangeListeners(); // Remove change listeners to prevent updating views not yet GCed.
        realm.close(); // Remember to close Realm when done.
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        City modifiedCity = adapter.getItem(position);

        // Acquire the name of the clicked City, in order to be able to query for it.
        final String name = modifiedCity.getName();

        // Create an asynchronous transaction to increment the vote count for the selected City in the Realm.
        // The write will happen on a background thread, and the RealmChangeListener will update the GridView automatically.
        realm.executeTransactionAsync(bgRealm -> {
            // We need to find the City we want to modify from the background thread's Realm
            City city = bgRealm.where(City.class).equalTo("name", name).findFirst();
            if (city != null) {
                // Let's increase the votes of the selected city!
                city.setVotes(city.getVotes() + 1);
            }
        });
    }
}

(d). MyApplication.java


package io.realm.examples.realmgridview;

import android.app.Application;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;

import io.realm.Realm;
import io.realm.RealmConfiguration;

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Realm.init(this);
        Realm.setDefaultConfiguration(new RealmConfiguration.Builder()
                .initialData(realm -> {
                    // Load from file "cities.json" first time
                    List<City> cities = loadCities();
                    if (cities != null) {
                        // Use insertOrUpdate() to convert the objects into proper RealmObjects managed by Realm.
                        realm.insertOrUpdate(cities);
                    }
                })
                .deleteRealmIfMigrationNeeded()
                .build()
        );
    }

    private List<City> loadCities() {
        // In this case we're loading from local assets.
        // NOTE: could alternatively easily load from network.
        // However, that would need to happen on a background thread.
        InputStream stream;
        try {
            stream = getAssets().open("cities.json");
        } catch (IOException e) {
            return null;
        }

        Gson gson = new GsonBuilder().create();

        JsonElement json = new JsonParser().parse(new InputStreamReader(stream));

        return gson.fromJson(json, new TypeToken<List<City>>() {
        }.getType());
    }
}

Reference

You can DOWNLOAD FULL CODE.
You can also browse code or read more here.
Follow code author here.

Related Posts