JetPack之DataBinding的使用

前言

这篇文章主要讲解JetPack中的DataBinding组件的使用。

简单实用

解决问题:当界面被改写的时候,Activity中的控件代码也必须被改写。为了避免这个问题的发生,使用了databinding。

  • Build.gradle配置项目使用dataBinding功能
defaultConfig{
  ...
  dataBinding{
     enabled true
  }
  ...
}
  • 将布局文件转换成dataBinding适配的布局文件
  • 反向绑定(数据回绑到界面上)
    • 在xml中的标签声明变化的数据。
    • 通过@{}绑定变量和函数@{()->函数名称}
<layout>

  <data>
<!-- 声明变量 
name:变量名称
type:变量类型-->
     <variable
         name="jetViewModel"
         type="com.martin.jetmo.jetpack.JetViewModel" />
    
 <!-- 导包-->   
      <import type="com.dustess.baselib.common.assist.Check" />
  
  </data>
	
   <!-- 
使用时需要将文件语法
   字符串:@{字符串内容},在引用时变量时,必须要有getXXX的函数,否则编译报错。
   调用函数:@{()->调用的函数}
   -->  
  <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名称)。
    • 在Activitiy中使用databinding.setData()和databinding.setLifecyclerOwner()开启双向绑定。

代码示例

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....
}

这样就达到了,我们想要的效果了。

DataBindingUtil的常用的代码片段

在项目中我们会集成一些常用的代码片段,提升开发效率:
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$s</string>

资源布局

<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>

进阶点:布局控件使用自定义属性

使用案例1:自定义防止多次点击属性app:singleClick

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>

使用案例2:自定义图片加载属性app:imageUrl

我们自定义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>

持续更新中…

推荐文章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章