最近在通过阅读《第一行代码》第三版的方式去学习Android开发,但是因为成书时间与Android发展,书上有些内容已经发现改变。改动较为明显的一个便是在AS1.4.2之后的版本中viewBinding将逐步替代kotlin-android-extensions去实现对View的实例化。
具体官方文章如下:Kotlin Android Extensions 的未来计划
于是在后续的学习中我也选择了使用viewBinding去绑定控件。
在书中4.2.2章节使用了接口的方式来注册监听器
1.使用接口注册监听器并在监听器中调用其他view
class MainActivity : AppCompatActivity(), View.OnClickListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.setOnClickListener(this)
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.button -> {
val inputText = editText.text.toString()
Toast.makeText(this, inputText, Toast.LENGTH_SHORT).show()
}
}
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#00ff00"
android:textSize="24sp"
android:text="This is TextView"
android:gravity="center" />
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button"
android:textAllCaps="false"/>
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="这是一段提示"
android:maxLines="2"/>
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="?android:attr/progressBarStyleHorizontal"
android:max="100"
/>
LinearLayout>
实现的功能很简单就是获取文本框中输入的内容并将其打到toast上。
如果改用viewBinding去做,首先发现以为viewBinding是创建在onCreate方法中的,无法在onCLick方法中获得
首先想到的解决方式是直接在onClick中再创建一个viewBinding实例不就好了。
这样改动以后代码不报错了。
运行之后发现虽然调用了toast,但是无法正确显示输入的文本内容,原因是因为绑定了两次视图的缘故,更深层的原因还在研究
那么在这种使用场景之下,我们该如何使用viewBinding去实现对视图的一次绑定,并且可以在别的方法中调用,实现功能呢?
选择将viewBinding设置为全局变量,用懒加载的方式实现实例化,具体操作如下。
class MainActivity : AppCompatActivity(), View.OnClickListener {
private lateinit var viewbinding:ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewbinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(viewbinding.root)
viewbinding.button.setOnClickListener(this)
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.button -> {
val inputText = viewbinding.editText.text.toString()
Toast.makeText(this, inputText, Toast.LENGTH_SHORT).show()//viewbinding.imageView.setImageResource(R.mipmap.pic2)
//viewbinding.progressBar.progress += 10
/*if(viewbinding.progressBar.visibility == View.VISIBLE) {
viewbinding.progressBar.visibility = View.GONE
} else {
viewbinding.progressBar.visibility = View.VISIBLE
}*/
/*AlertDialog.Builder(this).apply {
setTitle("This is Dialog")
setMessage("Something important")
setCancelable(false)
setPositiveButton("ok") { dialog,which ->
}
setNegativeButton("Cancel") {dialog, which ->}
show()
}*/
}
}
}
}
第二种用法:viewBinding在自定义控件中的使用
首先推荐我学习时参考的一篇博文:【JetPack】视图绑定 ( ViewBinding ) 各种应用 ( 视图绑定两种方式 | Activity 布局 | 对话框布局 | 自定义组件布局 | RecyclerView 列表布局 )
在学习到《第一行代码》的4.4.2的时候,作者使用了自定义控件的形式去注册按钮,代码如下:
class TitleLayout(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
init {
LayoutInflater.from(context).inflate(R.layout.title, this)
titleBack.setOnClickListener {
val activity = context as Activity
activity.finish()
}
titleEdit.setOnClickListener {
Toast.makeText(context, "You clicked Edit button", Toast.LENGTH_SHORT).show()
}
}
}
点击Back销毁Activity,点击EDIT将一段文本以toast的形式输出到屏幕,同样如果使用ko-an-ex插件的话直接可以完成注册
如果是用viewBinding,并且使用第一种用法中的懒加载,发现无法通过setContextView的方式将根视图注册进去,因为setContextView是Activity的方法而TitleLayout继承自LinearLayout,LinearLayout继承自ViewGroup,ViewGroup继承自View。绕来绕去一句话说明白就是这个类他是个视图。
难道说在自定义组件中我们就要使用findViewById<>()方法去注册视图了吗?
我们点开app\build\generated\data_binding_base_class_source_out下生成的TitleBinding.java文件,发现有两个inflate方法,一个是之前使用的一个参数的,另一个则是三个参数的方法,并且第二个参数的类型是ViewGrop,第三个参数输入的是attachToParent,输入一个boolean类型参数表示是否连接到父View
那么我们可以对我们的代码做出如下修改
class TitleLayout(context: Context, attributeSet: AttributeSet): LinearLayout(context, attributeSet) {
private lateinit var titleBinding: TitleBinding
init {
titleBinding = TitleBinding.inflate(LayoutInflater.from(context),this,true)
titleBinding.titleBack.setOnClickListener {
val activity = context as Activity
activity.finish()
}
titleBinding.titleEdit.setOnClickListener {
Toast.makeText(context, "You clicked Edit button", Toast.LENGTH_SHORT).show()
}
}
}
运行效果: