在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的體驗優秀。
1.配置
在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'
2.在Actiivty裏的用法
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"
/>
3.在Adapter裏的用法
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。
4.原理
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
}