CRUD (Create, Read, Update, Dan Delete) With SQL LITE The Notes App
CRUD (Create, Read, Update, Dan Delete) With SQL LITE The Notes App
Open build.gradle under app directory and add RecyclerView dependency. The
RecyclerView will be used to display the Notes in list manner.
build.gradle
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
// ..
implementation 'com.android.support:recyclerview-v7:26.1.0'
}
1
<color name="msg_no_notes">#999</color>
<color name="hint_enter_note">#89c3c3c3</color>
<color name="timestamp">#858585</color>
<color name="note_list_text">#232323</color>
</resources>
dimens.xml
<resources>
<dimen name="fab_margin">16dp</dimen>
<dimen name="activity_margin">16dp</dimen>
<dimen name="dot_margin_right">10dp</dimen>
<dimen name="msg_no_notes">26sp</dimen>
<dimen name="margin_top_no_notes">120dp</dimen>
<dimen name="lbl_new_note_title">20sp</dimen>
<dimen name="dimen_10">10dp</dimen>
<dimen name="input_new_note">20sp</dimen>
<dimen name="dot_height">30dp</dimen>
<dimen name="dot_text_size">40sp</dimen>
<dimen name="timestamp">14sp</dimen>
<dimen name="note_list_text">18sp</dimen>
</resources>
string.xml
<resources>
<string name="app_name">CRUD SQL LITE APPS</string>
<string name="action_settings">Settings</string>
<string name="activity_title_home">Notes</string>
<string name="msg_no_notes">No notes found!</string>
<string name="lbl_new_note_title">New Note</string>
<string name="lbl_edit_note_title">Edit Note</string>
<string name="hint_enter_note">Enter your note!</string>
</resources>
Quickly create few packages named database, database/model, utils and view.
Below is the final project structure and files we gonna need.
2
3. Writing SQLite Helper Class
We need to create a class that extends from SQLiteOpenHelper. This class perform
CRUD operations (Create, Read, Update and Delete) on the database.
Note.java
package com.dipanegara.crudsqlliteapps.database.model;
DatabaseHelper.java
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import com.dipanegara.crudsqlliteapps.database.model.Note;
// Database Version
private static final int DATABASE_VERSION = 1;
// Database Name
private static final String DATABASE_NAME = "notes_db";
4
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
// Creating Tables
@Override
public void onCreate(SQLiteDatabase db) {
// Upgrading database
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int
newVersion) {
// Drop older table if existed
db.execSQL("DROP TABLE IF EXISTS " + Note.TABLE_NAME);
Now we’ll see the methods required to store or retrieve the notes. Add the
following methods to same class.
Inserting Note
Inserting data requires getting writable instance (getReadableDatabase()) on
database. Below, we are inserting new note in database.
• ContentValues() is used to define the column name and its data to be stored.
Here, we are just setting the note value only ignoring ‘id’ and ‘timestamp’ as
these two will be inserted automatically.
• Every time the database connection has to be closed once you are done with
database access. Calling db.close() closes the connection.
• Once the note is inserted, the ‘id’ of newly inserted note will be returned.
// insert row
long id = db.insert(Note.TABLE_NAME, null, values);
// close db connection
5
db.close();
Reading Notes
Reading data requires only read access (getReadableDatabase()) on the database.
• getNote() takes already existed note ‘id’ and fetches the note object.
• getAllNotes() fetches all the notes in descending order by timestamp.
• getNotesCount() returns the count of notes stored in database.
if (cursor != null)
cursor.moveToFirst();
cursor.getString(cursor.getColumnIndex(Note.COLUMN_NOTE)),
cursor.getString(cursor.getColumnIndex(Note.COLUMN_TIMESTAMP)));
return note;
}
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
6
// looping through all rows and adding to list
if (cursor.moveToFirst()) {
do {
Note note = new Note();
note.setId(cursor.getInt(cursor.getColumnIndex(Note.COLUMN_ID)));
note.setNote(cursor.getString(cursor.getColumnIndex(Note.COLUMN_NOT
E)));
note.setTimestamp(cursor.getString(cursor.getColumnIndex(Note.COLUM
N_TIMESTAMP)));
notes.add(note);
} while (cursor.moveToNext());
}
// close db connection
db.close();
// return count
return count;
}
Updating Note
Updating data again requires writable access. Below the note is updated by its ‘id’.
public int updateNote(Note note) {
SQLiteDatabase db = this.getWritableDatabase();
// updating row
return db.update(Note.TABLE_NAME, values, Note.COLUMN_ID + " =
?",
new String[]{String.valueOf(note.getId())});
}
7
Deleting Note
Deleting data also requires writable access. Below method deletes a note by finding
its ‘id’.
public void deleteNote(Note note) {
SQLiteDatabase db = this.getWritableDatabase();
db.delete(Note.TABLE_NAME, Note.COLUMN_ID + " = ?",
new String[]{String.valueOf(note.getId())});
db.close();
}
After adding all the methods, the DatabaseHelper.java class should be like this.
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import java.util.ArrayList;
import java.util.List;
import com.dipanegara.crudsqlliteapps.database.model.Note;
// Database Version
private static final int DATABASE_VERSION = 1;
// Database Name
private static final String DATABASE_NAME = "notes_db";
// Creating Tables
@Override
public void onCreate(SQLiteDatabase db) {
// Upgrading database
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int
newVersion) {
// Drop older table if existed
db.execSQL("DROP TABLE IF EXISTS " + Note.TABLE_NAME);
// insert row
long id = db.insert(Note.TABLE_NAME, null, values);
// close db connection
db.close();
if (cursor != null)
cursor.moveToFirst();
cursor.getInt(cursor.getColumnIndex(Note.COLUMN_ID)),
cursor.getString(cursor.getColumnIndex(Note.COLUMN_NOTE)),
cursor.getString(cursor.getColumnIndex(Note.COLUMN_TIMESTAMP)));
return note;
}
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
note.setId(cursor.getInt(cursor.getColumnIndex(Note.COLUMN_ID)));
note.setNote(cursor.getString(cursor.getColumnIndex(Note.COLUMN_NOT
E)));
note.setTimestamp(cursor.getString(cursor.getColumnIndex(Note.COLUM
N_TIMESTAMP)));
notes.add(note);
} while (cursor.moveToNext());
}
// close db connection
db.close();
// return count
return count;
}
// updating row
return db.update(Note.TABLE_NAME, values, Note.COLUMN_ID +
" = ?",
new String[]{String.valueOf(note.getId())});
}
10
public void deleteNote(Note note) {
SQLiteDatabase db = this.getWritableDatabase();
db.delete(Note.TABLE_NAME, Note.COLUMN_ID + " = ?",
new String[]{String.valueOf(note.getId())});
db.close();
}
}
RecyclerTouchListener.java
import android.content.Context;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
this.clicklistener = clicklistener;
gestureDetector = new GestureDetector(context, new
GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
@Override
public void onLongPress(MotionEvent e) {
View child =
recycleView.findChildViewUnder(e.getX(), e.getY());
if (child != null && clicklistener != null) {
clicklistener.onLongClick(child,
recycleView.getChildAdapterPosition(child));
}
}
});
}
11
@Override
public boolean onInterceptTouchEvent(RecyclerView rv,
MotionEvent e) {
View child = rv.findChildViewUnder(e.getX(), e.getY());
if (child != null && clicklistener != null &&
gestureDetector.onTouchEvent(e)) {
clicklistener.onClick(child,
rv.getChildAdapterPosition(child));
}
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
@Override
public void onRequestDisallowInterceptTouchEvent(boolean
disallowIntercept) {
MyDividerItemDecoration.java
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.TypedValue;
import android.view.View;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@Override
public void onDrawOver(Canvas c, RecyclerView parent,
RecyclerView.State state) {
if (mOrientation == VERTICAL_LIST) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
@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);
}
}
4. Adding Notes UI
Now we have the database helper class ready. Let’s quickly build the main interface
and integrate it with the database.
First we need an adapter to display the notes in list manner. For this, we need a
layout file and Adapter class.
Create new xml layout named note_list_row.xml. This layout holds the design of
single note item in the list.
note_list_row.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
14
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:foreground="?attr/selectableItemBackground"
android:paddingBottom="@dimen/dimen_10"
android:paddingLeft="@dimen/activity_margin"
android:paddingRight="@dimen/activity_margin"
android:paddingTop="@dimen/dimen_10">
<TextView
android:id="@+id/dot"
android:layout_width="wrap_content"
android:layout_height="@dimen/dot_height"
android:layout_marginRight="@dimen/dot_margin_right"
android:layout_marginTop="@dimen/dimen_10"
android:includeFontPadding="false"
android:textColor="@color/colorAccent"
android:lineSpacingExtra="0dp"
android:textSize="@dimen/dot_text_size" />
<TextView
android:id="@+id/timestamp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/dot"
android:textColor="@color/timestamp"
android:textSize="@dimen/timestamp" />
<TextView
android:id="@+id/note"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/timestamp"
android:layout_toRightOf="@id/dot"
android:textColor="@color/note_list_text"
android:textSize="@dimen/note_list_text" />
</RelativeLayout>
Under view package, create a class named NotesAdapter.java. This adapter class
renders the RecyclerView with defined layout and data set.
NotesAdapter.java
import android.content.Context;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
15
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import com.dipanegara.crudsqlliteapps.R;
import com.dipanegara.crudsqlliteapps.database.model.Note;
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int
viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.note_list_row, parent, false);
@Override
public void onBindViewHolder(MyViewHolder holder, int position)
{
Note note = notesList.get(position);
holder.note.setText(note.getNote());
@Override
public int getItemCount() {
return notesList.size();
}
/**
* Formatting timestamp to `MMM d` format
* Input: 2018-02-21 00:15:42
* Output: Feb 21
*/
private String formatDate(String dateStr) {
try {
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd
HH:mm:ss");
Date date = fmt.parse(dateStr);
SimpleDateFormat fmtOut = new SimpleDateFormat("MMM
d");
return fmtOut.format(date);
} catch (ParseException e) {
return "";
}
}
<TextView android:id="@+id/dialog_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/dimen_10"
android:fontFamily="sans-serif-medium"
android:lineSpacingExtra="8sp"
android:text="@string/lbl_new_note_title"
android:textColor="@color/colorAccent"
17
android:textSize="@dimen/lbl_new_note_title"
android:textStyle="normal" />
<EditText
android:id="@+id/note"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:gravity="top"
android:hint="@string/hint_enter_note"
android:inputType="textCapSentences|textMultiLine"
android:lines="4"
android:textColorHint="@color/hint_enter_note"
android:textSize="@dimen/input_new_note" />
</LinearLayout>
Open the layout files of main activity (activity_main.xml) and add RecyclerView
widget. I am also changing the icon of FAB here.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context=".view.MainActivity"
tools:showIn="@layout/activity_main">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<TextView
android:id="@+id/empty_notes_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/margin_top_no_notes"
android:fontFamily="sans-serif-light"
android:text="@string/msg_no_notes"
android:textColor="@color/msg_no_notes"
android:textSize="@dimen/msg_no_notes" />
</RelativeLayout>
18
Finally open MainActivity.java and do the below changes.
• showNoteDialog() open the alert dialog to create new note. This dialog will be
shown by tapping FAB.
• createNote() inserts new note in database and adds the newly inserted note in
RecyclerView list.
• showActionsDialog() shows a dialog with Edit and Delete options. This dialog
can be invoked by long pressing the note in the list.
• Selecting Edit, opens the update note dialog with already existed note text. You
can modify the note text and update it in database by calling updateNote()
method.
• deleteNote() deletes a note from database. The deleted note is again removed
from list by calling notifyItemRemoved() on adapter.
• toggleEmptyNotes() toggles the visibility of notes and empty note view
depending on the count (db.getNotesCount() > 0) of notes.
MainActivity.java
package com.dipanegara.crudsqlliteapps.view;
import androidx.appcompat.app.AppCompatActivity;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.dipanegara.crudsqlliteapps.R;
import com.dipanegara.crudsqlliteapps.database.DatabaseHelper;
import com.dipanegara.crudsqlliteapps.database.model.Note;
import
com.dipanegara.crudsqlliteapps.utils.MyDividerItemDecoration;
import com.dipanegara.crudsqlliteapps.utils.RecyclerTouchListener;
import java.util.ArrayList;
19
import java.util.List;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recycler_view);
noNotesView = findViewById(R.id.empty_notes_view);
db = new DatabaseHelper(this);
notesList.addAll(db.getAllNotes());
toggleEmptyNotes();
/**
* On long press on RecyclerView item, open alert dialog
* with options to choose
* Edit and Delete
* */
recyclerView.addOnItemTouchListener(new
RecyclerTouchListener(this,
recyclerView, new
RecyclerTouchListener.ClickListener() {
@Override
public void onClick(View view, final int position) {
}
@Override
public void onLongClick(View view, int position) {
showActionsDialog(position);
}
}));
20
/**
* Inserting new note in db
* and refreshing the list
*/
private void createNote(String note) {
// inserting note in db and getting
// newly inserted note id
long id = db.insertNote(note);
if (n != null) {
// adding new note to array list at 0 position
notesList.add(0, n);
toggleEmptyNotes();
}
}
/**
* Updating note in db and updating
* item in the list by its position
*/
private void updateNote(String note, int position) {
Note n = notesList.get(position);
// updating note text
n.setNote(note);
// updating note in db
db.updateNote(n);
/**
* Deleting note from SQLite and removing the
* item from the list by its position
*/
private void deleteNote(int position) {
// deleting the note from db
db.deleteNote(notesList.get(position));
toggleEmptyNotes();
21
}
/**
* Opens dialog with Edit - Delete options
* Edit - 0
* Delete - 0
*/
private void showActionsDialog(final int position) {
CharSequence colors[] = new CharSequence[]{"Edit",
"Delete"};
22
if (shouldUpdate && note != null) {
inputNote.setText(note.getNote());
}
alertDialogBuilderUserInput
.setCancelable(false)
.setPositiveButton(shouldUpdate ? "update" :
"save", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialogBox,
int id) {
}
})
.setNegativeButton("cancel",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface
dialogBox, int id) {
dialogBox.cancel();
}
});
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListen
er(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Show toast message when no text is entered
if
(TextUtils.isEmpty(inputNote.getText().toString())) {
Toast.makeText(MainActivity.this, "Enter
note!", Toast.LENGTH_SHORT).show();
return;
} else {
alertDialog.dismiss();
}
/**
* Toggling list and empty notes view
*/
23
private void toggleEmptyNotes() {
// you can check notesList.size() > 0
if (db.getNotesCount() > 0) {
noNotesView.setVisibility(View.GONE);
} else {
noNotesView.setVisibility(View.VISIBLE);
}
}
@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, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_input) {
showNoteDialog(false, null, -1);
return true;
}
return super.onOptionsItemSelected(item);
}
}
Result
Insert Data :
24
Update Data :
Delete Data :
Refrence :
https://www.androidhive.info/2011/11/android-sqlite-database-
tutorial/
25