Android — Estimated Time: 15 minutes
Working with the RecyclerView
is a standard skill for all Android developers. It is an extremely flexible class whenever a single column list of data is required. However, setting up the list view and binding it to your data can get quite tedious. When storing your data in Realm, RealmRecyclerView
and RealmBasedRecyclerViewAdapter
can be used to quickly set up your list and automatically bind your data to your view.
To demonstrate its functionality, we are going to build a basic to-do list app that animates the insertion and deletion of the rows.
Let’s get started, but if you want to jump to the end point, you can see the final code on GitHub here
Tutorial
Create a new Android Studio project, using the “Blank Activity” template.
RealmRecyclerView is available as a gradle dependency via jitpack.io.
To add it, add the following line to your project’s build.gradle
file.
maven { url "https://jitpack.io" }
The result may look like this:
allprojects {
repositories {
jcenter()
maven { url "https://jitpack.io" }
}
}
Next, in your app module’s build.gradle
file, add the following line:
compile 'com.github.thorbenprimke:realm-recycler-view:0.9.5'
That’s it, re-sync the dependencies and you are ready to go.
Now that we have our base project setup, it’s time to integrate the RealmRecyclerView
and subclass the RealmBasedRecyclerViewAdapter
to customize the display of the RecyclerView.
Integration
Open the MainActivity
’s layout file (content_main.xml
) and replace the contents with the following:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<co.moonmonkeylabs.realmrecyclerview.RealmRecyclerView
android:id="@+id/realm_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:rrvLayoutType="LinearLayout"
app:rrvSwipeToDelete="true"
app:rrvEmptyLayoutId="@layout/to_do_empty_view"
/>
</FrameLayout>
Note here that we specify three custom attributes: rrvLayoutType
, rrvSwipeToDelete
and rrvEmptyLayoutId
.
- rrvLayoutType -
RealmRecyclerView
supports different layout types and for this tutorial we want a ‘LinearLayout’. - rrvSwipeToDelete - This attribute is only supported with rrvLayoutType of LinearLayout. If set to true, swiping a row to delete is enabled. When swiped, the row is deleted from the Realm directly.
- rrvEmptyLayoutId - A custom empty state view can be provided via this attribute. Whenever the list has no item, the empty state is shown.
In order to provide a custom empty state, a layout resource is provided by setting rrvEmptyLayoutId
. Create a file in your res/layout
directory with the name to_do_empty_view.xml
and place the following content into the file:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center_horizontal"
android:text="Looks like you are all done\n(or haven\'t added anything)!"
android:textSize="24sp"
/>
</FrameLayout>
Next, RealmBasedRecyclerViewAdapter
needs to be subclassed, but before we do that we need to implement the type it will display. In the sample we are using the following ToDoItem
model class. Add this class to your project.
public class TodoItem extends RealmObject {
@PrimaryKey
private long id;
private String description;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
We will use the TodoItem class in the adapter to render a to-do item and provide it as a type parameter.
The ToDoItem
is rendered with a simple view that has a single TextView
and looks like this:
_------------------_
To-Do-Description
_------------------_
Create a layout file in your res/layout
directory with the following contents:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/todo_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:padding="4dp"
android:textSize="32sp"
android:textColor="@android:color/white"
/>
</FrameLayout>
At this point you’re ready to create a class and extend the RealmBasedRecyclerViewAdapter
.
In your activity insert the following code as a subclass into the Activity.
public class ToDoRealmAdapter
extends RealmBasedRecyclerViewAdapter<TodoItem, ToDoRealmAdapter.ViewHolder> {
public class ViewHolder extends RealmViewHolder {
public TextView todoTextView;
public ViewHolder(FrameLayout container) {
super(container);
this.todoTextView = (TextView) container.findViewById(R.id.todo_text_view);
}
}
public ToDoRealmAdapter(
Context context,
RealmResults<TodoItem> realmResults,
boolean automaticUpdate,
boolean animateResults) {
super(context, realmResults, automaticUpdate, animateResults);
}
@Override
public ViewHolder onCreateRealmViewHolder(ViewGroup viewGroup, int viewType) {
View v = inflater.inflate(R.layout.to_do_item_view, viewGroup, false);
ViewHolder vh = new ViewHolder((FrameLayout) v);
return vh;
}
@Override
public void onBindRealmViewHolder(ViewHolder viewHolder, int position) {
final TodoItem toDoItem = realmResults.get(position);
viewHolder.todoTextView.setText(toDoItem.getDescription());
viewHolder.itemView.setBackgroundColor(
COLORS[(int) (toDoItem.getId() % COLORS.length)]
);
}
}
Notice that two methods in the ToDoRealmAdapter
have been overloaded -
onCreateRealmViewHolder
: This creates the viewHolder with the view.onBindRealmViewHolder
: This binds the story to the view.
In the onCreateRealmViewHolder
method the to_do_item_view
layout is inflated and it is passed to the ViewHolder
.
In the onBindRealmViewHolder
method the ToDoItem
is bound to the view.
Copy the following code into your MainActivity
to define the COLORS
array:
private static final int[] COLORS = new int[] {
Color.argb(255, 28, 160, 170),
Color.argb(255, 99, 161, 247),
Color.argb(255, 13, 79, 139),
Color.argb(255, 89, 113, 173),
Color.argb(255, 200, 213, 219),
Color.argb(255, 99, 214, 74),
Color.argb(255, 205, 92, 92),
Color.argb(255, 105, 5, 98)
};
Connecting The Pieces
Now that the ToDoRealmAdapter
is set up, it’s time to put the pieces together.
In the onCreate
method of your Activity, add the following code:
// 'realm' is a field variable
realm = Realm.getInstance(this);
RealmResults<TodoItem> toDoItems = realm
.where(TodoItem.class)
.findAllSorted("id", true);
ToDoRealmAdapter toDoRealmAdapter = new ToDoRealmAdapter(this, toDoItems, true, true);
RealmRecyclerView realmRecyclerView = (RealmRecyclerView) findViewById(R.id.realm_recycler_view);
realmRecyclerView.setAdapter(toDoRealmAdapter);
Here the initial RealmResult
is queried and provided to the ToDoRealmAdapter
.
Notice that the constructor has two boolean parameters. These ensure that the adapter automatically displays new Realm results for the original query and that the LinearLayout items are animated.
At this point you can run the application but the Realm
is empty and thus nothing will be displayed.
We need to create a UI for the user to be able to add ToDo items. To do this you’ll need to connect the FAB to a dialog into which the user can type the description of a to-do item.
First, let’s update the FAB’s icon to a ‘+’ icon. You can download it here (or right click and download the image below).
Open the MainActivity
’s layout file (activity_main.xml
) and find the FloatingActionButton
and set its src
property to @drawable/ic_add_white_24dp
.
Next, we will create an input dialog that will allow items to be added. You can simply copy the following code snippets.
Create a layout file in the res/layout
directory with the name to_do_dialog_view.xml
and insert the following xml layout code:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:gravity="center_horizontal"
android:text="What do you want to accomplish?"
android:textStyle="bold"
android:textSize="18sp"
/>
<EditText
android:id="@+id/input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:singleLine="true"
android:imeOptions="actionDone"
android:hint="Task Description"
android:inputType="textCapWords"
/>
</LinearLayout>
Back in your Activity we need to add two methods that will enable the dialog to be shown and new ToDo items to be added to the Realm. Add this code to your Activity class.
private void buildAndShowInputDialog() {
final AlertDialog.Builder builder = new AlertDialog.Builder(ToDoActivity.this);
builder.setTitle("Create A Task");
LayoutInflater li = LayoutInflater.from(this);
View dialogView = li.inflate(R.layout.to_do_dialog_view, null);
final EditText input = (EditText) dialogView.findViewById(R.id.input);
builder.setView(dialogView);
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
addToDoItem(input.getText().toString());
}
});
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
final AlertDialog dialog = builder.show();
input.setOnEditorActionListener(
new EditText.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE ||
(event.getAction() == KeyEvent.ACTION_DOWN &&
event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) {
dialog.dismiss();
addToDoItem(input.getText().toString());
return true;
}
return false;
}
});
}
private void addToDoItem(String toDoItemText) {
if (toDoItemText == null || toDoItemText.length() == 0) {
Toast
.makeText(this, "Empty ToDos don't get stuff done!", Toast.LENGTH_SHORT)
.show();
return;
}
realm.beginTransaction();
TodoItem todoItem = realm.createObject(TodoItem.class);
todoItem.setId(System.currentTimeMillis());
todoItem.setToDo(toDoItemText);
realm.commitTransaction();
}
The Realm
related code is in addToDoItem
. In the addToDoItem
method a new ToDoItem
is created and added to the Realm
. Because the ToDoRealmAdapter
has automatic updates enabled, any items added to the Realm
will automatically be displayed in the list.
Before we are ready to try out our To-Do app, the FAB click event needs to be wired up to display the dialog. For this, add buildAndShowInputDialog()
method to the respective onClickListener
in onCreate
method of your activity and remove any existing code beforehand. It should look like this:
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
buildAndShowInputDialog();
}
});
For reference, the full working example activity code is here.
With the to-do item ‘add dialog’ in place, we can now run the application and add to-do items. Create a few to see them displayed in the list with different color backgrounds. Swipe any of the items to delete them.
That’s it! Happy coding!
Receive news and updates from Realm straight to your inbox