欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Kotlin如何直接使用控件ID原理详析

程序员文章站 2022-04-12 11:52:56
前言 最近断断续续地把项目的界面部分的代码由java改成了kotlin编写,并且如果应用了kotlin-android-extensions插件,一个显而易见的好处是再也...

前言

最近断断续续地把项目的界面部分的代码由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文件。

Kotlin如何直接使用控件ID原理详析

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

  }
 }

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。