kotlin-android-extensions
是kotlin为Android专门提供的扩展插件,在这主要讲下替代 findViewById
的场景
首先在 build.gradle 添加以下代码
apply plugin: 'kotlin-android-extensions'
- Activity
布局文件activity_main
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.think.MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="test"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
Activity 中主要代码
//导入布局
import kotlinx.android.synthetic.main.activity_main.*
class TestActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
textView.text = "123"
}
}
Activity 中使用很简单,注意不要导错了包就行
import 格式是这样的:kotlinx.android.synthetic.main.布局名称.*
- Fragment
布局省略
Fragment 中主要代码
//导入布局
import kotlinx.android.synthetic.main.activity_main.*
class TestFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.activity_main, container, false)
}
//在这里才能取到textView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
textView.text = "123"
}
}
与 Activity 不同的是,Fragment 需要在 onViewCreated
中使用控件才行,否则会报空指针异常 。反编译代码后可以找到答案
AndroidStudio 中打开 kotlin Bytecode
,进行反编译查看编译后的代码。
Tools -> Kotlin -> Show Kotlin Bytecode -> Decompile
public final class TestFragment 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(layout.activity_main, container, false);
}
public void onViewCreated(@Nullable View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
View var10000 = this._$_findCachedViewById(id.textView);
Intrinsics.checkExpressionValueIsNotNull(var10000, "textView");
···
}
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();
}
}
反编译后发现有个 _$_findCachedViewById
方法,这个方法作用是在第一次使用控件的时候,在缓存集合中进行查找,有就直接使用,没有就通过 findViewById
进行查找,并添加到缓存集合中。其还提供了 _$_clearFindViewByIdCache()
方法用于清除缓存,在我们想要彻底替换界面控件时可以使用到。
注意到这行代码 View var10000 = this.getView();
这个 getView()
是在 Fragment onCreateView
返回的 View ,如果在 onCreateView
中使用控件,这个时候自然会报空指针异常。
- ViewHolder
在 build.gradle 添加以下代码
androidExtensions {
experimental = true
}
RecyclerViewAdapter 中 ViewHolder 代码
//导入布局
import kotlinx.android.synthetic.main.activity_main.*
class ViewHolder(view: View, override val containerView: View = view) : RecyclerView.ViewHolder(view), LayoutContainer
···
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int?) {
super.onBindViewHolder(holder, position)
val viewHolder = holder as ViewHolder
viewHolder.textView.text = "123"
}
注意不要写成 viewHolder.itemView.textView
,至于为什么我们反编译一下代码看看
使用 viewHolder.itemView.textView
的代码
···
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
super.onBindViewHolder(holder, position)
···
((TextView)var10000.findViewById(id.textView)).setText("123")
}
可以发现执行了 findViewById
方法,这显然是不对的,而且 import 的包变成import kotlinx.android.synthetic.main.activity_main.view.*
使用 viewHolder.textView
的代码
···
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
super.onBindViewHolder(holder, position)
···
((TextView)viewHolder._$_findCachedViewById(id.textView).setText("123")
}
这次调用的是 _$_findCachedViewById
方法,说明这才是正确的使用方式
注意不要忘记添加 experimental = true