Develop Android apps elegantly with Kotlin

Develop Android apps elegantly with Kotlin

Preface

In the previous article, we briefly learned about the advantages of the new language Kotlin, and also touched upon some common syntax and its simple usage. I believe you will have a strong interest in it. Let's understand it as being interested in it for the time being, hahaha. So, how can we apply this new language in Android? Today's article will show you how to use Kotlin to develop Android applications, and compare it with our traditional language Java, so that you can truly feel its beauty and elegance.

Configuration

Project gradle file

  1. apply plugin: 'com.android.application'  
  2.  
  3. apply plugin: 'kotlin-android'  
  4.  
  5. apply plugin: 'kotlin-android-extensions'  
  6.  
  7.   
  8.  
  9. dependencies {
  10.  
  11. classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.1'  
  12.  
  13. }

app Gradle file:

  1. compile 'org.jetbrains.kotlin:kotlin-stdlib:1.1.1'  
  2.  
  3. compile 'org.jetbrains.anko:anko-sdk25:0.10.0-beta-1' // sdk15, sdk19, sdk21, sdk23 are also available
  4.  
  5. compile 'org.jetbrains.anko:anko-appcompat-v7:0.10.0-beta-1'  

Anko

Through the above configuration, you will find that the dependency of anko is introduced. Anko is a powerful library developed by JetBrains. Speaking of JetBrains, it is awesome. Kotlin language is developed by them, and the most popular development tool Intellij idea is also developed by them. AS is also based on IDEA. Well, let's get back to the topic. Anko is a Kotlin library officially developed by Kotlin to make the development of Android applications faster and easier, and can make the code we write simpler, clearer and easier to read. It includes multiple parts, as follows

  1. Anko Commons: a lightweight library full   of helpers for intents, dialogs, logging and so on ;
  2.  
  3. Anko Layouts: a fast and type-safe way to write dynamic Android layouts;
  4.  
  5. Anko SQLite: a query DSL and parser collection for Android SQLite;
  6.  
  7. Anko Coroutines: utilities based on the kotlinx.coroutines library

Next, let's understand the advantages of using Kotlin language to develop Android through code.

No more findViewById

Anyone who has done Android development knows that if you write too many layout files, findViewById is also a lot of work, and you have to declare the variable first, then findViewById and then force it into your control. The usage is generally as follows

  1. TextView username;
  2.  
  3. username=(TextView)findViewById(R.id. user );
  4.  
  5.   
  6.  
  7. username.setText( "I am a TextView" );

Sometimes I feel like vomiting when writing this. Some people may say that there are some annotation libraries now, such as butterknife. When we use annotations, we don’t need to findViewById. The usage is as follows

  1. @BindView(R.id. user )
  2.  
  3. TextView username;
  4.  
  5. username.setText( "I am a TextView" );

That's true. Using annotations does save us some work, but it's still not the simplest solution. The simplest solution is to assign a value to the control with the id user. You may find this a bit unbelievable. But Kotlin does it. We can write it like this:

  1. user .text = "I am a TextView"  

Do you feel like you've seen it too late? It's so concise. user is the id declared in our layout file, and .text is like setText(). In Kotlin, we don't see set/get methods like in Java. It should be noted that when we want to use it like this (without findViewById, directly use the XML control, we need to add apply plugin: 'kotlin-android-extensions' in gradle), we need to add the following line of code

  1. //activity_login is our layout
  2.  
  3. import kotlinx.android.synthetic.main.activity_login.*

Anko Layout

Usually we use XML files to write our layouts, but it has some disadvantages such as not type-safe, not null-safe, parsing XML files consumes more CPU and power, etc. Anko Layout can use DSL to dynamically create our UI, and it is much more convenient than using Java to dynamically create layouts. It is mainly more concise, and it has a hierarchical relationship with XML to create layouts, which makes it easier for us to read.

  1. verticalLayout {
  2.  
  3. val textView = textView ( "I am a TextView" )
  4.  
  5. val name = editText( "EditText" )
  6.  
  7. button( "Button" ) {
  8.  
  9. onClick { toast( "${name.text}!" ) }
  10.  
  11. }
  12.  
  13. }

We can remove setContentView in the OnCreate method, and then add the above code to display the effect as shown below, that is, a vertical linear layout with a TextView, an EditText, and a Button. And the Button has a click event, when clicked, the content of the EditText is displayed.

Displayed as a toast.

The above code is very simple and easy to understand. Of course, the default controls cannot meet our needs. For example, we need to change the color and size of the font, set the width and height, and set the margin and padding values. So how to implement it? Of course, it is also very simple, because its logic is the same as the XML writing layout. For example, the following implementation

  1. val textView = textView ( "I am a TextView" ) {
  2.  
  3. textSize = sp(17).toFloat()
  4.  
  5. textColor=context.resources.getColor(R.color.red)
  6.  
  7. }.lparams{
  8.  
  9. margin=dip(10)
  10.  
  11. height= dip(40)
  12.  
  13. width= matchParent
  14.  
  15. }

I think I don't need to explain the above code, you should be able to see the effect of the control, because its attributes correspond to the names of the attributes we set in XML.

In the process of creating the UI above, we directly wrote the code for creating the UI in the onCreate method. Of course, there is another way to write it. We create an inner class to implement the AnkoComponent interface and override the createView method, which returns a View, which is the layout we created. Modify it as follows

  1. inner class UI : AnkoComponent<LoginActivity> {
  2.  
  3. override fun createView(ui: AnkoContext<LoginActivity>): View {
  4.  
  5. return   with (ui){
  6.  
  7. verticalLayout {
  8.  
  9. val textView = textView ( "I am a TextView" ) {
  10.  
  11. textSize = sp(17).toFloat()
  12.  
  13. textColor=context.resources.getColor(R.color.red)
  14.  
  15. }.lparams{
  16.  
  17. margin=dip(10)
  18.  
  19. height= dip(40)
  20.  
  21. width= matchParent
  22.  
  23. }
  24.  
  25. val name = editText( "EditText" )
  26.  
  27. button( "Button" ) {
  28.  
  29. onClick { view ->
  30.  
  31. toast( "Hello, ${name.text}!" )
  32.  
  33. }
  34.  
  35. }
  36.  
  37. }
  38.  
  39. }
  40.  
  41. }
  42.  
  43. }

Then add a line of code in the onCreate method to create our layout page.

  1. UI().setContentView(this@LoginActivity)

Now we compile and run, and we find that the effect is the same as the interface written in the layout file. However, its performance is advantageous, but I didn't find the performance advantage. Anyway, this DSL is indeed easy to read and easy to use. In the above code, you may have noticed dip(10), which means converting 10dp to pixels. It is an extension function of Anko. If you have read the source code of Anko, you will find that there are a lot of extension functions in it, which is also one of the advantages of the Kotlin language. It is indeed very powerful, for example, dip extension (extract View extension)

  1. inline fun View .dip(value: Int ): Int = context.dip(value)
  2.  
  3. fun Context.dip(value: Int ): Int = (value * resources.displayMetrics.density).toInt()

In the above, resources.displayMetrics.density has the same effect as our Java getResources().getDisplayMetrics().density, but looking at it, you may feel more comfortable than writing in Java. At least, that's how I feel.

In the above, we added a click event to Button, and we found that it supports lambda expressions. If we want to display a Toast, we only need toast("content"), which is very concise. In fact, it is also an extension function.

  1. inline fun AnkoContext<*>.toast(message: CharSequence) = ctx.toast(message)
  2.  
  3. fun Context.toast(message: CharSequence) = Toast.makeText(this, message, Toast.LENGTH_SHORT).show()

Of course, creating a dialog is still very simple, as follows

  1. alert ( "I am Dialog" ) {
  2.  
  3. yesButton { toast( "yes" )}
  4.  
  5. noButton { toast( "no" )}
  6.  
  7. }.show()

The more I read it, the more comfortable I feel, haha. Here is another powerful, simple, and concise code implementation.

  1. doAsync {
  2.  
  3. //Background execution code
  4.  
  5. uiThread {
  6.  
  7. //UI thread
  8.  
  9. toast( "Thread${Thread.currentThread().name}" ) }
  10.  
  11. }

This code implements the effect of AsyncTask, but you should find it much simpler than the Java implementation. Of course, unless you are color blind, you will notice the simplicity.

If you use Kotlin to develop Android for a while, you will find that it reduces a lot of code for us. Of course, more advantages and uses need to be explored by ourselves. I believe it will surprise you after exploration.

Implement a simple login interface

The interface is very simple, pseudo code

  1. <LinearLayout>
  2.  
  3.   
  4.  
  5. <ImageView/>
  6.  
  7.   
  8.  
  9. <LinearLayout> <ImageView/><EditText account/><LinearLayout>
  10.  
  11.   
  12.  
  13. <LinearLayout> <ImageView/><EditText password/><LinearLayout>
  14.  
  15.   
  16.  
  17. <Button Login/>
  18.  
  19.   
  20.  
  21. <LinearLayout> <CheckBox Remember password/><TextView Privacy Agreementxieu/><LinearLayout>
  22.  
  23.   
  24.  
  25. <TextView/>
  26.  
  27.   
  28.  
  29. </LinearLayout>

It doesn't look complicated, so I won't post the XML implementation code here. If you want to see the XML implementation, you can click to check it out. Next, let's just look at how Anko implements this layout in Kotlin code.

  1. lateinit var et_account: EditText
  2.  
  3. lateinit var et_password: EditText
  4.  
  5. inner class LoginUi : AnkoComponent<LoginActivity> {
  6.  
  7. override fun createView(ui: AnkoContext<LoginActivity>) = with (ui) {
  8.  
  9. verticalLayout {
  10.  
  11. backgroundColor = context.resources.getColor(android.R.color.white)
  12.  
  13. gravity = Gravity.CENTER_HORIZONTAL
  14.  
  15. imageView(R.mipmap.ic_launcher).lparams {
  16.  
  17. width = dip(100)
  18.  
  19. height = dip(100)
  20.  
  21. topMargin = dip(64)
  22.  
  23. }
  24.  
  25.   
  26.  
  27. linearLayout {
  28.  
  29. gravity = Gravity.CENTER_VERTICAL
  30.  
  31. orientation=HORIZONTAL
  32.  
  33. backgroundResource = R.drawable.bg_frame_corner
  34.  
  35. imageView {
  36.  
  37. image = resources.getDrawable(R.mipmap.ic_username)
  38.  
  39. }.lparams(width = wrapContent, height = wrapContent) {
  40.  
  41. leftMargin = dip(12)
  42.  
  43. rightMargin = dip(15)
  44.  
  45. }
  46.  
  47. et_account = editText {
  48.  
  49. hint = "Login account"  
  50.  
  51. hintTextColor = Color.parseColor( "#666666" )
  52.  
  53. textSize = 16f
  54.  
  55. background = null  
  56.  
  57. }
  58.  
  59. }.lparams(width = dip(300), height = dip(40)) {
  60.  
  61. topMargin = dip(45)
  62.  
  63. }
  64.  
  65.   
  66.  
  67. linearLayout {
  68.  
  69. orientation=HORIZONTAL
  70.  
  71. backgroundResource = R.drawable.bg_frame_corner
  72.  
  73. gravity = Gravity.CENTER_VERTICAL
  74.  
  75. imageView {
  76.  
  77. image = resources.getDrawable(R.mipmap.ic_password)
  78.  
  79. }.lparams {
  80.  
  81. leftMargin = dip(12)
  82.  
  83. rightMargin = dip(15)
  84.  
  85. }
  86.  
  87. et_password = editText {
  88.  
  89. hint = "Login password"  
  90.  
  91. hintTextColor = Color.parseColor( "#666666" )
  92.  
  93. textSize = 16f
  94.  
  95. background = null  
  96.  
  97. }
  98.  
  99. }.lparams {
  100.  
  101. width = dip(300)
  102.  
  103. height = dip(40)
  104.  
  105. topMargin = dip(10)
  106.  
  107.   
  108.  
  109. }
  110.  
  111.   
  112.  
  113. button( "Login" ) {
  114.  
  115. gravity = Gravity.CENTER
  116.  
  117. background = resources.getDrawable(R.drawable.bg_login_btn)
  118.  
  119. textColor = Color.parseColor( "#ffffff" )
  120.  
  121. onClick {
  122.  
  123. if (et_account.text.toString().isNotEmpty() && et_password.text.toString().isNotEmpty())
  124.  
  125. startActivity<MainActivity>() else toast( "Please enter your account or password" )
  126.  
  127. }
  128.  
  129. }.lparams(width = dip(300), height = dip(44)) {
  130.  
  131. topMargin = dip(18)
  132.  
  133. }
  134.  
  135. linearLayout {
  136.  
  137. orientation=HORIZONTAL
  138.  
  139. gravity = Gravity.CENTER_VERTICAL
  140.  
  141. checkBox( "Remember password" ) {
  142.  
  143. textColor = Color.parseColor( "#666666" )
  144.  
  145. textSize = 16f
  146.  
  147. leftPadding = dip(5)
  148.  
  149. }
  150.  
  151. textView( "Privacy Agreement" ) {
  152.  
  153. textColor = Color.parseColor( "#1783e3" )
  154.  
  155. gravity = Gravity. RIGHT  
  156.  
  157. textSize = 16f
  158.  
  159. }.lparams(width = matchParent)
  160.  
  161. }.lparams(width = dip(300)) {
  162.  
  163. topMargin = dip(18)
  164.  
  165. }
  166.  
  167.   
  168.  
  169. textView( "Copyright © Code4Android" ) {
  170.  
  171. textSize = 14f
  172.  
  173. gravity = Gravity.CENTER or Gravity.BOTTOM
  174.  
  175.   
  176.  
  177. }.lparams {
  178.  
  179. bottomMargin = dip(35)
  180.  
  181. weight=1f
  182.  
  183. }
  184.  
  185. }
  186.  
  187. }
  188.  
  189. }

How about the code above? It looks good, right? Even if you can't write it now, you can still understand it. In the above, we set an event for the login button to open MainActivity. The Activity we want to jump to is written in startActivity. If you want to pass parameters to the opened interface, write them directly in (). For example, if we pass the input account and password to the jump interface, it is implemented as

  1. startActivity<MainActivity>( "account"   to et_account.text.toString(), "password"   to et_password.text.toString())

In fact, Anko's power is far more than this, and it is worth our careful appreciation. If you want to learn more, you can go to GitHub Anko (https://github.com/Kotlin/anko). This is the end of this article. If you find any mistakes while reading, please point them out. Thank you. Have a wonderful day.

<<:  Android Developer: Why I Switched to Kotlin

>>:  TensorFlow implements image completion based on deep learning

Recommend

Find out! Grass carp originated in western China 33 million years ago

Grass carp is one of the four major freshwater ca...

Product operation from 0 to 1: How to obtain seed users?

This article mainly focuses on the practical part...

Baidu Netdisk Super 0 Tutorial Course Materials

Baidu Netdisk Super 0 Tutorial Course Materials 1...

Analysis of product life cycle model!

How do you view the product life cycle ? Insights...

Which one is more important, conversion or traffic?

Why conversion is more important than traffic Fro...

Urticaria is common in spring. How to get rid of it?

The weather is getting warmer and the days are ge...

How to turn users who hate your product into the most loyal ones

During my short 5-year operation career, I spent ...

Mozilla CTO publicly blames Android and iOS

[[122515]] In a report provided to The Guardian, ...

More than 20% of users are still using third-party Android ROMs

Compared with the closed system of Apple iOS, And...