美文网首页kotlinKotlin从入门到放弃Kotlin
Kotlin-Android-Extensions:不仅仅是替代

Kotlin-Android-Extensions:不仅仅是替代

作者: AndrLin | 来源:发表于2017-09-21 19:51 被阅读359次

    kotlin-android-extensions是kotlin为Android专门提供的扩展插件,虽然现在其提供的功能不多,但是光是替代findViewById功能,就已经值得使用了。至于以后官方是否会提供更多的功能,让Android开发更快速便捷,就只能拭目以待了。

    module:app -> build.gradle 添加以下代码

    apply plugin: 'kotlin-android-extensions'

    和findViewById说再见

    直接上实例,布局文件activity_main

    <?xml version="1.0" encoding="utf-8"?>
    <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>
    

    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!"
        }
    }
    

    我们不需要使用findViewById来获取控件,只需要使用控件的id就可以操作控件的相关方法。当然大家也发现了import,没错,这也是必须的,AndroidStudio会自动为你添加。如果你不嫌麻烦,手动写也是没毛病的。

    import格式是这样的:kotlinx.android.synthetic.main.布局名称.*。需要注意的是include进来的布局,也是需要通过import导入相应布局,这样才能正常使用。

    建议布局文件中id的命名规则和java类中的命名规则保持一致,都使用驼峰命名。

    真的是和findViewById说再见了吗?

    AndroidStudio中可以直接打开kotlin Bytecode,进行反编译查看编译后的代码。
    Tools->Kotlin->Show Kotlin Bytecode->Decompile

    查看编译后的代码,我们可以发现很有趣的东西。

    ...
       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();
          }
    
       }
    ...
    

    代码很简单,在第一次使用控件的时候,在缓存集合中进行查找,有就直接使用,没有就通过findViewById进行查找,并添加到缓存集合中。其还提供了_$_clearFindViewByIdCache()方法用于清除缓存,在我们想要彻底替换界面控件时可以使用到。

    我们再来看看Fragment中使用,编译过后的代码是如何的。

       private HashMap _$_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();
       }
    

    和Activity的唯一区别就是在onDestroyView()方法中调用了_$_clearFindViewByIdCache(),来清楚缓存,所以我们不用担心在View销毁的时候,缓存不能及时释放的问题。

    到这里,我想大家肯定很清楚了,我们并没有完全的离开findViewById,只是kotlin的扩展插件利用缓存的方式让我们开发更方便、更快捷。

    ViewHolder中如何使用Extansions

    刚开始使用kotlin的时候,唯一觉得不太好的就是ViewHolder中不能使用kotlin扩展替代findViewById。但是在八月kotlin更新的1.1.4版本中,做了相应的更新

    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.

    使用1.1.4版本的kotlin-android-extensions增强功能,需要在build.gradle中开启实验性标志:

    androidExtensions {
        experimental = true
    }
    

    接下来我们就可以试着去编写ViewHolder了,只需要实现LayoutContainer接口,该接口只提供了一个containerView,用于存储视图,具体作用看编译后源码:

    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
        }
    }
    

    编译后代码:

    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();
          }
    
       }
    }
    

    ViewHolder初始化的时候,将传进来的view存储在containerView变量中,和Activity的_$_findCachedViewById一样,ViewHolder中的使用的是containerView.findViewById,即通过传进来的View进行View.findViewById,从而获取控件。

    使用ContainerOptions修改View的缓存类型

    默认View的缓存是是使用的HashMap来做的,官方提供了注解的方式来进行修改:

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

    CacheImplementation提供了三种方式:

    public enum class CacheImplementation {
        SPARSE_ARRAY,
        HASH_MAP,
        NO_CACHE;
    
        public val hasCache: Boolean
            get() = this != NO_CACHE
    
        companion object {
            val DEFAULT = HASH_MAP
        }
    }
    

    当然某些时候你只在Activity加载的时候,使用一次控件,那么就可以选择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!");
       }
    }
    

    没错,当你每次使用控件的时候,都会通过findViewById进行查找获取控件,将不会再有_$_findCachedViewById方法,在特殊情况下不使用缓存可能会更好,所以应根据不同的情况来决定使用哪种缓存方式。

    Parcelable 扩展

    1.1.4版本中新增了通过注解的方式实现Parcelable。

    @Parcelize
    class User(val firstName: String, val lastName: String) : Parcelable
    

    值得注意的是此方法也是处于实验阶段,也需要在build.gradle文件中开启实验性标志,其他Parcelable相关请查看官方详情

    相关文章

      网友评论

      • Obadiah:build.gradle 里怎么开启实验性标志?
        AndrLin:在app的build.gradle中
        android {

        androidExtensions {
        experimental = true
        }

        }

      本文标题:Kotlin-Android-Extensions:不仅仅是替代

      本文链接:https://www.haomeiwen.com/subject/zfutextx.html