Simple Todo list app using Room and kotlin.
Repository
https://github.com/Emeecodes01/todolist-app
What Will I Learn?
From this tutorial.
- You will learn how to use room database.
- You will learn how to create simple android UI in xml.
- You will learn how to use kotlin for android development.
- You will do actual practice with the knowledge gained.
- At the end you will learn how to create a simple todo list app in Kotlin.
Requirements
The requirements for this tutorial are
- Android studio IDE must be installed.
- You must have programming knowledge and must be comfortable working with kotlin programming.
Difficulty
- Intermediate
Tutorial Contents
In this tutorial i am going to extensively cover how to use room database, the best practices in android development and kotlin programming.
Firstly, what is Room and why Room?
Room is an abstraction layer over SQlite database to allow fluent database access..
Traditionally, using database in your android app require lot of efforts in writing error prone boiler plate code, but with room, you can get your database up and running with just few lines of code.Room provides you with the following benefits
- Unlike Sqlite, they require less time and effort to create
- Errors are checked at compile time, this makes it possible to avoid unexpected crashes
Add Room Library dependency in your app level build.gradle file.
def room_version = "1.1.0"
implementation "android.arch.persistence.room:runtime:$room_version"
kapt "android.arch.persistence.room:compiler:$room_version"
Note: do not forget to include the kotlin annotation processor plugin apply plugin: 'kotlin-kapt'
So, In this tutorial we are going to build a simple todo list app that allows users add a todo.
Room components
- An Entity class: This represents a database table in room, they are created by annotating the class with @Entity
- A Dao class: This is an interface annotated with @Dao, it represents various operation that you can perform on a database such as insert, update, delete, and query
- A Database class: This is an abstract class annotated with @Database, this class creates the database.
I will start by creating an Entity class, in Kotlin, you can create a data class and then annotate it with @Entity
Hint: A data class is a class that is solely made to hold your data, it automatically override your hashcode, toString etc..So this makes your code cleaner and shorter which enhances readability.
@Entity(tableName = "todo")
data class Todo(@PrimaryKey(autoGenerate = true) val tId: Int = 0,
@ColumnInfo(name = "todo_title")
val title:String = "",
@ColumnInfo(name = "todo_priority")
val priority: Int = 0){
val detail: String = ""
By default the table name is the name of the class, however you can also pass a table name as a parameter.
The next step is to create a DAO(data access object), this is an interface annotated with @Dao and it represents the various operations you can do on a database(Insert, delete, update and query).
@Dao
interface TodoDao{
/**
* SELECT -> This retrieve rows from a table in a database
* FROM -> You specify the table to retrieve the rows from
* ORDER BY -> This is just a sort algorithm
* ASC -> Ascending order
* WHERE -> This is a condition used to query data
* */
@Query("SELECT*FROM todo ORDER BY tId ASC")
fun getTodoList(): List<Todo>
@Query("SELECT*FROM todo WHERE tId=:tid")
fun getTodoItem(tid: Int)
/**
* @param todo is what we want to save in our database
* so many conflict can occur when a data is to be saved, the strategy is used to handle such conflicts
* Abort -> this aborts the transaction
* Ignore -> this ignores and continues the transaction
* Replace -> this replace the data
* others includes fail, and roolback
* */
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun saveTodo(todo: Todo)
@Update
fun updateTodo(todo: Todo)
@Delete
fun removeTodo(todo: Todo)
}
Once our DAO is ready we can now create our database class.
You can do this by annotating a class with @Database and the pass some parameters, the most important parameters to pass in are entities(this is an array of entity used in the database), the version, an optional parameter is the exportSchema which can be set to true or false.
TypeConverter annotation can also be applied, they are used to convert any class into another class that can be accepted in the database.
Note: When creating your database it is important to use the singleton design pattern, this ensures that only one instance is created in the whole application..the code snippet below explains that.
//You can also check out type converters
@Database(entities = [Todo::class], version = 1, exportSchema = true)
abstract class TodoListDatabase: RoomDatabase(){
/**
* This is an abstract method that returns a dao for the Db
* */
abstract fun getTodoDao(): TodoDao
/**
* A singleton design pattern is used to ensure that the database instance created is one
* */
companion object {
val databaseName = "tododatabase"
var todoListDatabase: TodoListDatabase? = null
fun getInstance(context: Context): TodoListDatabase?{
if (todoListDatabase == null){
todoListDatabase = Room.databaseBuilder(context,
TodoListDatabase::class.java,
TodoListDatabase.databaseName)
.allowMainThreadQueries()//i will remove this later, database are not supposed to be called on main thread
.build()
}
return todoListDatabase
}
}
}
Note: You will also observe that i called allowMainThreadQueries method when creating the database, this is not a good practice, operations such as querying a database are not supposed to be done on the main thread, however for the sake of this tutorial and to simplify things we are going to so that..In my next tutorial i will explain explain multi-threading and how to do it using Android architectural components..
Finally we can now use our database in our application..
The layout for the app will be as simple as possible
activity_main.xml file:
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"
tools:context=".Todo.TodoActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/todo_rv"
android:layout_width="368dp"
android:layout_height="495dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:src="@drawable/ic_add"
app:fabSize="normal"
android:id="@+id/add_todo"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</android.support.constraint.ConstraintLayout>
The layout for each item in the recyclerVeiw
<?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:padding="5dp"
android:layout_height="80dp">
<android.support.v7.widget.CardView
app:cardCornerRadius="5dp"
android:layout_centerVertical="true"
app:cardElevation="3dp"
app:cardPreventCornerOverlap="true"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<FrameLayout
android:id="@+id/frameLayout"
android:layout_width="50dp"
android:layout_height="50dp">
<ImageView
android:id="@+id/priority_imgView"
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"
tools:text="A"
android:textSize="20sp"
android:textStyle="bold"
android:id="@+id/first_letter"
android:textColor="@color/white" />
</FrameLayout>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
tools:text="Todo goes in here"
android:id="@+id/title"
android:ems="1"
android:maxLines="1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/frameLayout"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
</android.support.v7.widget.CardView>
</RelativeLayout>
Also we need a layout to add todo, this should be open when a user clicks the add button
here the user can enter the title, details, set priority level(low, medium or high).
A user can edit a todo item by either clicking it or long clicking it.
Also, a user can delete a todo item by long clicking the item and then selecting the delete option from the dialog that appears.
In your Activities you can get an instance of the database by using this line of code:
todoDatabase = TodoListDatabase.getInstance(this)
you can now get the dao instance which is then used to perform database operations
//to get the list of todos
todoDatabase?.getTodoDao()?.getTodoList()
//to add a todo
todoDatabase!!.getTodoDao().saveTodo(todo)
//to update a todo
todoDatabase!!.getTodoDao().updateTodo(todo)
I will end this tutorial here, hope it helps you, if you have any questions you can leave it in the comment..
Thank you for your contribution.
Need help? Write a ticket on https://support.utopian.io/.
Chat with us on Discord.
[utopian-moderator]
When you say "try to find something more innovative" what exactly do you mean..
this post is a tutorial and i intend to add more parts to it..such as using live data etc
Besides every tutorial have a different approach, so i dont think there is supposed to be a limit to the numbers of tutorials in a particular topic..
Every tutorial takes time and effort to bring together.
Congratulations @emeecodez! You have completed some achievement on Steemit and have been rewarded with new badge(s) :
Click on the badge to view your Board of Honor.
If you no longer want to receive notifications, reply to this comment with the word
STOPDo not miss the last post from @steemitboard!
Participate in the SteemitBoard World Cup Contest!
Collect World Cup badges and win free SBD
Support the Gold Sponsors of the contest: @good-karma and @lukestokes
Congratulations @emeecodez! You received a personal award!
You can view your badges on your Steem Board and compare to others on the Steem Ranking
Vote for @Steemitboard as a witness to get one more award and increased upvotes!