(Tutorial Android) Tutorial CRUD with Realm Android - ARTANA

(Tutorial Android) Tutorial CRUD with Realm Android


Salah satu database yang wajib di coba oleh para developer mobile adalah Realm. Realm adalah mobile database pengganti SQLite dan Core Data. Realm dapat berjalan secara langsung di dalam perangkat smartphone, tablet atau wearebles. Selain itu, Realm memiliki beberapa kelebihan dibanding SQLite diantaranya :

1. Simple : Data dipresentasikan dalam bentuk object dan menjalankan query dengan kode.
2. Cepat : Realm lebih cepat daripada SQLite maupun ORM.
3. Cross-Platform : Realm mendukung berbagai platform diantaranya iOS, OSX dan Android.
4. Modern : Realm mendukung thread-safety, relationships dan encryption.

Dan masih banyak lagi keunggulan dari Realm.

Oke, pada tutorial ini kita akan membuat aplikasi CRUD sederhana dengan Realm. Aplikasinya saya namakan Simple Note App.

Untuk project ini saya menggunakan Realm versi 0.87.5. (kalau yang sekarang sudah versi 0.90.1).
Latest version bisa dilihat di https://realm.io/docs/java/latest/

Pertama kita menambahkan dependencies di build.gradle.
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.3.0'
compile 'com.android.support:recyclerview-v7:23.3.0'
compile 'io.realm:realm-android:0.87.5'
}
Buat beberapa layout berikut :
list_item_note.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/activity_horizontal_margin">

<TextView
android:id="@+id/note"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:text="Note" />

<TextView
android:id="@+id/date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="11sp"
android:text="Date"
android:layout_gravity="right" />


</LinearLayout>
activity_save.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:descendantFocusability="beforeDescendants"
android:focusableInTouchMode="true">

<EditText
android:id="@+id/noteText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:inputType="text"
android:ems="10"
android:gravity="top"
android:imeOptions="flagNoExtractUi"
android:hint="Write note here..."/>

</LinearLayout>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
tools:context=".activities.MainActivity">

<android.support.v7.widget.RecyclerView
android:id="@+id/lvNote"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical"
android:layout_above="@+id/btnAdd"
android:layout_alignParentTop="true" />

<Button
android:id="@+id/btnAdd"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Add"
android:background="@color/colorPrimary"
android:textColor="@android:color/white"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />

</RelativeLayout>
Nah berikutnya kita akan membuat kelas yang berisi settingan realm dengan method-method CRUD.
package com.example.android_realm.db;

import android.content.Context;
import java.util.List;
import io.realm.Realm;
import io.realm.RealmConfiguration;
import io.realm.RealmMigration;
import io.realm.RealmObject;
import io.realm.RealmResults;

/**
* Created by wim on 5/10/16.
*/
public class RealmDB {

private Realm realm;
private Context context;

public RealmDB(Context context) {
this.context = context;
realm = Realm.getInstance(context);
}

public void setMigration(RealmMigration migration) {
RealmConfiguration config = new RealmConfiguration.Builder(context)
.schemaVersion(0)
.name("example.realm")
.migration(migration)
.build();
realm = Realm.getInstance(config);
}

public Realm getRealm(){
return realm;
}

@SuppressWarnings("unchecked")
public <T extends RealmObject> T getById(Class<? extends RealmObject> cls, int id) {
return (T) this.getRealm().where(cls.asSubclass(RealmObject.class)).equalTo("id", id).findFirst();
}

public RealmResults<? extends RealmObject> getAllData(Class<? extends RealmObject> cls) {
return this.getRealm().where(cls.asSubclass(RealmObject.class)).findAll();
}

public void add(RealmObject object) {
this.getRealm().beginTransaction();
this.getRealm().copyToRealm(object);
this.getRealm().commitTransaction();
}

public void add(List<RealmObject> listObject) {
this.getRealm().beginTransaction();
this.getRealm().copyToRealm(listObject);
this.getRealm().commitTransaction();
}

public void delete(Class<? extends RealmObject> cls, int id) {
RealmResults results = this.getRealm().where(cls.asSubclass(RealmObject.class)).equalTo("id", id).findAll();
this.getRealm().beginTransaction();
results.clear();
this.getRealm().commitTransaction();
}

public void update(RealmObject object){
this.getRealm().beginTransaction();
this.getRealm().copyToRealmOrUpdate(object);
this.getRealm().commitTransaction();
}
}

Buat kelas MainApplication berikut.
package com.example.android_realm;

import android.app.Application;

import com.example.android_realm.db.RealmDB;

import io.realm.DynamicRealm;
import io.realm.RealmMigration;
import io.realm.RealmSchema;

/**
* Created by wim on 5/12/16.
*/
public class MainApplication extends Application {

@Override
public void onCreate() {
super.onCreate();

RealmDB realmDB = new RealmDB(this);
realmDB.setMigration(new DataMigration());
}

private class DataMigration implements RealmMigration {
@Override
public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {

RealmSchema schema = realm.getSchema();

if (oldVersion == 0) {
schema.create("Note")
.addField("id", int.class)
.addField("note", String.class)
.addField("dateModified", String.class);
oldVersion++;
}

}
}
}

Kemudian kita membuat model datanya. Kelas ini wajib untuk meng-extends RealmObject.
package com.example.android_realm.model;

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

/**
* Created by wim on 5/14/16.
*/
public class Note extends RealmObject{

@PrimaryKey
private int id;
private String note;
private String dateModified;


public Note() {
}

public int getId() {
return id;
}

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

public String getNote() {
return note;
}

public void setNote(String note) {
this.note = note;
}

public String getDateModified() {
return dateModified;
}

public void setDateModified(String dateModified) {
this.dateModified = dateModified;
}

}
Kemudian buat adapter untuk recyclerview.
package com.example.android_realm.adapter;

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.example.android_realm.R;
import com.example.android_realm.listener.RecyclerItemClickListener;
import com.example.android_realm.model.Note;
import com.example.android_realm.util.TimeUtil;

import io.realm.RealmResults;

/**
* Created by wim on 5/14/16.
*/
public class NoteListAdapter extends RecyclerView.Adapter<NoteListAdapter.NoteViewHolder>{

private RealmResults<Note> noteList;
private RecyclerItemClickListener recyclerItemClickListener;

public NoteListAdapter(RecyclerItemClickListener recyclerItemClickListener) {
this.recyclerItemClickListener = recyclerItemClickListener;
}

public void setNoteList(RealmResults<Note> noteList){
this.noteList = noteList;
notifyDataSetChanged();
}

public Note getItem(int position){
return noteList.get(position);
}

@Override
public NoteViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_note, parent, false);

final NoteViewHolder noteViewHolder = new NoteViewHolder(view);
noteViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int adapterPos = noteViewHolder.getAdapterPosition();
if (adapterPos != RecyclerView.NO_POSITION) {
if (recyclerItemClickListener != null) {
recyclerItemClickListener.onItemClick(adapterPos, noteViewHolder.itemView);
}
}
}
});

return noteViewHolder;
}

@Override
public void onBindViewHolder(NoteViewHolder holder, int position) {
final Note note = noteList.get(position);

holder.note.setText(note.getNote().length() > 50 ? note.getNote().substring(0, 50) : note.getNote());
holder.date.setText(TimeUtil.unixToTimeAgo(note.getDateModified()));
}

@Override
public int getItemCount() {
return noteList.size();
}

public static class NoteViewHolder extends RecyclerView.ViewHolder {

TextView note;
TextView date;

public NoteViewHolder(View itemView) {
super(itemView);

note = (TextView) itemView.findViewById(R.id.note);
date = (TextView) itemView.findViewById(R.id.date);
}
}
}
Listener ketika recyclerview diklik.
package com.example.android_realm.listener;

import android.view.View;

/**
* Created by wim on 5/14/16.
*/
public interface RecyclerItemClickListener {

void onItemClick(int position, View view);
}
Buat beberapa utility yang akan kita gunakan sebagai berikut :

DividerItemDecoration.java
package com.example.android_realm.util;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;

/**
* Created by wim on 5/14/16.
*/
public class DividerItemDecoration extends RecyclerView.ItemDecoration {

private static final int[] ATTRS = new int[]{
android.R.attr.listDivider
};

public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;

public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;

private Drawable mDivider;

private int mOrientation;

public DividerItemDecoration(Context context, int orientation) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
setOrientation(orientation);
}

public void setOrientation(int orientation) {
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}

@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (mOrientation == VERTICAL_LIST) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}

public void drawVertical(Canvas c, RecyclerView parent) {
final int left = parent.getPaddingLeft();
final int right = parent.getWidth() - parent.getPaddingRight();

final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}

public void drawHorizontal(Canvas c, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();

final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}

@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if (mOrientation == VERTICAL_LIST) {
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
}
}
}

TimeUtil.java
package com.example.android_realm.util;

import android.text.format.DateUtils;

import java.util.Date;

/**
* Created by wim on 5/14/16.
*/
public class TimeUtil {

public static long getUnix(){
return new Date().getTime();
}

public static String unixToTimeAgo(String unix){
CharSequence timeAgo = DateUtils.getRelativeTimeSpanString(
Long.parseLong(unix),
System.currentTimeMillis(), DateUtils.SECOND_IN_MILLIS);

return timeAgo.toString();
}
}

Setelah itu, buat kelas activity dengan nama SaveActivity.java. Nah di kelas ini kita mengimplementasikan method berupa insert, update dan delete.
package com.example.android_realm.activities;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;

import com.example.android_realm.R;
import com.example.android_realm.db.RealmDB;
import com.example.android_realm.model.Note;
import com.example.android_realm.util.TimeUtil;

/**
* Created by wim on 5/14/16.
*/
public class SaveActivity extends AppCompatActivity {

private EditText noteText;
private int id;

public static void start(Context context, int id){
Intent intent = new Intent(context, SaveActivity.class);
intent.putExtra(SaveActivity.class.getSimpleName(), id);
context.startActivity(intent);
}

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

ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}

noteText = (EditText) findViewById(R.id.noteText);

id = getIntent().getExtras().getInt(SaveActivity.class.getSimpleName());

if(id != 0){
// get by id
Note note = new RealmDB(this).getById(Note.class, id);
noteText.setText(String.valueOf(note.getNote()));
}
}

// add note
public void addNote(String noteText) {
Note note = new Note();
note.setId((int) (System.currentTimeMillis()) / 1000);
note.setNote(noteText);
note.setDateModified(String.valueOf(TimeUtil.getUnix()));

new RealmDB(this).add(note);
}

// update note
public void updateNote(int id, String noteText){
Note note = new Note();
note.setId(id);
note.setNote(noteText);
note.setDateModified(String.valueOf(TimeUtil.getUnix()));

new RealmDB(this).update(note);
}

// delete note
public void deleteNote(int id) {
new RealmDB(this).delete(Note.class, id);
}

private void createOrUpdate(){
if(!TextUtils.isEmpty(noteText.getText().toString())) {
if (id == 0) {
addNote(noteText.getText().toString());
} else {
updateNote(id, noteText.getText().toString());
}
}else{
return;
}
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_save, menu);

return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.

switch (item.getItemId()) {
case android.R.id.home:
createOrUpdate();
finish();
return true;
case R.id.menu_save:
createOrUpdate();
finish();
return true;
case R.id.menu_delete:
if(id != 0) deleteNote(id);
finish();
return true;
default:
return super.onOptionsItemSelected(item);
}

}

}
Terakhir, buat MainActivity.java. Di sini kita akan mengakses semua datanya kemudian menampilkannya dalam bentuk list.
package com.example.android_realm.activities;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;

import com.example.android_realm.R;
import com.example.android_realm.adapter.NoteListAdapter;
import com.example.android_realm.db.RealmDB;
import com.example.android_realm.listener.RecyclerItemClickListener;
import com.example.android_realm.model.Note;
import com.example.android_realm.util.DividerItemDecoration;

import io.realm.RealmResults;
import io.realm.Sort;

public class MainActivity extends AppCompatActivity implements RecyclerItemClickListener{

private RecyclerView lvNote;
private Button btnAdd;

private NoteListAdapter noteListAdapter;
private LinearLayoutManager linearLayoutManager;

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

lvNote = (RecyclerView) findViewById(R.id.lvNote);
btnAdd = (Button) findViewById(R.id.btnAdd);

noteListAdapter = new NoteListAdapter(this);
linearLayoutManager = new LinearLayoutManager(this);

lvNote.setLayoutManager(linearLayoutManager);
lvNote.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));
lvNote.setAdapter(noteListAdapter);

btnAdd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SaveActivity.start(MainActivity.this, 0);
}
});
}

@Override
protected void onResume() {
super.onResume();
loadData();
}

private void loadData(){
if(retrieve() != null)
noteListAdapter.setNoteList(retrieve());
}

public RealmResults<Note> retrieve() {
RealmResults<Note> result = (RealmResults<Note>) new RealmDB(this).getAllData(Note.class);
result.sort("dateModified", Sort.DESCENDING);
return result;
}

@Override
public void onItemClick(int position, View view) {
SaveActivity.start(this, noteListAdapter.getItem(position).getId());
}

@Override
public void onBackPressed() {
super.onBackPressed();
finish();
}
}

Build dan jalankan maka hasilnya sebagai berikut :



Oke segitu dulu tutorial untuk kali ini.

Source code lengkap dapat dilihat di https://github.com/wimsonevel/Android-Realm

Sekian dan semoga bermanfaat.
Happy Coding :)

Berlangganan update artikel terbaru via email:

close