在使用 kotlin 编写Android 应用的时候,会使用到一个插件kotlin-android-extensions
既:
1
| apply plugin: 'kotlin-android-extensions'
|
该插件的主要作用就是避免了手动调用findViewById()
的来获取视图,绑定 View。类似ButterKnife 中BindVIew
背后逻辑也是调用findViewById()
来绑定 VIew。这里理一下背后的实现逻辑。
kotlin-android-extensions
提供了两个功能来获取 xml 中 View 的实例
- 一 在Activity、Fragment、View中可以直接通过ID名称访问试图
- 二 可以直接访问一个view的内部属性 通过
view.xxx_id
访问 view 内部的xxx_id
资源控件
通过Tools -> Kotlin -> Show Kotlin ByteCode
点击Decompile 按钮查看生成的代码可以发现
对于第一中方式,内部的实现方式是生成了_$_findCachedViewById()
来 findView,以及_$_clearFindViewByIdCache()
来清除 View 实例
调用方式:
Activity:
1 2 3 4 5 6 7 8 9 10
| import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) main_title.visibility = View.VISIBLE } }
|
Fragment:
1 2 3 4 5 6 7 8 9 10 11 12 13
| import kotlinx.android.synthetic.main.fragment_main.*
class MainFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) content_list.apply { layoutManager = LinearLayoutManager(context) adapter = ContentListAdapterInnel() } }
}
|
View:
1 2 3 4 5 6 7 8 9 10 11 12
| import kotlinx.android.synthetic.main.view_title.view.*
class TitleView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : LinearLayout(context, attrs, defStyleAttr) { init { LayoutInflater.from(context).inflate(R.layout.view_title, this, true) view_title.text = "Background KTA Extension" } }
|
背后的实现方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| private HashMap _$_findViewCache;
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(); } }
|
对于第二种方式,却是直接调用的findViewById()
来绑定 View 实列。
比如在RecyclerView 的 bindViewHolder 中
1 2 3 4 5 6 7 8 9 10
| import kotlinx.android.synthetic.main.item_list.view.*
class ContentListAdapter : RecyclerView.Adapter<ContentListAdapter.Holder>() {
override fun onBindViewHolder(holder: Holder, position: Int) { holder.itemView.item_list_position.text = list[position] }
class Holder(v: View) : RecyclerView.ViewHolder(v) }
|
背后的实现方式:
1 2 3 4 5 6 7 8
| public void onBindViewHolder(@NotNull ContentListAdapter.Holder holder, int position) { Intrinsics.checkParameterIsNotNull(holder, "holder"); View var10000 = holder.itemView; Intrinsics.checkExpressionValueIsNotNull(var10000, "holder.itemView"); TextView var3 = (TextView)var10000.findViewById(id.item_list_position); Intrinsics.checkExpressionValueIsNotNull(var3, "holder.itemView.item_list_position"); var3.setText((CharSequence)this.list.get(position)); }
|
第一种是会缓存View,第二种是每次都findViewById()
,所以第一种性能上要高于第二种。
由于默认的 extension 只支持第一类,如果想在任意类中都提供这种缓存机制可以使用LayoutContainer
接口。
LayoutContainer
接口还是一个实验功能,需要增加配置
1 2 3 4 5
| android { androidExtensions{ experimental = true } }
|
LayoutContainer
定于如下:
1 2 3 4
| public interface LayoutContainer { /** Returns the root holder view. */ public val containerView: View? }
|
只需要是实现containerView
的赋值即可。
修改后的调用方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import kotlinx.android.synthetic.main.item_list.*
class ContentListAdapterInnel : RecyclerView.Adapter<ContentListAdapterInnel.Holder>() {
override fun onBindViewHolder(holder: Holder, position: Int) { holder.bind(list[position]) holder.item_list_position.text = list[position] }
class Holder(override val containerView: View) : RecyclerView.ViewHolder(containerView), LayoutContainer {
fun bind(s: String) { item_list_position.text = s } } }
|
调用的是holder.xxx_id
,而不是holder.itemView.xxx_id
,从导入语法上来看也由import kotlinx.android.synthetic.main.item_list.view.*
变成了import kotlinx.android.synthetic.main.item_list.*
下面是几种方式的区分点
种类 |
Auto Import |
调用方式 |
背后实现方式 |
Activity/Fragment |
import kotlinx.android.synthetic.main.item_list.* |
xxx_id |
$findCachedViewById |
View |
import kotlinx.android.synthetic.main.view_title.view.* |
xxx_id |
$findCachedViewById |
view.xxx_id |
import kotlinx.android.synthetic.main.item_list.view.* |
view.xxx_id |
findViewById |
LayoutContiner |
import kotlinx.android.synthetic.main.item_list.* |
xxx_id |
$findCachedViewById |
参考:
kotlin-android-extensions
Kotlin Android Extensions: Using View Binding the right way
tutorial-android-plugin