分享一个 RecyclerView 中定点刷新的小技巧,android 音频框架
class ItemViewModel constructor( val data:String){
//not
//val count = data
//val liked = false
//should
val count = ObservableField<String>(data)
val liked = ObservableBoolean()
}
//partial_list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable
name="item"
type="io.ditclear.app.partial.PartialItemViewModel" />
<variable
name="presenter"
type="io.ditclear.bindingadapterx.ItemClickPresenter" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:onClick="@{(v)->presenter.onItemClick(v,item)}"
<TextView
android:text="@{item.count}" />
<ImageView
android:src="@{item.liked?@drawable/ic_action_liked:@drawable/ic_action_unlike}"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
当 UI 需要改变时,改变 ItemViewModel 的数据即可。
//activity
override fun onItemClick(v: View, item: PartialItemViewModel) {
item.toggle()
}
// ItemViewModle
fun toggle(){
liked.set(!liked.get())
count.set("{if (liked.get()) "liked" else ""}")
}
至于为什么?这就要说到 DataBinding 更新 UI 的原理了。
#/? ?DataBinding 更新 UI 原理? ?/
当我们在项目中运用 DataBinding,并将 xml 文件转换为 DataBinding 形式之后。经过编译 build,会生成相应的 binding 文件,比如 partial_list_item.xml 就会生成对于的 PartialListItemBinding 文件,这是一个抽象类,还会有一个 PartialListItemBindingImpl 实现类实现具体的渲染 UI 的方法 executeBindings。
当数据有改变的时候,就会重新调用 executeBindings 方法,更新 UI,那怎么做到的呢?
我们先来看 PartialListItemBinding 的构造方法.
public abstract class PartialListItemBinding extends ViewDataBinding {
@NonNull
public final ImageView imageView;
@Bindable
protected PartialItemViewModel mItem;
@Bindable
protected ItemClickPresenter mPresenter;
protected PartialListItemBinding(DataBindingComponent _bindingComponent, View _root,
int _localFieldCount, ImageView imageView) {
//调用父类的构造方法
super(_bindingComponent, _root, _localFieldCount);
this.imageView = imageView;
}
//...
}
调用了父类 ViewDataBinding 的构造方法,并传入了三个参数,这里看第三个参数_localFieldCount,它代表 xml 中存在几个 ObservableField 形式的数据,继续追踪.
protected ViewDataBinding(DataBindingComponent bindingComponent, View root, int localFieldCount) {
this.mBindingComponent = bindingComponent;
//考点 1
this.mLocalFieldObservers = new ViewDataBinding.WeakListener[localFieldCount];
this.mRoot = root;
if (Looper.myLooper() == null) {
throw new IllegalStateException("DataBinding must be created in view's UI Thread");
} else {
if (USE_CHOREOGRAPHER) {
//考点 2
this.mChoreographer = Choreographer.getInstance();
this.mFrameCallback = new FrameCallback() {
public void doFrame(long frameTimeNanos) {
ViewDataBinding.this.mRebindRunnable.run();
}
};
} else {
this.mFrameCallback = null;
this.mUIThreadHandler = new Handler(Looper.myLooper());
}
}
}
通过观察,发现其根据 localFieldCount 初始化了一个 WeakListener 数组,名为 mLocalFieldObservers。另一个重点是初始化了一个 mFrameCallback,在回调中执行了 mRebindRunnable.run。
当生成的 PartialListItemBindingImpl 对象调用 executeBindings 方法时,通过 updateRegistration 会对 mLocalFieldObservers 数组中的内容进行赋值。
随之生成的是相应的 WeakPropertyListener,来看看它的定义。
private static class WeakPropertyListener extends Observable.OnPropertyChangedCallback
implements ObservableReference<Observable> {
final WeakListener<Observable> mListener;
public WeakPropertyListener(ViewDataBinding binder, int localFieldId) {
mListener = new WeakListener<Observable>(binder, localFieldId, this);
}
//...
@Override
public void onPropertyChanged(Observable sender, int propertyId) {
ViewDataBinding binder = mListener.getBinder();
if (binder == null) {
return;
}
Observable obj = mListener.getTarget();
if (obj != sender) {
return; // notification from the wrong object?
}
//划重点
binder.handleFieldChange(mListener.mLocalFieldId, sender, propertyId);
}
}
当 ObservableField 的值有改变的时候,onPropertyChanged 会被调用,然后就会回调 binder(即 binding 对象)的 handleFieldChange 方法,继续观察。
private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
if (!this.mInLiveDataRegisterObserver) {
boolean result = this.onFieldChange(mLocalFieldId, object, fieldId);
if (result) {
this.request
Rebind();
评论