在android开发中,Activity和Fragment等页面中可能需要大量的findViewById(),虽然可以用一些方法简化写法,或者用类似Butter Knife插件自动生成,但是如果页面上的控件很多,依然有一长串子代码,或者这些库需要为这些控件添加注释,而Kotlin android 的扩展插件Extensions可以让我们获得和某些三方库相同的体验,而且无需添加任何额外的代码。
下面先体验下原生的写法:
在BaseActivity或BaseFragment里添加如下方法,简单整理一下findViewById()方法:
@SuppressWarnings("unchecked")
protected final <E extends View> E $(@IdRes int id) {
try {
return (E) this.findViewById(id);
} catch (ClassCastException ex) {
Logger.e("Could not cast View to concrete class \n" + ex.getMessage());
throw ex;
}
}
在子类Activity中就可以这样使用:
private TextView as_tv_status;
private ImageView as_iv_splash;
@Override
protected void initView() {
as_tv_status = $(R.id.as_tv_status);
as_iv_splash = $(R.id.as_iv_splash);
}
这已经是不用三方库和插件的情况下最简化的写法了(谁有更简化的方式告诉我,我请他吃皮皮虾);
另一种就是使用Butter Knife插件,这种方式比原生实现的一个优点是可以自动把需要定义的控件批量自动初始化,就连$()一个一个控件都不需要,但是需要通过注解的方式初始化这些控件。
但即便如此,还是不如kotlin的Extensions的体验优秀。
在project/build.gradle里加上extensions
buildscript {
ext.kotlin_version = '1.2.31'
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
app/build.gradle
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
import kotlinx.android.synthetic.main.activity_extensions.*
class ExtensionsActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_extensions)
ae_tv.text = "123"
}
}
activity_extensions.xml:
<TextView
android:id="@+id/ae_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
/>
import kotlinx.android.synthetic.main.item_list.view.*
/**
* Created by Aislli on 2018/4/27 0027.
*/
class ListAdapter(mContext: Context, mList: ArrayList<String>) : ARecyclerBaseAdapter<ListAdapter.ViewHolder, String>(mContext, mList) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_list, parent, false))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val s = mList[position]
holder.tv_title.text = s
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
val tv_title: TextView = itemView.il_textview
}
}
在Activity里设置数据:
fun initView(){
val list = ArrayList<String>()
for (x in 1..10) {
list.add("number $x")
}
recyclerView.layoutManager = LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false)
recyclerView.adapter = ListAdapter(this,list)
}
区别是import最后面多了个.view,相当于把item_list这个资源对象赋值给了itemView。
kotlin Extension是通过添加一个隐藏的缓存容器来来缓存视图,保证最小化的调用findViewById(),这个模块很小,不会增加apk的大小;
class MyActivity : Activity()
fun MyActivity.a() {
textView.text = "Hidden view"
textView.visibility = View.INVISIBLE
}
例如上面的示例中,textView只会被调用一次findViewById();
还可以通过在build.gradle里修改配置来改变缓存方式,或不缓存,默认使用HashMap方式存储:
androidExtensions {
experimental = true
defaultCacheImplementation = "HASH_MAP" // also SPARSE_ARRAY, NONE
}
例如加上下面注解了的代码,就不缓存视图了,每次使用控件,都会调findViewById()。
import kotlinx.android.extensions.ContainerOptions
@ContainerOptions(cache = CacheImplementation.NO_CACHE)
class MyActivity : Activity()
fun MyActivity.a() {
// findViewById() will be called twice
textView.text = "Hidden view"
textView.visibility = View.INVISIBLE
}