Kotlin如何直接使用控件ID原理详析
前言
最近断断续续地把项目的界面部分的代码由java改成了kotlin编写,并且如果应用了kotlin-android-extensions插件,一个显而易见的好处是再也不用写 findviewbyid()来实例化你的控件对象了,直接操作你在布局文件里的id即可,这一点我感觉比butterknife做的还简洁友好。
activity
import android.support.v7.app.appcompatactivity import android.os.bundle import kotlinx.android.synthetic.main.activity_main.* class mainactivity : appcompatactivity() { override fun oncreate(savedinstancestate: bundle?) { super.oncreate(savedinstancestate) setcontentview(r.layout.activity_main) textview.text="hello world" } }
其中kotlinx.android.synthetic.main.activity_main.*
是kotlin-android-extensions插件自动生成的。下面我们来解析下原理。因为kotlin也是一门jvm语言,最近也会和java一样编译成class字节码,所以我们直接来反编译看看生成的java文件。
选择decompile,解析出来的代码如下
public final class mainactivity extends appcompatactivity { private hashmap _$_findviewcache; protected void oncreate(@nullable bundle savedinstancestate) { super.oncreate(savedinstancestate); this.setcontentview(2131296284); textview var10000 = (textview)this._$_findcachedviewbyid(id.textview); intrinsics.checkexpressionvalueisnotnull(var10000, "textview"); var10000.settext((charsequence)"hello world"); } public view _$_findcachedviewbyid(int var1) { if (this._$_findviewcache == null) { this._$_findviewcache = new hashmap(); } view var2 = (view)this._$_findviewcache.get(var1); if (var2 == null) { var2 = this.findviewbyid(var1); this._$_findviewcache.put(var1, var2); } return var2; } public void _$_clearfindviewbyidcache() { if (this._$_findviewcache != null) { this._$_findviewcache.clear(); } } }
可以很清楚看到最终还是调用了findviewbyid()
,不过获取view对象直接调用的是findcachedviewbyid,并且创建一个 hashmap 进行view对象的缓存,避免每次调用 view 时都会重新调用findviewbyid()
进行查找。
fragment
再来看下fragment中的使用:
import android.os.bundle import android.support.v4.app.fragment import android.view.layoutinflater import android.view.view import android.view.viewgroup import kotlinx.android.synthetic.main.fragment_blank.* class blankfragment : fragment() { override fun oncreateview(inflater: layoutinflater, container: viewgroup?, savedinstancestate: bundle?): view? { return inflater.inflate(r.layout.fragment_blank, container, false) } override fun onviewcreated(view: view, savedinstancestate: bundle?) { super.onviewcreated(view, savedinstancestate) textview_fra.text="hello world" } }
反编译后代码如下
public final class blankfragment extends fragment { private hashmap _$_findviewcache; @nullable public view oncreateview(@notnull layoutinflater inflater, @nullable viewgroup container, @nullable bundle savedinstancestate) { intrinsics.checkparameterisnotnull(inflater, "inflater"); return inflater.inflate(2131296285, container, false); } public void onviewcreated(@notnull view view, @nullable bundle savedinstancestate) { intrinsics.checkparameterisnotnull(view, "view"); super.onviewcreated(view, savedinstancestate); textview var10000 = (textview)this._$_findcachedviewbyid(id.textview_fra); intrinsics.checkexpressionvalueisnotnull(var10000, "textview_fra"); var10000.settext((charsequence)"hello world"); } public view _$_findcachedviewbyid(int var1) { if (this._$_findviewcache == null) { this._$_findviewcache = new hashmap(); } view var2 = (view)this._$_findviewcache.get(var1); if (var2 == null) { view var10000 = this.getview(); if (var10000 == null) { return null; } var2 = var10000.findviewbyid(var1); this._$_findviewcache.put(var1, var2); } return var2; } public void _$_clearfindviewbyidcache() { if (this._$_findviewcache != null) { this._$_findviewcache.clear(); } } // $ff: synthetic method public void ondestroyview() { super.ondestroyview(); this._$_clearfindviewbyidcache(); } }
可以看到最终是通过调用getview().findviewbyid()
来进行控件的实例化。
看下getview()
源码
@nullable public view getview() { return this.mview; }
再看下mview成员变量的赋值时机:
void performcreateview(@nonnull layoutinflater inflater, @nullable viewgroup container, @nullable bundle savedinstancestate) { if (this.mchildfragmentmanager != null) { this.mchildfragmentmanager.notestatenotsaved(); } this.mperformedcreateview = true; this.mviewlifecycleowner = new lifecycleowner() { public lifecycle getlifecycle() { if (fragment.this.mviewlifecycleregistry == null) { fragment.this.mviewlifecycleregistry = new lifecycleregistry(fragment.this.mviewlifecycleowner); } return fragment.this.mviewlifecycleregistry; } }; this.mviewlifecycleregistry = null; this.mview = this.oncreateview(inflater, container, savedinstancestate); if (this.mview != null) { this.mviewlifecycleowner.getlifecycle(); this.mviewlifecycleownerlivedata.setvalue(this.mviewlifecycleowner); } else { if (this.mviewlifecycleregistry != null) { throw new illegalstateexception("called getviewlifecycleowner() but oncreateview() returned null"); } this.mviewlifecycleowner = null; } }
可以看到mview其实就是oncreateview()
的返回值,所以我们不能在oncreateview()
方法里操作控件id的方式操作view对象,会产生空指针异常。建议在onviewcreated()
方法里使用。
其他(动态布局)
除了activity和fragment,我们用的最多的ui布局当属adapter了,kotlin-android-extensions也提供了对这一类动态布局的支持。因为这一功能是,默认关闭,我们需要手动打开,在build.gradle中开启:
androidextensions { experimental = true }
然后再recycler.adapter中使用如下:
import kotlinx.android.extensions.layoutcontainer import kotlinx.android.synthetic.main.item_recyclerview.* class myadapter(val context: context, val data: list<string>) : recyclerview.adapter<myadapter.viewholder>() { override fun oncreateviewholder(parent: viewgroup, viewtype: int): viewholder { val view = layoutinflater.from(context).inflate(r.layout.item_recyclerview, parent, false) return viewholder(view) } override fun onbindviewholder(holder: viewholder, position: int) { holder.name_tv.text = data[position] holder.itemview.setonclicklistener { toast.maketext(context,"点击了第$position 项",toast.length_short).show() } } override fun getitemcount(): int { return data.size } inner class viewholder(itemview: view) : recyclerview.viewholder(itemview), layoutcontainer { override val containerview: view = itemview } }
可以看到相比activity和fragment,我们的viewholder需要多实现一个接口layoutcontainer。看下它的源码:
public final class viewholder extends android.support.v7.widget.recyclerview.viewholder implements layoutcontainer { @notnull private final view containerview; private hashmap _$_findviewcache; @notnull public view getcontainerview() { return this.containerview; } public viewholder(@notnull view itemview) { intrinsics.checkparameterisnotnull(itemview, "itemview"); super(itemview); this.containerview = itemview; } public view _$_findcachedviewbyid(int var1) { if (this._$_findviewcache == null) { this._$_findviewcache = new hashmap(); } view var2 = (view)this._$_findviewcache.get(var1); if (var2 == null) { view var10000 = this.getcontainerview(); if (var10000 == null) { return null; } var2 = var10000.findviewbyid(var1); this._$_findviewcache.put(var1, var2); } return var2; } public void _$_clearfindviewbyidcache() { if (this._$_findviewcache != null) { this._$_findviewcache.clear(); } } }
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。
上一篇: 如何拍照会使得拍出的照片有电影感觉?