The Kotlin-Android-Extensions are an Android extension from Kotlin. They don’t provide much functionality right now, but they’re worth using just as an alternative to findViewById. It remains to be seen whether more features will be added to make Android development faster and easier.

Module :app -> build.gradle adds the following code

apply plugin: 'kotlin-android-extensions'

Say goodbye to findViewById

Go directly to the instance and layout the file activity_main


       
<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"
    tools:context="cn.andrlin.kotlin.demo.MainActivity">

    <TextView
        android:id="@+id/helloTv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>Copy the code

The main code of the Activity:

import kotlinx.android.synthetic.main.activity_main.*

class TestActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        helloTv.text = "Hello Kotlin!"}}Copy the code

We don’t need to use findViewById to get the control, just the id of the control to manipulate its methods. Of course, you’ve also found the import, and yes, it’s a must, and AndroidStudio will automatically add it for you. There’s nothing wrong with writing it manually if you don’t bother.

The import format is like this: kotlinx. Android. Synthetic. Main. Layout name.*. Note the layout included in the include and import the layout to ensure normal use.

You are advised to use the same naming rules for ids in the layout file as those in Java classes.

Have you really said goodbye to findViewById?

In AndroidStudio, you can open kotlin Bytecode directly and decompile to see the compiled code. Tools->Kotlin->Show Kotlin Bytecode->Decompile

Looking at the compiled code, we can find something interesting.

.
   private HashMap _$_findViewCache;

   protected void onCreate(@Nullable Bundle savedInstanceState) {
      .
      ((TextView)this._$_findCachedViewById(id.helloTv)).setText((CharSequence)"Hello Kotlin!");
   }

   public View _$_findCachedViewById(int var1) {
      if(this._$_findViewCache == null) {
         this._$_findViewCache = new HashMap();
      }

      View var2 = (View)this._$_findViewCache.get(Integer.valueOf(var1));
      if(var2 == null) {
         var2 = this.findViewById(var1);
         this._$_findViewCache.put(Integer.valueOf(var1), var2);
      }

      return var2;
   }

   public void _$_clearFindViewByIdCache() {
      if(this._$_findViewCache != null) {
         this._$_findViewCache.clear();
      }

   }
.Copy the code

The code is simple: when the control is used for the first time, find it in the cache collection, use it directly, and find it in findViewById and add it to the cache collection. It also provides the _$_clearFindViewByIdCache() method to clear the cache when we want to completely replace interface controls.

Let’s look at the compiled code used in fragments.

   privateHashMap _$_findViewCache; .public void onViewCreated(@Nullable View view, @Nullable Bundle savedInstanceState) {
      super.onViewCreated(view, savedInstanceState);
      ((TextView)this._$_findCachedViewById(id.helloTv)).setText((CharSequence)"Hello Fragment By Kotlin!");
   }

   public View _$_findCachedViewById(int var1) {
      if(this._$_findViewCache == null) {
         this._$_findViewCache = new HashMap();
      }

      View var2 = (View)this._$_findViewCache.get(Integer.valueOf(var1));
      if(var2 == null) {
         View var10000 = this.getView();
         if(var10000 == null) {
            return null;
         }

         var2 = var10000.findViewById(var1);
         this._$_findViewCache.put(Integer.valueOf(var1), var2);
      }

      return var2;
   }

   public void_ $_clearFindViewByIdCache() {
      if(this._$_findViewCache ! =null) {
         this._$_findViewCache.clear(); }}// $FF: synthetic method
   public void onDestroyView() {
      super.onDestroyView();
      this._$_clearFindViewByIdCache();
   }Copy the code

The only difference with an Activity is that the onDestroyView() method calls _$_clearFindViewByIdCache() to clear the cache, so you don’t have to worry about releasing the cache when the View is destroyed.

At this point, I’m sure it’s clear that we haven’t completely moved on from findViewById, but kotlin’s extension takes advantage of caching to make development easier and faster.

How are Extansions used in ViewHolder

The only downside to using Kotlin at first is that you can’t use the Kotlin extension instead of findViewById in ViewHolder. However, in the 1.1.4 version of kotlin’s August update, a corresponding update was made.

Android Extensions plugin now supports not only Activities and Fragments, but also custom Views and even custom layout containers such as a ViewHolder. Also, variants are now fully supported.

To use the 1.1.4 kotlin-Android-Extensions enhancement, you need to enable the experimental flag in build.gradle:

androidExtensions {
    experimental = true
}Copy the code

We can then try to write a ViewHolder by implementing the LayoutContainer interface, which provides only one containerView for storing the view. See the compiled source code:

import android.support.v7.widget.RecyclerView
import android.view.View
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.activity_main.*

class ViewHolder constructor(override val containerView: View?)
    : RecyclerView.ViewHolder(containerView),
      LayoutContainer {

    fun setContent(str: String) {
        helloTv.text = str
    }
}Copy the code

Compiled code:

public final class ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder implements LayoutContainer {
   @Nullable
   private final View containerView;
   private HashMap _$_findViewCache;

   public final void setContent(@NotNull String str) {
      Intrinsics.checkParameterIsNotNull(str, "str");
      ((TextView)this._$_findCachedViewById(id.helloTv)).setText((CharSequence)str);
   }

   @Nullable
   public View getContainerView() {
      return this.containerView;
   }

   public ViewHolder(@Nullable View containerView) {
      super(containerView);
      this.containerView = containerView;
   }

   public View _$_findCachedViewById(int var1) {
      if(this._$_findViewCache == null) {
         this._$_findViewCache = new HashMap();
      }

      View var2 = (View)this._$_findViewCache.get(Integer.valueOf(var1));
      if(var2 == null) {
         View var10000 = this.getContainerView();
         if(var10000 == null) {
            return null;
         }

         var2 = var10000.findViewById(var1);
         this._$_findViewCache.put(Integer.valueOf(var1), var2);
      }

      return var2;
   }

   public void_ $_clearFindViewByIdCache() {
      if(this._$_findViewCache ! =null) {
         this._$_findViewCache.clear(); }}}Copy the code

When the ViewHolder is initialized, the view passed in is stored in the containerView variable, just like the Activity’s _$_findCachedViewById, In the ViewHolder use containerView. The findViewById, namely through the incoming View View. The findViewById, thus acquiring the control.

Use ContainerOptions to change the cache type of the View

The default View cache is made using HashMap, and annotations are provided to modify it:

@ContainerOptions(CacheImplementation.SPARSE_ARRAY)
class TestActivity : AppCompatActivity() {
    .
}Copy the code

CacheImplementation provides three ways:

public enum class CacheImplementation {
    SPARSE_ARRAY,
    HASH_MAP,
    NO_CACHE;

    public val hasCache: Boolean
        get() = this! = NO_CACHE companionobject {
        val DEFAULT = HASH_MAP
    }
}Copy the code

Of course, sometimes you only use the control once while the Activity is loading, so you can select NO_CACHE. Let’s look at the compiled source code for NO_CACHE:

@ContainerOptions(
   cache = CacheImplementation.NO_CACHE
)
...
public final class TestActivity extends AppCompatActivity {
   protected void onCreate(@Nullable Bundle savedInstanceState) {
      ...
      ((TextView)this.findViewById(id.helloTv)).setText((CharSequence)"Hello Kotlin!"); }}Copy the code

That’s right, every time you use a control, findViewById will be used to find the control. There will no longer be a _$_findCachedViewById method.

Parcelable extension

Annotated Parcelable was added in 1.1.4.

@Parcelize
class User(val firstName: String.val lastName: String) : ParcelableCopy the code

It is important to note that this method is also experimental and requires the experimental flag to be turned on in the build.gradle file