这篇文章主要讲解JetPack中的DataBinding组件的使用。
解决问题:当界面被改写的时候,Activity中的控件代码也必须被改写。为了避免这个问题的发生,使用了databinding。
defaultConfig{
...
dataBinding{
enabled true
}
...
}
<layout>
<data>
<variable
name="jetViewModel"
type="com.martin.jetmo.jetpack.JetViewModel" />
<import type="com.dustess.baselib.common.assist.Check" />
data>
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{String.valueOf(jetViewModel.likeNumber)}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.196" />
<Button
android:id="@+id/button"
android:layout_width="87dp"
android:layout_height="36dp"
android:text="+1"
android:onClick="@{()->jetViewModel.add()}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
<layout>
setContentView
改写为dataBindingUtil.setContentView(R.layout.xxxx)
返回一个AndroidDataBinding类型,这里变量起为databinding。findViewbyId()
被替换为databinding.textView(AndroidDataBinding类型.id名称)。代码示例
public class JetPackActivity extends AppCompatActivity {
/**
* 创建JetModel
*/
private JetViewModel mJetViewModel;
/**
* 当将布局自动转换为databinding的xml就会自动创建此类
*/
private ActivityMainBinding mainBinding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main);
mainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
mJetViewModel = ViewModelProviders.of(this).get(JetViewModel.class);
//根据在布局中声明变量自动生成
mainBinding.setJetViewModel(mJetViewModel);
mainBinding.setLifecycleOwner(this);
}
}
官网中《双向绑定》文章中提到。若用户在使用EditText
输入文本时,通过dataBinding
可以将直接将监听器直接在XML中进行绑定。
AppCompatEditText
<layout>
<data>
<variable
name="afterChangeListener"
type="androidx.databinding.adapters.TextViewBindingAdapter.AfterTextChanged" />
data>
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/idSearchEdit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
...
android:afterTextChanged="@={afterChangeListener}"/>
layout>
@={} 表示法接收属性的数据更改并同时监听用户更新,其中重要的是包含“=”符号。
上面的TextViewBindingAdapter为Android中内置的。
接下来我们来看下代码的编写。
//将xml申明的afterChangeListener对象在代码中进行复制。
binding.afterChangeListener = TextViewBindingAdapter.AfterTextChanged {
//do something....
}
这样就达到了,我们想要的效果了。
在项目中我们会集成一些常用的代码片段,提升开发效率:
BaseBindingActivity
abstract class BaseBindingMVPActivity<T : ViewDataBinding> : BaseInputMVPActivity() {
lateinit var binding: T
override fun getLayoutId(): Int {
binding = DataBindingUtil.setContentView(this, bindingLayoutId) as T
return 0
}
abstract val bindingLayoutId: Int
}
BaseBindingFragment
abstract class BaseBindingMVPFragment<TDB : ViewDataBinding> : BaseMVPLoadFragment(){
lateinit var binding: TDB
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false)
return binding.root
}
}
BaseBindingViewHolder
class BaseBindViewHolder(view: View) : BaseViewHolder(view) {
var binding: ViewDataBinding? = null
var presenter: BasePresenter<*>? = null
init {
try {
binding = DataBindingUtil.bind(view)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
关键方法@{@string/bu_cost_learning_time(time)}
资源字符串
<string name="bu_cost_learning_time">累计学习时长:%1$sstring>
资源布局
<layout 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">
<data>
<variable
name="time"
type="com.lang.String" />
data>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@{@string/bu_cost_learning_time(time)}"
android:textColor="@color/textColor595959"
android:textSize="13sp"
app:layout_constraintStart_toStartOf="@+id/idDescriptionTv"
app:layout_constraintTop_toBottomOf="@+id/idDescriptionTv"
tools:text="@string/bu_cost_learning_time"/>
layout>
databing可以将自定义属性,并且使用在对应的布局中,如:点击事件我们可以在布局中导入,并且绑定到按钮中进行触发。下面讲解使用案例
创建扩展类DataBindingAdapter.kt
/**
* 短时间内响应一次点击
*/
//定义布局中引用的属性名称
@BindingAdapter("singleClick")
//实现细节
fun bindSingleClick(view: View, onClickListener: View.OnClickListener) {
//OnSingleClickListener:是自己再想买里面实现的防双点击的按钮
view.setOnClickListener(object : OnSingleClickListener() {
override fun onSingleClick(v: View) {
onClickListener.onClick(v)
}
})
}
在布局中使用app:singleClick属性
<layout 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">
<data>
<variable
name="listener"
type="android.view.View.OnClickListener" />
data>
<TextView
android:id="@+id/idTitle"
android:layout_width="match_parent"
android:layout_height="@dimen/def_tab_height"
android:background="@color/white"
android:gravity="center_vertical"
android:text="双点击测试"
android:textSize="@dimen/font_size_def_title"
android:textStyle="bold"
app:singleClick="@{listener}"
app:layout_constraintTop_toTopOf="parent" />
layout>
我们自定义app:imageUrl
属性,在xml中使用后,可以直接执行bindImageUrl
函数代码段
扩展工具类:ImageDataBindingAdapter.kt
//自定义属性:app:imageUrl、app:roundRadius、app:placeImage
@BindingAdapter(value = ["app:imageUrl", "app:roundRadius", "app:placeImage"], requireAll = false)
//具体实现细节
fun bindImageUrl(imageView: ImageView, url: String?, roundRadius: Int = 0, placeImage: Drawable?) {
val drawable = placeImage?:imageView.context.getDrawable(R.drawable.def_place)
GlideUtils.getInstance().loadContextRoundBitmap(imageView.context, Check.checkReplace(url),
imageView, drawable, drawable, DensityUtils.dip2px(imageView.context, roundRadius.toFloat()))
}
使用扩展属性app:imageUrl
<layout 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">
<data>
<variable
name="imgUrl"
type="com.lang.String" />
data>
<ImageView
android:id="@+id/imagUrlTestIv"
android:layout_width="@dimen/dp_36"
android:layout_height="@dimen/dp_36"
android:layout_marginStart="@dimen/dp_8"
app:imageUrl="@{imgUrl}"/>
layout>