How to Realize Minimal Callback with Kotlin

  • 2021-11-01 04:29:36
  • OfStack

Preface

Callbacks are widely used in various development scenarios, and their names are often various Callback and Listener, among which View. OnClickListener is probably the earliest and most commonly used contact in Android.


 mBtn.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
    Log.d("MM","Click");
   }
  });

However, it is a little annoying to write too much. I only want to print a log, but I have written so much code. Fortunately, there is only one method in this interface, but it is bloated to change to one callback with a large number of methods:


 mEdit.addTextChangedListener(new TextWatcher() {
   @Override
   public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    
   }

   @Override
   public void onTextChanged(CharSequence s, int start, int before, int count) {

   }

   @Override
   public void afterTextChanged(Editable s) {

   }
  });

If you want to optimize your code to make it look more concise and elegant, you can try one of the methods in Kotlin.

Simplification

Let's look at the callback in Kotlin first:


  mBtn.setOnClickListener(object :View.OnClickListener{
   override fun onClick(v: View?) {
    println("Click")
   }
  })

It seems that 1 point is not simplified, but because the function is also a kind of parameter in Kotlin, only one method interface is contained in Java, and Lambda expression can be used in Kotlin to achieve one effect.


 mBtnCallback.setOnClickListener { println("Click") }

Is it much simpler, but the above usage only applies to the situation that there is only one method in the interface, and if there are multiple methods, of course, it can be simplified:


 mEdit.addTextChangedListener {
   beforeTextChanged { text, start, count, after -> println("beforeTextChanged") }
   onTextChanged { text, start, before, count -> println("onTextChanged") }
   afterTextChanged { text -> println("afterTextChanged") }
  }

You can also call any of these methods on demand:


 mEdit.addTextChangedListener {
   onTextChanged { text, start, before, count -> println("onTextChanged") }
  }

However, addTextChangedListener here is an extension function, which we need to implement ourselves:


inline fun TextView.addTextChangedListener(init: TextWatcherBridge.() -> Unit) = addTextChangedListener(TextWatcherBridge().apply(init))

class TextWatcherBridge : TextWatcher {

 private var beforeTextChanged: ((CharSequence?, Int, Int, Int) -> Unit)? = null
 private var onTextChanged: ((CharSequence?, Int, Int, Int) -> Unit)? = null
 private var afterTextChanged: ((Editable?) -> Unit)? = null

 override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
  beforeTextChanged?.invoke(s, start, count, after)
 }

 override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
  onTextChanged?.invoke(s, start, before, count)
 }

 override fun afterTextChanged(s: Editable?) {
  afterTextChanged?.invoke(s)
 }

 fun beforeTextChanged(listener: (CharSequence?, Int, Int, Int) -> Unit) {
  beforeTextChanged = listener
 }

 fun onTextChanged(listener: (CharSequence?, Int, Int, Int) -> Unit) {
  onTextChanged = listener
 }

 fun afterTextChanged(listener: (Editable?) -> Unit) {
  afterTextChanged = listener
 }

}

The principle is to realize an extension function, and add our own TextWatcherBridge to the callback, because Kotlin supports functional programming, which is a high-order function. To reduce performance loss, spread functions are declared as inline functions.

Summarize


Related articles: