当前位置:网站首页>Kotlin practical skills you should know

Kotlin practical skills you should know

2022-06-23 17:54:00 Be a happy yard farmer

Preface

as everyone knows ,kotlin yes google To replace by pushing java Of android development language

kotlin Easy to use , At the same time, there are many grammatical sugars

This article mainly explains some practical kotlin skill

Custom rounded rectangle

In the project , We often have to define rounded rectangular backgrounds , It is generally used to customize drawable Realized

However, the background and rounded corners of rounded rectangles often change slightly , And once it changes, we have to create a new one drawable file

This will cause the problem of file explosion

We can use kotlin The extension function of , To achieve a simple and convenient rounded rectangular background

fun View.setRoundRectBg(color: Int = Color.WHITE, cornerRadius: Int = 15.dp) {
    background = GradientDrawable().apply {
        setColor(color)
        setCornerRadius(cornerRadius.toFloat())
    }
}

For those who need to customize the background View, Call directly setRoundRectBg that will do , Easy and convenient

reified Use

reified,kotlin Generic materialization keyword in , Make abstract things more concrete or real .

Let's give two examples to see how to use reified

startActivity Example

We generally startActivity It's written like this

startActivity(context, NewActivity::class.java)  

We make use of reified Define an extension function

// Function
inline fun <reified T : Activity> Activity.startActivity(context: Context) {
    startActivity(Intent(context, T::class.java))
}

// Caller
startActivity<NewActivity>(context)

Use reified, Simplify generic parameters by adding type passing

In this way, there is no need to manually pass generic types

Gson Analyze the example

Let's first look at what we normally use gson analysis json How it's done

stay Java Serialization Library ( Such as Gson) in , When you want to deserialize the JSON When the string , You must eventually Class Object as a parameter , In order to Gson Know the type you want .

User user = new Gson().fromJson(getJson(), User.class)

Now? , Let's show reified The magic of type materialization parameters We will create a very lightweight extension function to wrap Gson Method :

inline fun <reified T> Gson.fromJson(json: String) = 
        fromJson(json, T::class.java)

Now? , In our Kotlin In the code , We can deserialize JSON character string , You don't even need to pass type information at all !

val user: User = Gson().fromJson(json)

Kotlin Infer the type from its usage - Because we assign it to User Variable of type ,Kotlin Use it as fromJson() Type parameter of

kotin Interface support SAM transformation

What is? SAM transformation ? Some students may not know much about , Let's do some science popularization here :

SAM transformation , namely Single Abstract Method Conversions, For a transformation that has only a single non default abstract method interface —— For interfaces that meet this condition ( be called SAM Type ), stay Kotlin Can be used directly in Lambda To express —— Of course, the premise is Lambda The function type represented by can match the method in the interface .

stay Kotlin1.4 Before ,Kotlin It is not supported Kotlin Of SAM The conversion , Only support Java SAM transformation , The official explanation is : yes Kotlin It already has function types and higher-order functions , You don't have to go SAM conversion . Developers don't buy this explanation , If you used Java Lambda and Fuction Interface. When you switch to Kotlin when , Will be very confused . It seems Kotlin Is aware of this , Or see feedback from developers , Finally, I support it .

stay 1.4 Before , Only one object can be passed , It is not supported Kotlin SAM Of , And in the 1.4 after , Can support Kotlin SAM, But the usage has changed a bit .interface Need to use fun Keyword declaration . Use fun Keyword marks the interface , Just take this kind of interface as a parameter , It can be lambda Pass as a parameter .

//  Attention needs fun  Keyword declaration 
fun interface Action {
    fun run()
}

fun runAction(a: Action) = a.run()

fun main(){
	// 1.4 Before , Only use object
    runAction(object : Action{
        override fun run() {
            println("run action")
        }
    })
     // 1.4-M1 Support SAM,OK
    runAction {
        println("Hello, Kotlin 1.4!")
    }
}

entrust

occasionally , The way to do some work is to delegate it to someone else . This is not to suggest that you entrust your work to your friends , It's about delegating the work of one object to another .

Of course , Delegation is not a new term in the software industry . entrust (Delegation) It's a design pattern , In this mode , Object delegates an assistant (helper) Object to handle the request , This helper object is called a proxy . The agent is responsible for processing the request on behalf of the original object , And make the results available to the original object .

Class delegation

for instance , When we want to implement an enhanced version of ArrayList, Support to restore the last deleted item

One way to implement this use case , It's inheritance ArrayList class . Because the new class inherits the concrete ArrayList Class, not implementation MutableList Interface , So it's with ArrayList The realization of high coupling .

If you just need to cover remove() Function to keep references to deleted items , And will MutableList The rest of the empty implementation is delegated to other objects , How nice that should be . In order to achieve this goal ,Kotlin Provides a way to delegate most of the work to an internal ArrayList Instance, and you can customize the way it behaves , A new keyword is introduced for this purpose : by.

<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
class ListWithTrash <T>(private val innerList: MutableList<T> = ArrayList<T>()) : MutableCollection<T> by innerList {
	var deletedItem : T? = null
	override fun remove(element: T): Boolean {
	       deletedItem = element
			return innerList.remove(element)
	}
	fun recover(): T? {
		return deletedItem
	}
}

by Keyword telling Kotlin take MutableList The function of the interface is delegated to a innerList Internal ArrayList. By bridging to the inside ArrayList The way object methods ,ListWithTrash Still supported MutableList All functions in the interface . meanwhile , Now you can add your own behavior .

Property delegate

Except for class proxies , You can also use it by Keyword for attribute proxy . By using attribute proxies , The proxy class is responsible for handling the corresponding properties get And set Function call . This feature is used when you need to reuse between other objects getter/setter Logic is very useful , At the same time, you can easily expand the function of simple support fields

for instance , Delegate attributes can be used to encapsulate SharedPreference

Delegating data store operations to proxy classes has several benefits

1. The code is simplified , Convenient storage and read calls

2. And SP To understand the coupling , If you want to replace the repository later , Just modify the proxy class

Call the following :

object Pref: PreferenceHolder() {
    var isFirstInstall: Boolean by bindToPreferenceField(false)
    var time: Long? by bindToPreferenceFieldNullable()
}

The specific implementation is visible :SharedPreferences use Kotlin It should be written like this

With state LiveData

At present, we use more and more in the development process MVVM Patterns and ViewModel

We often use LiveData To identify the network request status

We need to define the request start , The request is successful , request was aborted , Three LiveData

This is also very redundant code , So we can do some encapsulation , Encapsulate a stateful LiveData

The definition is as follows :

typealias StatefulLiveData<T> = LiveData<RequestState<T>>
typealias StatefulMutableLiveData<T> = MutableLiveData<RequestState<T>>

@MainThread
inline fun <T> StatefulLiveData<T>.observeState(
    owner: LifecycleOwner,
    init: ResultBuilder<T>.() -> Unit
) {
    val result = ResultBuilder<T>().apply(init)

    observe(owner) { state ->
        when (state) {
            is RequestState.Loading -> result.onLading.invoke()
            is RequestState.Success -> result.onSuccess(state.data)
            is RequestState.Error -> result.onError(state.error)
        }
    }
}

Use as follows

val data = StatefulMutableLiveData<String>()
viewModel.data.observeState(viewLifecycleOwner) {
            onLading = {
                //loading
            }
            onSuccess = { data ->
                //success
            }
            onError = { exception ->
                //error
            }
        }

Through the above packaging , It can encapsulate the network request gracefully and concisely loading,success,error state , Streamlined the code , The structure is also quite clear

DSL

DSL(domain specific language), Domain specific language : A computer language designed to solve a particular problem , For example, we are familiar with SQL And regular expressions .

however , If you create a separate language to solve a specific domain problem , Both development and learning costs are high , So there is the interior DSL The concept of . The so-called interior DSL, Is to use a general programming language to build DSL. such as , As mentioned in this article Kotlin DSL, We are Kotlin DSL Make a simple definition :

“ Use Kotlin Language development , Solve problems in specific areas , With unique code structure API .”

for instance , We use TabLayout when , If you want to add monitoring for him , You need to implement the following 3 A way

override fun onTabReselected(tab: TabLayout.Tab?){

}

override fun onTabUnselected(tab: TabLayout.Tab?){

}
    
override fun onTabSelected(tab: TabLayout.Tab?){

}

In fact, we usually only use onTabSelected Method , The other two are generally empty implementations

We make use of DSL Yes OnTabSelectedListener encapsulate , You can avoid writing unnecessary empty implementation code

The specific implementation is as follows :

private typealias OnTabCallback = (tab: TabLayout.Tab?) -> Unit

class OnTabSelectedListenerBuilder : TabLayout.OnTabSelectedListener {

    private var onTabReselectedCallback: OnTabCallback? = null
    private var onTabUnselectedCallback: OnTabCallback? = null
    private var onTabSelectedCallback: OnTabCallback? = null

    override fun onTabReselected(tab: TabLayout.Tab?) =
            onTabReselectedCallback?.invoke(tab) ?: Unit

    override fun onTabUnselected(tab: TabLayout.Tab?) =
            onTabUnselectedCallback?.invoke(tab) ?: Unit

    override fun onTabSelected(tab: TabLayout.Tab?) =
            onTabSelectedCallback?.invoke(tab) ?: Unit

    fun onTabReselected(callback: OnTabCallback) {
        onTabReselectedCallback = callback
    }

    fun onTabUnselected(callback: OnTabCallback) {
        onTabUnselectedCallback = callback
    }

    fun onTabSelected(callback: OnTabCallback) {
        onTabSelectedCallback = callback
    }

}

fun registerOnTabSelectedListener(function: OnTabSelectedListenerBuilder.() -> Unit) =
        OnTabSelectedListenerBuilder().also(function)

Definition DSL The general steps of :

  • 1. First define a class to implement the callback interface , And implement its callback method .
  • 2. Observe the parameters of the callback method , Extract into a function type (function type), And use the type alias to give the function type another name as needed , And decorate with private .
  • 3. Declare some mutable functions of nullable types in the class (var) Private member variables , And get the corresponding variable in the callback function to realize its invoke function , Pass in the corresponding parameter .
  • 4. Define some names in the class that are the same as the callback interface , But the parameter is a function of the corresponding function type , And assign the function type to the corresponding member variable of the current class .
  • 5. Define a member function , Parameter is a receiver object with the class we specified and returns Unit Of Lambda expression , Create the corresponding object in the function , And use also Function Lambda Pass in the expression .

Call the following :

tabLayout.addOnTabSelectedListener(registerOnTabSelectedListener {
    onTabSelected { vpOrder.currentItem = it?.position ?: 0 }
})

Above , You can avoid writing some unnecessary empty implementation code

原网站

版权声明
本文为[Be a happy yard farmer]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/01/202201042206464189.html