kotlin_android_extensions插件原理

用kotlin已经写过不少东西了,kotlin带给开发效率的提升真的是来自方方面面,尤其是再也不用写findViewById这类代码了,也不用再类里面写很多控件的成员变量了,因为有kotlin_android_extensions插件帮我们做了找控件的操作,我们只需要引用xml文件里面控件的id,就能引用这个控件做我们想做的事了。在activity里面使用的时候没什么问题,可是在fragment里面使用的时候却出了问题,我们来看一下在fragment里面的代码

class DemoFragment : Fragment() {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view = inflater.inflate(R.layout.fragment_demo, container)
        tv_1.text = "demo fragment"
        return view
    }
}
复制代码

报错信息:Caused by: java.lang.IllegalStateException: tv_1 must not be null,tv_1是空的,证明没有找到tv_1这个控件,但为什么activity就可以啊,到了fragment却不行了,带着问题我们来探究一下kotlin_android_extensions插件是怎么实现找控件的操作的?我们还是先来看看activity吧

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        pv1.setOnClickListener { }
    }
}
复制代码

还是直接使用xml文件里面的id(pv1)来设置点击事件,那就让我们看看为什么直接用id就可以直接调用控件的方法,我们先show kotlin bytecode然后在decomple,看一下编译之后的代码

private HashMap _$_findViewCache;

protected void onCreate(@Nullable Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  this.setContentView(2131296283);
  ((PracticeView1)this._$_findCachedViewById(id.pv1)).setOnClickListener((OnClickListener)null.INSTANCE);
}

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;
}
复制代码

可以看到pv1,被编译后其实是调用了_$_findCachedViewById这个方法,其实就是先从cache里面找view,没有的话就调用findViewById找到控件,然后缓存起来,就是这样,那我们看看fragment为什么就会报错,还是通过之前的方法来查看编译后的java代码

public final class DemoFragment extends Fragment {
   private HashMap _$_findViewCache;

   @Nullable
   public View onCreateView(@NotNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle  savedInstanceState) {
      Intrinsics.checkParameterIsNotNull(inflater, "inflater");
      View view = inflater.inflate(2131296284, container);
      TextView var10000 = (TextView)this._$_findCachedViewById(id.tv_1);
      Intrinsics.checkExpressionValueIsNotNull(var10000, "tv_1");
      var10000.setText((CharSequence)"demo fragment");
      return view;
   }

   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();
   }
}
复制代码

kotlin代码tv_1.text = "demo fragment"这一句被编译后被翻译成

TextView var10000 = (TextView)this._$_findCachedViewById(id.tv_1);
Intrinsics.checkExpressionValueIsNotNull(var10000, "tv_1");
var10000.setText((CharSequence)"demo fragment");
复制代码

第一句,还是通过_$findCachedViewById来找控件,第二句验证找到的控件非空,第三句才是设置text,那第二句验证非空出问题了,那么就是找控件的时候有问题了,看一下$_findCachedViewById,从cache里面找没有的话,再从getview里面找,但是getview为空时直接返回了null,那我们继续看看getview

/**
 * Get the root view for the fragment's layout (the one returned by {@link #onCreateView}),
 * if provided.
 *
 * @return The fragment's root view, or null if it has no layout.
 */
@Nullable
public View getView() {
    return mView;
}
复制代码

这个返回的mView是onCreateView返回的,所以在onCreateView返回之前mView都是null,真相一目了然了,所以我们在fragment里面使用的时候需要等fragment的view创建之后,这样就能避免报错

kotlin_android_extensions插件其他拓展功能

kotlin_android_extensions插件还可以在自定义view、viewholder里面使用,但是这些功能并不是最终的版本,所以你需要将它们添加到build.gradle中来启动它们:

androidExtensions {
	experimental = true
}
复制代码

@Parcelize注释,你可以以非常简单的方式使任何类都能实现Parcelable

@Parcelize
class Model(val title: String, val amount: Int) : Parcelable
复制代码

在这组实验中包含的新功能是一个新注释@ContainerOptions。这允许你以自定义方式构建缓存,甚至阻止类创建它。 默认情况下,它将用Hashmap,如我们之前看到的那样。但是,这可以用Android框架的SparseArray来改变,这在某些情况下可能会更有效率。如果由于某种原因,你不需要一个类的缓存,你也可以使用该选项。

@ContainerOptions(CacheImplementation.SPARSE_ARRAY)
class MainActivity : AppCompatActivity() {}
复制代码

目前,已有的选择是:

public enum class CacheImplementation {
    /** Use [android.util.SparseArray] as a backing store for the resolved views. */
    SPARSE_ARRAY,
    /** Use [HashMap] as a backing store for the resolved views (default). */
    HASH_MAP,
    /** Do not cache views for this layout. */
    NO_CACHE;

    companion object {
        /** The default cache implementation is [HASH_MAP]. */
        val DEFAULT = HASH_MAP
    }
}
复制代码

恩,这么多好用的功能,赶紧用起来吧!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值