前言

我们在使用Java时,findViewById是比较麻烦的,通过控件的ID来查找控件,当然也有第三方框架ButterKnife,Dagger等来减少findViewById的使用,通过插件自动生成,但在使用Kotlin时,便不必如此。
在app的Gradle下添加

  1. apply plugin: 'kotlin-android-extensions'

使用扩展函数的好处

在Kotlin官方文档中对ButterKnife类似的库是这样介绍

Being libraries dependent on runtime, they require annotating fields for each View

也就是说类似ButterKnife这类库,在运行时,需要对每一个控件进行注解。
而对于Kotlin, 官方是这样介绍

The Kotlin Android Extensions plugin allows us to obtain the same experience we have with some of these libraries, without having to add any extra code or shipping any additional runtime.

Kotlin提供类似于这些库的相同体验,但不需要额外的代码和运行时间。我自己理解为减少findViewById的使用的同时,没有产生额外的代码,也不用在运行时对每个控件进行注解。

使用

在需要绑定视图的Activity、Fragment、Adapter及自定义View中引入资源文件

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

3、 在使用的位置直接使用xml中对应的id访问视图完整代码如下

import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        btn_login.setOnClickListener {
            Toast.makeText(this@MainActivity,"登录",Toast.LENGTH_SHORT).show()
        }
    }
}

引入文件详细说明

import kotlinx.android.synthetic.main.activity_main.*
  • 固定前缀import kotlinx.android.synthetic.main
  • 布局文件名称activity_main
  • 需要引入的视图“”表示引入布局下所有视图当然也可以只引入需要的视图把换成对应的id就行啦如下:

    import kotlinx.android.synthetic.main.activity_main.btn_login
    
  • 在Adapter和自定义View中引入需要在布局文件名后添加view节点如下

    import kotlinx.android.synthetic.main.view_login.view.*
    

Kotlin插件绑定视图范围

在Activity中使用引入资源文件直接使用id访问视图

import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        btn_login.setOnClickListener {
            Toast.makeText(this@MainActivity,"登录",Toast.LENGTH_SHORT).show()
        }
    }
}

在Fragment中使用引入资源文件直接使用id访问视图

有一点特别注意在onCreateView中不直接访问视图因为视图没有加载完成容易引起空指针需要在onViewCreated中访问视图代码如下

import kotlinx.android.synthetic.main.view_login.*
class LoginFragment:Fragment() {
    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        super.onCreateView(inflater, container, savedInstanceState)
        return inflater?.inflate(R.layout.view_login, container, false)
    }
    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        btn_login.setOnClickListener {
            Toast.makeText(context,"登录", Toast.LENGTH_SHORT).show()
        }
    }
}

在Adapter中使用引入布局文件

需要添加view节点可使用ViewHolder中的itemView直接访问视图当然也可以在ViewHolder中做一次视图绑定与传统
ViewHolder类似代码如下

import kotlinx.android.synthetic.main.view_login.view.*
class LoginAdapter(var context: Context):RecyclerView.Adapter<LoginAdapter.ViewHolder>() {
    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(context)
                .inflate(R.layout.view_login,parent,false)
        return ViewHolder(view)
    }
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.itemView.btn_login.setOnClickListener {
            Toast.makeText(context,"登录", Toast.LENGTH_SHORT).show()
        }
    }

    override fun getItemCount(): Int {
        return 3
    }
    class ViewHolder(view: View) : RecyclerView.ViewHolder(view)
}
  • 在自定义View中使用引入布局文件需要添加view节点在自定义视图中可直接使用id访问视图代码如下
    import kotlinx.android.synthetic.main.view_login.view.*
    class LoginView @JvmOverloads constructor(
          context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
    ) : FrameLayout(context, attrs, defStyleAttr) {
      init {
          View.inflate(context,R.layout.view_login,this)
          btn_login.setOnClickListener {
              Toast.makeText(context,"登录", Toast.LENGTH_SHORT).show()
          }
      }
    }
    

实现原理

在kotlin -> Tools下面点击show kotlin bytecode可以查看生成的字节码。
字节码有时候不容易理解,所以可以点击Decompile
看一下activity生成的代码:

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

当我们需要一个view的时候,首先会去缓存中查找,如果没有,将会通过findViewById查找他并且添加到缓存中。
他还额外的生成了一个清除缓存的方法:clearFindViewByIdCache,如果必须重建视图,可以使用该方法,因为旧视图将失效。
下面这行代码:

welcomeMessage.text = "Hello Kotlin!"

将会被替换为:

((TextView)this._$_findCachedViewById(id.welcomeMessage)).setText((CharSequence)"Hello Kotlin!");

所以这个属性不是真实的,插件不会为每个view生成一个属性。只是会在编译期间替换代码以访问视图缓存,将其转换为正确的类型并调用该方法。

作者:小菜鸟程序媛 链接:https://www.jianshu.com/p/6ee7ef051872 来源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

作者:雷宇 链接:https://www.imooc.com/article/21913?block_id=tuijian_wz 来源:慕课网 本文原创发布于慕课网 ,转载请注明出处,谢谢合作