• 5 Kotlin Extensions To Make Your Android Code More Expressive
  • Originally written by Siva Ganesh Kantamani
  • The Nuggets translation Project
  • Permanent link to this article: github.com/xitu/gold-m…
  • Translator: keepmovingljzy
  • Proofread with: PassionPenguin

5 Kotlin extension tips to make your Android code more expressive

You’ve probably seen a bunch of articles about Kotlin’s extensions, but this one isn’t just about extensions. It’s all about making your code more expressive, so I’m going to cover and outline my top extensions to make your code as natural as possible.

The main purpose of this article is to learn how to use extensions, rather than copying snippets of code, to express code in your own way.

introduce

Kotlin is a modern, expressive language that was built for developers. In my opinion, good Kotlin code is code that expresses itself in a natural, readable way.

Switching from Java to Kotlin was a huge shift for me in many ways, but I think it worked out for the best in every way. You can refer to my previous post.

What I like most about Kotlin is the extension. As a Mobile Java developer, I would never dream of adding custom functionality to any class, especially a class in a third-party library. But when I heard about the extension concept, it really blew my mind. For Android developers, this feature opens the door to a lot of code enhancements.

“Kotlin provides the ability to extend classes with new functionality without having to inherit the class or use design patterns like decorators. This is done through special declarations called extensions.” – Kotlin document

If you want to learn more about Kotlin extensions, read this article: Advanced Android Programming with Kotlin

I used the Kotlin extension to make the code more expressive and to make the language as natural as possible.

Stop procrastinating and start working!

1. Show, Hide, Remove (view)

One of the most common tasks for mobile developers is to hide and show views. If you use Java, you’ll need to call the setVisibility method and pass in view. VISIBILE or view. GONE. As follows:

view.setVisibility(View.GONE)
Copy the code

The code works and there are no problems. But using the set and get methods makes it look clunky and unnatural, and Kotlin provides a convenient way to assign values without using the set and get methods. The existing code is as follows:

view.visibility = View.GONE
Copy the code

Even now, it doesn’t look natural because of the assignment operator, so I thought, “Why don’t I use extensions to make it as natural as possible?” At this point I started using the following extensions:

fun View.show(a){
    this.visibility = View.VISIBLE
}

fun View.hide(a) {
    this.visibility = View.INVISIBLE
}

fun View.remove(a){
    this.visibility = View.GONE
}
Copy the code

Now you can use:

view.show()
view.hide() 
view.remove()
Copy the code

Now it looks friendlier and more natural. I’m happy to optimize it, so if you have any suggestions, please leave a comment.

2. Check

Validating strings is critical in any development environment. Back in 2015, when I first started my career, I saw some applications showing NULL in some text fields. This is because there is no proper verification.

Before using Kotlin, I maintained a utility class with some static functions to validate strings. Consider a simple validation function in Java:

// Function in utility class
public static Boolean isStringNotNullOrEmpty(String data){
    returndata ! =null && data.trim().length() > 0 
            && !data.equalsIgnoreCase("null");
}

// Usage at call site
if(Utility.isStringNotNullOrEmpty(data))
Copy the code

Data types are essentially just classes. So we can use the Kotlin extension to add validation. For example, I created the following Kotlin extension with a data type of String to check if it is valid:

//Extension function
funString? .valid(a) : Boolean =
        this! =null&&!this.equals("null".true)
                && this.trim().isNotEmpty()

// Usage at call site
if(data.valid())
Copy the code

Obviously, data. Valid () looks better than the Utility. IsStringNotNullOrEmpty (data) more concise, more readable. Calling extension functions on data types seems more natural than firing some utility class functions. Here are a few extensions to inspire you to write your own validation extensions:

//Email Validation
fun String.isValidEmail(a): Boolean
  = this.isNotEmpty() && Patterns.EMAIL_ADDRESS.matcher(this).matches()

//Phone number format
fun String.formatPhoneNumber(context: Context, region: String): String? {
    val phoneNumberKit = PhoneNumberUtil.createInstance(context)
    val number = phoneNumberKit.parse(this, region)
    if(! phoneNumberKit.isValidNumber(number))return null

    return phoneNumberKit.format(number, PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)
}
Copy the code

3. Extract the Bundle parameters

In Android, we pass data between components by binding a key-value pair. There are usually a few things we have to check before retrieving data from the bundle. First, we should check whether the key we are looking for is in the bundle. Then we need to check that it has a valid value. The usual practices are as follows:

fun extractData(extras : Bundle){
    if (extras.containsKey("name") && extras.getString("name").valid()){
        val name = extras.getString("name")}}Copy the code

This involves more hand-written code, and frankly, it doesn’t look pretty. Imagine how boring your code would look if you had five arguments. As I said, the code should be as natural as possible and require minimal manual calls.

Here, I use four extension functions: two for activities and two for fragments. Again, provide a pair for each component to obtain either a non-null value or a nullable value. As follows:

// Activity related
inline fun <reified  T : Any> Activity.getValue(
    lable : String, defaultvalue : T? = null) = lazy{
    valvalue = intent? .extras? .get(lable)
    if (value is T) value else defaultvalue
}

inline fun <reified  T : Any> Activity.getValueNonNull(
    lable : String, defaultvalue : T? = null) = lazy{
    valvalue = intent? .extras? .get(lable)
    requireNotNull((if (value is T) value else defaultvalue)){lable}
}

// Fragment related
inline fun <reified T: Any> Fragment.getValue(lable: String, defaultvalue: T? = null) = lazy {
    valvalue = arguments? .get(lable)
    if (value is T) value else defaultvalue
}

inline fun <reified T: Any> Fragment.getValueNonNull(lable: String, defaultvalue: T? = null) = lazy {
    valvalue = arguments? .get(lable)
    requireNotNull(if (value is T) value else defaultvalue) { lable }
}
Copy the code

For advanced features such as inline functions and concrete types, refer to this series of articles.

Now let’s see how to use the extension above:

val firstName by getValue<String>("firstName") // String?
val lastName by getValueNonNull<String>("lastName") // String
Copy the code

This approach has three advantages:

  1. Concise, readable, less code.
  2. An empty safe.
  3. Lazy loading.

4. Resource expansion

In Android, we need to access project resources through resource classes. This involves some boilerplate code that needs to be written manually every time you need to retrieve data from a resource file. If there are no extensions, retrieve color or drawable code as follows:

val color = ContextCompat.getColor(ApplicationCalss.instance, R.color.dark_blue)
val drawable = ContextCompat.getDrawable(MavrikApplication.instance, R.drawable.launcher)
Copy the code

When you try to get any resource, you need to access it through the resource ID in the generated R file. The data type of ID is Int. Therefore, we can write extensions to the INTEGER class for each resource type and use them to reduce boilerplate code and increase readability:

//Extensions
fun Int.asColor(a) = ContextCompat.getColor(ApplicationCalss.instance, this)
fun Int.asDrawable(a) = ContextCompat.getDrawable(MavrikApplication.instance, this)

//Usage at call site
val color = R.color.dark_blie.asColor()
val drawable = R.drawable.launcher.asDrawable()
Copy the code

5. Display Alert Dialog, Toast, or Snackbar

When it comes to front-end development, no matter what platform you use, you sometimes need to show the user a pop-up. It may be used to display unimportant data, or a pop-up prompts the user to confirm or show some errors.

When you want to display a simple pop-up message, the code you need to write can be very long. You’re not even going to pop up a dialog. These are common scenarios. They should be concise and easy to implement. That’s why I use the following extension to make the code call as cleanly as possible:

// Show alert dialog
fun Context.showAlertDialog(positiveButtonLable : String = getString(R.string.okay),
                            title : String = getString(R.string.app_name) , message : String,
                               actionOnPositveButton : () -> Unit) {
    val builder = AlertDialog.Builder(this)
            .setTitle(title)
            .setMessage(message)
            .setCancelable(false)
            .setPositiveButton(positiveButtonLable) { dialog, id ->
                dialog.cancel()
                actionOnPositveButton()
            }
    valalert = builder.create() alert? .show() }// Toash extensions
fun Context.showShotToast(message : String){
    Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}

fun Context.showLongToast(message : String){
    Toast.makeText(this, message, Toast.LENGTH_LONG).show()
}

// Snackbar Extensions
fun View.showShotSnackbar(message : String){
    Snackbar.make(this, message, Snackbar.LENGTH_SHORT).show()
}

fun View.showLongSnackbar(message : String){
    Snackbar.make(this, message, Snackbar.LENGTH_LONG).show()
}

fun View.snackBarWithAction(message : String, actionlable : String,
                            block : () -> Unit){
    Snackbar.make(this, message, Snackbar.LENGTH_LONG)
            .setAction(actionlable) {
                block()
            }
}
Copy the code

Writing these extensions is a one-time effort. See how these extensions are used:

// To show an alert dialog in activities, fragments and more
showAlertDialog(message){
  //TODO on user click on positive button on alert dialog
}

//To show toast in activities, fragments and more
showShotToast(message)
showLongToast(message)

//To show Snackbar applied on any active view
showShotSnackbar(message)
showLongSnackbar(message)
snackBarWithAction(message, lable){
  //TODO on user click on snackbar action lable
}
Copy the code

Common scenarios should be as easy to implement, readable, and natural as possible.

I hope you learned something useful. Thank you for reading.

If you find any mistakes in your translation or other areas that need to be improved, you are welcome to the Nuggets Translation Program to revise and PR your translation, and you can also get the corresponding reward points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.


The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.