Building your first app with Kotlin in Android Studio

As of Android Studio 3.0, Kotlin is supported as an official language for Android development. This announcement was made recently at Google I/O 2017 that has been widely welcomed by developers, many of whom were already using Kotlin. Today in this tutorial we’ll make our first Kotlin app using Android Studio 3.0 (See what’s new in Android Studio 3.0). If you’re still using an earlier version of Android Studio you’ve to install the Kotlin Plugin. Go to File -> Settings -> Plugins -> Install JetBrains plugin… and then search for and install Kotlin. You’ll need to restart the IDE after this completes.

Why Kotlin?

Kotlin is a fun, expressive and modern programming language. It’s a statically typed programming language that runs on the JVM and has 100% interoperability with Java. It’s a good alternative to Java that can also work along with Java in parallel in a single Android app. Compared to Java, it is much more concise and it has a stronger null safety, meaning that you can catch potential crashes much earlier in your development cycle. Kotlin has a lot of built-in boilerplate code, making development faster and less error-prone. The Android community has been very excited about Kotlin and I believe that Android will move quickly to Kotlin. So, it will be right to say that;

Kotlin is the Future!

Enough of talk, time to showdown!!



I’m assuming you have the basic idea of Android Development, I recommend that you use Android Studio version 3.0 or higher to write this app.

DOWNLOAD SOURCE CODE FROM GITHUB

Create a New Project

In Android Studio, go to File ⇒ New Project and fill all the details required to create a new project. Check the box that says Include Kotlin support as in below picture. When it prompts to select a default activity, select Blank Activity and proceed.

Kotlin Android & Extention Plugins

Open your app level build.gradle file observe two plugins on top of the file and “kotlin-stdlib” dependency. If you don’t see any Kotlin plugins add below lines to apply the Kotlin android and Kotlin android extension plugin under the android application plugin:

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
    ...
}

The Kotlin Android Extension plugin turns views into properties of the activity, allowing you to reference them directly, without the need for findViewById() calls.

Also if you’re doing manually then you’ve to add following lines of code in Project level gradle file.

buildscript {
    ext.kotlin_version = '1.1.60'
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.0'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

Creating Data Class – Task

Let’s first understand what is data class;data class is a class that only contains state and does not perform any operations.

The advantage of using data classes instead of regular classes is that Kotlin gives us an immense amount of self-generated code. Let’s compare java class with awesome data class of kotlin.

public class TaskJava {
    private long id;
    private String title;
    private String description;
    private Date createdAt;

    public TaskJava(long id, String title, String description, Date createdAt) {
        this.id = id;
        this.title = title;
        this.description = description;
        this.createdAt = createdAt;
    }

    public long getId() {
        return id;
    }

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

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Date getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(Date createdAt) {
        this.createdAt = createdAt;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        TaskJava taskJava = (TaskJava) o;

        if (id != taskJava.id) return false;
        if (title != null ? !title.equals(taskJava.title) : taskJava.title != null) return false;
        if (description != null ? !description.equals(taskJava.description) : taskJava.description != null)
            return false;
        return createdAt != null ? createdAt.equals(taskJava.createdAt) : taskJava.createdAt == null;
    }

    @Override
    public int hashCode() {
        int result = (int) (id ^ (id >>> 32));
        result = 31 * result + (title != null ? title.hashCode() : 0);
        result = 31 * result + (description != null ? description.hashCode() : 0);
        result = 31 * result + (createdAt != null ? createdAt.hashCode() : 0);
        return result;
    }

    @Override
    public String toString() {
        return "TaskJava{" +
                "id=" + id +
                ", title='" + title + '\'' +
                ", description='" + description + '\'' +
                ", createdAt=" + createdAt +
                '}';
    }
}

Although almost all above is generated by IDE still you’ve kept and maintained this code in your project. Here comes the awesomeness of data class, all above code is equivalent to below one line in Kotlin. YEAH!!!! THATS IT!!!  🙂

data class Task(val id: Long, val title: String, var description : String, val createdAt: Date)

Create a new package called model under java folder, create new kotlin class called Task.kt by going to New ⇒ Kotlin File/Class and paste above a single line of code.  With this, we already have a class with its constructor, its immutable properties, and other useful functions like,copy equals or hashCode implemented.


The Adapter

Create another package called adapter and create new kotlin class called TaskAdapter.kt and extend it from RecyclerView.Adapter. It will be somewhat similar to below code;

class TaskAdapter : RecyclerView.Adapter<TaskAdapter.TaskViewHolder>() {

    override fun onBindViewHolder(holder: TaskViewHolder?, position: Int) {
        TODO("not implemented")
    }

    override fun getItemCount(): Int {
        TODO("not implemented")
    }

    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): TaskViewHolder {
        TODO("not implemented")
    }

    class TaskViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
}

Here you’ll see I’ve created a TaskViewHolder that is extended from RecyclerView.ViewHolder. This is because Adapter needs an implementation of the original abstract class.

You may have noticed “?” in parameters of functions that shows that these values can be nullable.  The problem is that any reference coming from Java may be null, and Kotlin, being null-safe by design, forced the user to null-check every Java value, or use safe calls (?.) or not-null assertions (!!). But studying the support library a bit, I noticed that those values aren’t null, so we can remove it. Let’s override these functions as below:

class TaskAdapter(val taskList: List<Task>, val listener: (Task) -> Unit) :
        RecyclerView.Adapter<TaskAdapter.TaskViewHolder>() {

    override fun onBindViewHolder(holder: TaskViewHolder, position: Int) =
            holder.bind(taskList.get(position), listener);

    override fun getItemCount() = taskList.size

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TaskViewHolder {
        val rootView = LayoutInflater.from(parent.context).inflate(R.layout.item_task, parent, false)
        return TaskViewHolder(rootView)
    }

    class TaskViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        fun bind(task: Task, listener: (Task) -> Unit) = with(itemView) {
            tvTitle.text = task.title;
            tvDescription.text = task.description;
            setOnClickListener { listener(task) }
        }
    }
}

Have you noticed the first line? I’ve added a list of tasks and click event listener as a parameter.

Creating Activity & XML Layout

  1. Create an XML layout with your desired name or you can say activity_main.xml under the res ⇒ layout folder.
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.design.widget.CoordinatorLayout 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="com.nthreads.todo.MainActivity">
    
        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/AppTheme.AppBarOverlay">
    
            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:popupTheme="@style/AppTheme.PopupOverlay" />
    
        </android.support.design.widget.AppBarLayout>
    
        <include layout="@layout/content_main" />
    
        <android.support.design.widget.FloatingActionButton
            android:id="@+id/fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|end"
            android:layout_margin="@dimen/fab_margin"
            app:srcCompat="@android:drawable/ic_input_add" />
    
    </android.support.design.widget.CoordinatorLayout>
  2. Add another layout with the name content_main.xml under the res ⇒ layout folder.
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout 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="com.nthreads.todo.MainActivity"
        tools:showIn="@layout/activity_main">
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_marginBottom="8dp"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:layout_editor_absoluteX="8dp"
            tools:layout_editor_absoluteY="8dp" />
    
        <TextView
            android:id="@+id/tvNotTask"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="8dp"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            android:gravity="center"
            android:text="No task found.\nOnce you'll add any task it will be listed here."
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:layout_editor_absoluteX="8dp"
            tools:layout_editor_absoluteY="8dp" />
    
    </android.support.constraint.ConstraintLayout>
  3. Add following code in MainActivity.kt file:
    class MainActivity : AppCompatActivity() {
        
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            setSupportActionBar(toolbar)
    
            val tasksList = ArrayList<Task>();
    
            val task1 = Task(1, "Task 1", "Description of task", Date());
            val task2 = task1.copy(2, "Task 2");
            val task3 = task1.copy(id = 3, title="Task 3", description = "This is 3rd task");
            tasksList.add(task1)
            tasksList.add(task2)
            tasksList.add(task3)
    
            recyclerView.layoutManager = LinearLayoutManager(this);
            recyclerView.adapter = TaskAdapter(tasksList) {
                toast("${it.title} Clicked")
            };
    
            fab.setOnClickListener { view ->
                TODO("Add New Task")
            }
    
            if(tasksList.size > 0) tvNotTask.visibility = View.GONE;
            else tvNotTask.visibility = View.VISIBLE
    
        }
    
        private fun toast(msg: String) = Toast.makeText(this, msg, Toast.LENGTH_LONG).show()
    }

Have you noticed the magic of Kotlin Android Extensions Plugin this automatically import the views created in XML file, so we don’t need to call the findViewById function again and again.

Now run the app and see the magic, your output should be similar as below:

Kotlin TODO App

 

In next tutorial, we’ll take this app to a further level using new architecture component introduced by Google. If you find this post helpful please don’t forget to share it, like it and leave suggestions, criticisms in the comments section below. 🙂


This article has 3 comments

  1. froleprotrem Reply

    I’d have to verify with you here. Which is not something I normally do! I enjoy studying a put up that will make people think. Additionally, thanks for permitting me to comment!

Leave a Reply to froleprotrem Cancel reply

Your email address will not be published. Required fields are marked *