针对android.databinding.InverseBindingListener的实例源码和androiddatabinding原理这两个问题,本篇文章进行了详细的解答,同时本文还将给你拓展A
针对android.databinding.InverseBindingListener的实例源码和android databinding原理这两个问题,本篇文章进行了详细的解答,同时本文还将给你拓展AbstractMethodError: abstract method "androidx.databinding.ViewDataBinding androidx.databind...、Android Data Binding 系列 (二) -- Binding 与 Observer 实现原理、Android 官方 DataBinding(三):RecyclerView 使用 ViewDataBinding 更新数据、Android 官方 DataBinding(四):BindingAdapter等相关知识,希望可以帮助到你。
本文目录一览:- android.databinding.InverseBindingListener的实例源码(android databinding原理)
- AbstractMethodError: abstract method "androidx.databinding.ViewDataBinding androidx.databind...
- Android Data Binding 系列 (二) -- Binding 与 Observer 实现原理
- Android 官方 DataBinding(三):RecyclerView 使用 ViewDataBinding 更新数据
- Android 官方 DataBinding(四):BindingAdapter
android.databinding.InverseBindingListener的实例源码(android databinding原理)
@BindingAdapter(value = {"onRefreshListener","refreshingAttrChanged"},requireAll = false) public static void setonRefreshListener(final SwipeRefreshLayout view,final SwipeRefreshLayout.OnRefreshListener listener,final InverseBindingListener refreshingAttrChanged) { SwipeRefreshLayout.OnRefreshListener newValue = new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { if (listener != null) { if (refreshingAttrChanged != null) { refreshingAttrChanged.onChange(); } listener.onRefresh(); } } }; SwipeRefreshLayout.OnRefreshListener oldValue = ListenerUtil.trackListener(view,newValue,R.id.onRefreshListener); if (oldValue != null) { view.setonRefreshListener(null); } view.setonRefreshListener(newValue); }
/** * This is how you implement 2-way data binding of a custom attribute if you want to maintain * a custom listener interface - OnHuechangelistener,in this case - while also Leveraging * the synthetic attributes hueAttrChanged (the InverseBindingListener). For an example of * how to implement custom 2-way binding with just the synthetic AttrChanged attribute's * InverseBindingListener,see DocumentView or LayerListView's currentLayer 2-way binding * (along with the InverseBindingMethod declarations in DocumentviewmodelBindingAdapters). * * @param view The view we're 2-way binding to. * @param onHuechangelistener The OnHuechangelistener bound by the developer on the view * in question. * @param inverseBindingListener Synthetic attribute representing an InverseBindingListener * for changes to the hue attribute. */ @BindingAdapter(value = {"onHueChange","hueAttrChanged"},requireAll = false) public static void setListeners(HuePicker view,final HuePicker.OnHuechangelistener onHuechangelistener,final InverseBindingListener inverseBindingListener) { HuePicker.OnHuechangelistener newListener; if (inverseBindingListener == null) { // If the synthetic listener param is null,just keep track of the listener set in // the binding newListener = onHuechangelistener; } else { // Otherwise define a new listener to wraps both listeners and invoke them at the // right time within the new listener if they are non-null newListener = new HuePicker.OnHuechangelistener() { @Override public void onHueChange(HuePicker huePicker,float hue) { if (onHuechangelistener != null) { onHuechangelistener.onHueChange(huePicker,hue); } inverseBindingListener.onChange(); } }; } // Use track listener to record listener we're adding and return any old listeners // registered through this method so that on rebind we don't add duplicate listeners HuePicker.OnHuechangelistener oldListener = ListenerUtil.trackListener(view,newListener,R.id.huechangelistener); // Remove the old listener we set using this BindingAdapter,if there is one if (oldListener != null) { view.removeHuechangelistener(oldListener); } // Add our new listener if (newListener != null) { view.addHuechangelistener(newListener); } }
public void setCurrentLayerAttrChanged(InverseBindingListener listener) { mAdapter.setCurrentLayerAttrChanged(listener); }
public void setCurrentLayerAttrChanged(InverseBindingListener listener) { mCurrentLayerAttrChangedListener = listener; }
public void setViewportXAttrChanged(InverseBindingListener listener) { mViewportXAttrChangedListener = listener; }
public void setViewportYAttrChanged(InverseBindingListener listener) { mViewportYAttrChangedListener = listener; }
public void setViewportWidthAttrChanged(InverseBindingListener listener) { mViewportWidthAttrChangedListener = listener; }
public void setViewportHeightAttrChanged(InverseBindingListener listener) { mViewportHeightAttrChangedListener = listener; }
public void setSaturationAttrChanged(InverseBindingListener listener) { mSaturationAttrChangedListener = listener; }
public void setValueAttrChanged(InverseBindingListener listener) { mValueAttrChangedListener = listener; }
public void setViewportStartAttrChanged(InverseBindingListener listener) { mViewportStartAttrChangedListener = listener; }
public void setHighlightStartAttrChanged(InverseBindingListener listener) { mHighlightStartAttrChangedListener = listener; }
public void setCurrentLayerAttrChanged(InverseBindingListener currentLayerAttrChanged) { mCurrentLayerAttrChangedListener = currentLayerAttrChanged; }
AbstractMethodError: abstract method "androidx.databinding.ViewDataBinding androidx.databind...
混淆导致的数据绑定库错误
问题摘要
AbstractMethodError: abstract method "androidx.databinding.ViewDataBinding androidx.databinding.DataBinderMapper.getDataBinder(androidx.databinding.DataBindingComponent, android.view.View, int)"
堆栈如下:
java.lang.AbstractMethodError: abstract method "androidx.databinding.ViewDataBinding androidx.databinding.DataBinderMapper.getDataBinder(androidx.databinding.DataBindingComponent, android.view.View, int)"
at androidx.databinding.MergedDataBinderMapper.getDataBinder(MergedDataBinderMapper.java:74)
at androidx.databinding.DataBindingUtil.bind(DataBindingUtil.java:199)
at androidx.databinding.DataBindingUtil.inflate(DataBindingUtil.java:130)
at androidx.databinding.DataBindingUtil.inflate(DataBindingUtil.java:95)
at cn.sintoon.common.adapter.SinRecyclerViewBaseAdapter.onCreateViewHolder(Unknown Source:11)
at cn.sintoon.common.adapter.SinRecyclerViewBaseAdapter.onCreateViewHolder(Unknown Source:0)
at androidx.recyclerview.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:6794)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5975)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5858)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5854)
at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2230)
at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1557)
at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1517)
at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:612)
at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3924)
at androidx.recyclerview.widget.RecyclerView.onMeasure(RecyclerView.java:3336)
at android.view.View.measure(View.java:23454)
at androidx.constraintlayout.widget.ConstraintLayout.internalMeasureChildren(ConstraintLayout.java:1227)
at androidx.constraintlayout.widget.ConstraintLayout.onMeasure(ConstraintLayout.java:1572)
at android.view.View.measure(View.java:23454)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6834)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
at androidx.appcompat.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:143)
at android.view.View.measure(View.java:23454)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6834)
at androidx.appcompat.widget.ActionBarOverlayLayout.onMeasure(ActionBarOverlayLayout.java:403)
at android.view.View.measure(View.java:23454)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6834)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
at android.view.View.measure(View.java:23454)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6834)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1565)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:847)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:726)
at android.view.View.measure(View.java:23454)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6834)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
at com.android.internal.policy.DecorView.onMeasure(DecorView.java:847)
at android.view.View.measure(View.java:23454)
at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2954)
at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1753)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2041)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1636)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7946)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1092)
at android.view.Choreographer.doCallbacks(Choreographer.java:893)
at android.view.Choreographer.doFrame(Choreographer.java:812)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1078)
at android.os.Handler.handleCallback(Handler.java:907)
出现场景
封装了一个类库,向外提供一个封装好的 RecyclerAdapter,使用 DataBinding 。生成类库时开启了混淆,在使用时,抛出异常。
问题原因
将 androidx.databinding.DataBindingComponent
混淆掉了。
如何修复
修改混淆规则。不混淆这个类。
-keep class androidx.databinding.DataBindingComponent {*;}
排查过程
根据错误堆栈看,是找不到这个方法,初步怀疑就是被混淆掉了方法,然后直接使用不混淆的测试,的确没有异常了。确认是混淆的问题。
然后在 /build/outputs/mapping/ 目录下查看 mapping.txt 文件,发现果然是将这类混淆掉了。
# compiler: R8
# compiler_version: 1.4.77
# min_api: 1
androidx.databinding.DataBindingComponent -> a.a.a:
mapping.txt 这个文件提供混淆前后类、方法、类成员等的对照表。
总结
在基础类库中使用了数据绑定库要配置混淆规则。
参考 Proguard 混淆规则
Android Data Binding 系列 (二) -- Binding 与 Observer 实现原理
写在前面
上篇文章 Android Data Binding 系列 (一) -- 详细介绍与使用 介绍了 Data Binding
的基础及其用法,本文接上篇,结合 DataBindingDemo 来学习下 Data Binding
的实现。
绑定实现
Activity 在 inflate layout 时,通过 DataBindingUtil 来生成绑定,从代码看,是遍历 contentView 得到 View 数组对象,然后通过数据绑定 library 生成对应的 Binding 类,含 Views、变量、listeners 等。生成类位于 build/intermediates/classes/debug/...package.../databinding/xxx.java
下,具体如何生成这里暂不作深入。
绑定过程
- 首先,会在父类(ViewDataBinding)中实例化回调或 Handler,用于之后的绑定操作;
private static final boolean USE_CHOREOGRAPHER = SDK_INT >= 16;
if (USE_CHOREOGRAPHER) {
mChoreographer = Choreographer.getInstance();
mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
mRebindRunnable.run();
}
};
} else {
mFrameCallback = null;
mUIThreadHandler = new Handler(Looper.myLooper());
}
- 接着,通过调用
mapBindings(...)
遍历布局以获得包含 bound、includes、ID Views 的数组对象,再依次赋给对应 View
final Object[] bindings = mapBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds);
this.mboundView0 = (android.widget.LinearLayout) bindings[0];
this.mboundView0.setTag(null);
- 然后,调用
invalidateAll() -> requestRebind() -> ... -> mRebindRunnable.run() -
执行 Runnable
// 用于动态重新绑定 Views
private final Runnable mRebindRunnable = new Runnable() {
@Override
public void run() {
synchronized (this) {
mPendingRebind = false;
}
.....
executePendingBindings();
}
};
- 最后,通过该 Runnable 会执行到
executePendingBindings() -> ... -> executeBindings()
,在这里会执行绑定相关操作。
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags; // mDirtyFlags 变量更新的标志
mDirtyFlags = 0;
}
.....
}
设置变量 (数据对象)
普通 Java bean 对象
- 首先,通过 mDirtyFlags 标识变量 (所有变量共用)
synchronized(this) {
mDirtyFlags |= 0x1L;
}
- 然后,调用
notifyPropertyChanged(...)
来通知更新(若有回调)
public void notifyPropertyChanged(int fieldId) {
if (mCallbacks != null) {
mCallbacks.notifyCallbacks(this, fieldId, null);
}
}
- 最后,调用
requestRebind() -> ... -> executeBindings()
再次执行绑定操作,将数据更新到 Views 上
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
.....
}
Observable 对象
- 在设置变量时,会先调用
updateRegistration(..)
注册一个 Observable 对象的监听
public void setContact(com.connorlin.databinding.model.ObservableContact contact) {
updateRegistration(0, contact);
this.mContact = contact;
synchronized(this) {
mDirtyFlags |= 0x1L;
}
notifyPropertyChanged(BR.contact);
super.requestRebind();
}
- 其他步骤同普通 Java bean 对象
ObservableFields 对象
-
前期步骤同普通 Java Bean 对象
-
与 Observable 对象不同的是,Observable 对象的监听是在
executeBindings()
中注册的
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
...
if ((dirtyFlags & 0xfL) != 0) {
if ((dirtyFlags & 0xdL) != 0) {
if (contact != null) {
// read contact.mName
mNameContact = contact.mName;
}
updateRegistration(0, mNameContact);
if (mNameContact != null) {
// read contact.mName.get()
mNameContact1 = mNameContact.get();
}
}
...
}
...
}
注册 Observable 对象监听
- 入口
updateRegistration(0, contact)
:
protected boolean updateRegistration(int localFieldId, Observable observable) {
return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
}
private boolean updateRegistration(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
...
// 确保不重复监听,先移除再添加观察监听
unregisterFrom(localFieldId);
registerTo(localFieldId, observable, listenerCreator);
return true;
}
protected void registerTo(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
if (observable == null) {
return;
}
// 创建对象监听并存到mLocalFieldObservers中
WeakListener listener = mLocalFieldObservers[localFieldId];
if (listener == null) {
// CREATE_PROPERTY_LISTENER -> create(...)
listener = listenerCreator.create(this, localFieldId);
mLocalFieldObservers[localFieldId] = listener;
}
// 将监听绑定到Observable对象上
listener.setTarget(observable);
}
每个 Observable 对象都会添加一个观察监听,保存在数组 mLocalFieldObservers
中,并以 localFieldId
索引。
- CREATE_PROPERTY_LISTENER 为何物?
private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
@Override
public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
// 返回从WeakPropertyListener实例中获取的监听器(WeakListener)
return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();
}
}
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 WeakListener<Observable> getListener() {
return mListener;
}
@Override
public void addListener(Observable target) {
// WeakPropertyListener 继承于 Observable.OnPropertyChangedCallback,
// 所以 this 其实就是 Observable对象的属性监听器
target.addOnPropertyChangedCallback(this);
}
...
}
private static class WeakListener<T> extends WeakReference<ViewDataBinding> {
private final ObservableReference<T> mObservable;
protected final int mLocalFieldId;
private T mTarget;
...
public void setTarget(T object) {
unregister();
mTarget = object;
if (mTarget != null) {
// mObservable 是上面的 WeakPropertyListener对象
// mTarget 是绑定到listener上得Observable对象
mObservable.addListener(mTarget);
}
}
...
}
CREATE_PROPERTY_LISTENER 实际上只是一个接口实例,注册时会调用它的 create()
方法创建一个弱引用 listener,它的作用是将 listener 绑定到 Observable 对象上, 绑定时,会调用 listener.setTarget(...)
将 Observable 对象传给 WeakPropertyListener
实例,然后,WeakPropertyListener
会为 Observable 对象添加 OnPropertyChangedCallback
。
- addOnPropertyChangedCallback 实现
addOnPropertyChangedCallback 在 BaseObservable 中实现,首先会实例化一个 PropertyChangeRegistry
对象,同时创建一个用来通知 Observable 对象重新绑定更新的回调 CallbackRegistry.NotifierCallback
。然后将 OnPropertyChangedCallback
添加到 PropertyChangeRegistry 的回调列表中
@Override
public synchronized void addOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
if (mCallbacks == null) {
mCallbacks = new PropertyChangeRegistry();
}
mCallbacks.add(callback);
}
这样,注册 Observable 对象的监听就完毕了。
更新 (重新绑定) Observable 对象
设置或更新 Observable 对象时都会调用 notifyPropertyChanged()
或 notifyChange()
来通知更新,那到底是如何更新的呢?
- 回调过程
public void notifyPropertyChanged(int fieldId) {
// mCallbacks 是 PropertyChangeRegistry对象,在 addOnPropertyChangedCallback 时实例化
// 如果注册了Observable对象监听,那么mCallbacks不为null
if (mCallbacks != null) {
mCallbacks.notifyCallbacks(this, fieldId, null);
}
}
// baseLibrary
private void notifyCallbacks(T sender, int arg, A arg2, int startIndex, int endIndex, long bits) {
long bitMask = 1L;
for(int i = startIndex; i < endIndex; ++i) {
if((bits & bitMask) == 0L) {
// mNotifier 是实例化PropertyChangeRegistry时创建的
// mNotifier 即 CallbackRegistry.NotifierCallback
this.mNotifier.onNotifyCallback(this.mCallbacks.get(i), sender, arg, arg2);
}
bitMask <<= 1;
}
}
// PropertyChangeRegistry.NOTIFIER_CALLBACK
public void onNotifyCallback(Observable.OnPropertyChangedCallback callback, Observable sender,
int arg, Void notUsed) {
// callback 是为Observable对象添加的OnPropertyChangedCallback,即WeakPropertyListener
callback.onPropertyChanged(sender, arg);
}
// WeakPropertyListener
public void onPropertyChanged(Observable sender, int propertyId) {
// binder 即生成的Binding类对象
ViewDataBinding binder = mListener.getBinder();
...
binder.handleFieldChange(mListener.mLocalFieldId, sender, propertyId);
}
private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
// onFieldChange 实现在生成的Binding类中
boolean result = onFieldChange(mLocalFieldId, object, fieldId);
if (result) {
// 如果对象属性变化,将重新绑定
requestRebind();
}
}
通过 notifyPropertyChanged 调用到 mNotifier 回调, mNotifier 通知 OnPropertyChangedCallback
Observable 对象属性发生变化,然后在 onPropertyChanged
中又转给 ViewDataBinding 对象 (生成的 Binding 类) 处理。
- 判断是否需要重新绑定并执行,在生成的 Binding 类中实现
// 生成的Binding类中得方法
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
// 如果变量不是Observable类型或没有添加 Bindable注解,就不会判断,直接返回false
switch (localFieldId) {
case 0 :
return onChangeContact((com.connorlin.databinding.model.ObservableContact) object, fieldId);
}
return false;
}
private boolean onChangeContact(com.connorlin.databinding.model.ObservableContact contact, int fieldId) {
switch (fieldId) {
case BR.name: {
synchronized(this) {
mDirtyFlags |= 0x4L;// 通过mDirtyFlags判断对象是否变化
}
return true;
}
...
}
return false;
}
至此,更新过程完毕。
整个注册与更新过程可以用一张流程图来概括:
事件处理
事件处理的原理很简单,在生成 Binding 类中会实现 View 事件的监听,在构造时实例化 View 的事件监听,然后在绑定时将事件监听对象赋值给对应 View,这样,点击时就会触发相应的监听。
这里以 DataBindingDemo 中 EventActivity
部分为例:
- 生成的 Binding 类并实现 View 的事件监听
public class ActivityEventBinding extends android.databinding.ViewDataBinding
implements android.databinding.generated.callback.OnCheckedChangeListener.Listener,
android.databinding.generated.callback.OnClickListener.Listener {
// Checkbox check监听
private final android.widget.CompoundButton.OnCheckedChangeListener mCallback3;
private final android.view.View.OnClickListener mCallback2;
private final android.view.View.OnClickListener mCallback1;
// listeners
private OnClickListenerImpl mAndroidViewViewOnCl;
...
// Listener Stub Implementations
public static class OnClickListenerImpl implements android.view.View.OnClickListener{
private com.connorlin.databinding.handler.EventHandler value;
public OnClickListenerImpl setValue(com.connorlin.databinding.handler.EventHandler value) {
this.value = value;
return value == null ? null : this;
}
@Override
public void onClick(android.view.View arg0) {
this.value.onClickFriend(arg0);
}
}
...
}
- 实例化 View 的事件监听
public ActivityEventBinding(android.databinding.DataBindingComponent bindingComponent, View root) {
super(bindingComponent, root, 0);
...
// listeners
mCallback3 = new android.databinding.generated.callback.OnCheckedChangeListener(this, 3);
mCallback2 = new android.databinding.generated.callback.OnClickListener(this, 2);
mCallback1 = new android.databinding.generated.callback.OnClickListener(this, 1);
invalidateAll();
}
- 在执行绑定中绑定 View 事件监听
@Override
protected void executeBindings() {
...
if ((dirtyFlags & 0x6L) != 0) {
if (handler != null) {
// read handler::onClickFriend
androidViewViewOnCli = (((mAndroidViewViewOnCl == null)
? (mAndroidViewViewOnCl = new OnClickListenerImpl()) : mAndroidViewViewOnCl).setValue(handler));
}
}
// batch finished
if ((dirtyFlags & 0x6L) != 0) {
this.mboundView1.setOnClickListener(androidViewViewOnCli);
}
if ((dirtyFlags & 0x4L) != 0) {
this.mboundView2.setOnClickListener(mCallback1);
this.mboundView3.setOnClickListener(mCallback2);
android.databinding.adapters.CompoundButtonBindingAdapter.setListeners(
this.mboundView4, mCallback3, (android.databinding.InverseBindingListener)null);
}
}
- 触发事件并执行
ViewStub
原理类似,只是利用 ViewStubProxy
来延迟绑定。
- 使用 layout 中的 ViewStub 实例化一个 ViewStubProxy 对象赋给 viewstub 变量,并与 Bingding 关联
public ActivityViewStubBinding(android.databinding.DataBindingComponent bindingComponent, View root) {
super(bindingComponent, root, 0);
final Object[] bindings = mapBindings(bindingComponent, root, 2, sIncludes, sViewsWithIds);
...
this.viewStub = new android.databinding.ViewStubProxy((android.view.ViewStub) bindings[1]);
this.viewStub.setContainingBinding(this);
...
}
- 实例化 ViewStubProxy 的同时会注册 inflate 监听
private OnInflateListener mProxyListener = new OnInflateListener() {
@Override
public void onInflate(ViewStub stub, View inflated) {
mRoot = inflated;
mViewDataBinding = DataBindingUtil.bind(mContainingBinding.mBindingComponent,
inflated, stub.getLayoutResource());
mViewStub = null;
if (mOnInflateListener != null) {
mOnInflateListener.onInflate(stub, inflated);
mOnInflateListener = null;
}
mContainingBinding.invalidateAll();
mContainingBinding.forceExecuteBindings();
}
};
public ViewStubProxy(ViewStub viewStub) {
mViewStub = viewStub;
mViewStub.setOnInflateListener(mProxyListener);
}
- inflate ViewStub
if (!mActivityViewStubBinding.viewStub.isInflated()) {
mActivityViewStubBinding.viewStub.getViewStub().inflate();
}
当 ViewStub infate 时,执行 mProxyListener
,其中会生成 ViewStub 的 Binding,并强制执行主 Binding 重绑
- 绑定 ViewStub
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
// batch finished
if (viewStub.getBinding() != null) {
viewStub.getBinding().executePendingBindings();
}
}
这样,ViewStub 绑定就结束了。
本篇完,敬请期待下篇...
我的简书账号是 ConnorLin,欢迎关注!
我的简书专题是 Android 开发技术分享,欢迎关注!
我的个人博客 欢迎关注!
原创文章,欢迎转载,转载请注明出处!
欢迎您扫一扫上面的微信公众号,订阅我的博客!
Android 官方 DataBinding(三):RecyclerView 使用 ViewDataBinding 更新数据
本例基于 Android 官方 DataBinding,在 RecyclerView 上实现一个简单需求:点击一个 button 按钮,增加一个数据元素,并更新的 view 上。
(1)首先写一个布局,这个布局上面放一个按钮 button,下面一个标准 Android RecyclerView。Button 按钮的实现一个简单的功能:没点击一次就增加一个数据元素,然后更新到 RecyclerView。recycler_view_layout.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="添加元素" />
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
(2)和附录文章 1,2,写数据模型 User,对比这个 User 和附录 1,2 的 User 建模时候的异同,User.java:
package zhangphil.test;
import android.databinding.BaseObservable;
//import android.databinding.Bindable;
/**
* Created by Phil on 2017/8/17.
*/
public class User extends BaseObservable {
private String id;
private String name;
private String blog;
public void setId(String id) {
this.id = id;
//notifyPropertyChanged(BR.id);
}
//@Bindable
public String getId() {
return this.id;
}
public void setName(String name) {
this.name = name;
//notifyPropertyChanged(BR.name);
}
//@Bindable
public String getName() {
return this.name;
}
public void setBlog(String blog) {
this.blog = blog;
//notifyPropertyChanged(BR.blog);
}
//@Bindable
public String getBlog() {
return this.blog;
}
}
(3)因为是一个 RecyclerView,RecyclerView 需要 Adapter,在 Adapter 中需要一个布局 layout,为 User 的数据找到 View,Adapter 需要的子 view 布局这里将写 View 和 Model 的绑定代码,item.xml:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="zhangphil.test.User" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.id}"
android:textColor="@android:color/holo_red_light" />
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}"
android:textColor="@android:color/holo_red_light" />
<TextView
android:id="@+id/blog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.blog}"
android:textColor="@android:color/holo_red_light" />
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@android:color/holo_blue_bright" />
</LinearLayout>
</layout>
(4)上层 Java 代码,注意观察 ViewHolder 的写法。比较关键的是在创建 ViewHolder 时候传入的 ViewDataBinding。在 Adapter 的 onBindViewHolder 里面,仅需两行代码就实现数据模型和 View 的绑定,MainActivity.java:
package zhangphil.test;
import android.databinding.DataBindingUtil;
import android.databinding.ViewDataBinding;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import static zhangphil.test.BR.user;
public class MainActivity extends AppCompatActivity {
private int index = 0;
private ItemAdapter mItemAdapter;
private ArrayList<User> mItems;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.recycler_view_layout);
mItems = new ArrayList();
for (int i = 0; i < 1; i++) {
User u = new User();
u.setId(index + "");
u.setName("zhangphil @" + index);
u.setBlog("blog.csdn.net/zhangphil @" + index);
mItems.add(u);
index++;
}
RecyclerView mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
mItemAdapter = new ItemAdapter();
mRecyclerView.setAdapter(mItemAdapter);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
User u = new User();
u.setId(index + "");
u.setName("zhangphil @" + index);
u.setBlog("blog.csdn.net/zhangphil @" + index);
mItems.add(u);
mItemAdapter.notifyDataSetChanged();
index++;
}
});
}
private class ItemAdapter extends RecyclerView.Adapter<ItemViewHolder> {
@Override
public ItemViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater.from(viewGroup.getContext()), R.layout.item, viewGroup, false);
ItemViewHolder holder = new ItemViewHolder(binding);
return holder;
}
@Override
public void onBindViewHolder(ItemViewHolder viewHolder, int i) {
viewHolder.getBinding().setVariable(user, mItems.get(i));
viewHolder.getBinding().executePendingBindings();
}
@Override
public int getItemCount() {
return mItems.size();
}
}
private class ItemViewHolder extends RecyclerView.ViewHolder {
private ViewDataBinding binding;
public ItemViewHolder(ViewDataBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void setBinding(ViewDataBinding binding) {
this.binding = binding;
}
public ViewDataBinding getBinding() {
return this.binding;
}
}
}
代码运行结果,每点击一次 button,数据增加一个,更新:
附录:
1,《Android 官方 DataBinding 简例(一)》链接:http://blog.csdn.net/zhangphil/article/details/77322530
2,《Android 官方 DataBinding(二):动态数据更新 notifyPropertyChanged》链接:http://blog.csdn.net/zhangphil/article/details/77328688
Android 官方 DataBinding(四):BindingAdapter
(1)和附录 1,2,3 一样,毫无例外,先写一个数据模型 User.java:
package zhangphil.test;
import android.databinding.BaseObservable;
import android.databinding.ObservableField;
import android.databinding.ObservableInt;
/**
* Created by Phil on 2017/8/17.
*/
public class User extends BaseObservable {
public final ObservableInt id = new ObservableInt();
public final ObservableField<String> name = new ObservableField<>();
public final ObservableField<String> url = new ObservableField<>();
}
(2)写 activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="user"
type="zhangphil.test.User" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{String.valueOf(user.id)}"
android:textColor="@android:color/holo_red_light" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}"
android:textColor="@android:color/holo_red_light" />
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:imageUrl="@{user.url}" />
</LinearLayout>
</layout>
注意这里 ImageView 的属性配置。
(3)任意定义一个类,比如叫做 Util.java,在这个类里面的方法通过 BindingAdapter,imageUrl 和 loadImage 方法绑定:
package zhangphil.test;
import android.databinding.BindingAdapter;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.Priority;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
import com.bumptech.glide.request.RequestOptions;
/**
* Created by Phil on 2017/8/18.
*/
public class Util {
@BindingAdapter({"imageUrl"})
public static void loadImage(ImageView view, String u) {
RequestOptions options = new RequestOptions()
.centerCrop()
.placeholder(R.mipmap.ic_launcher_round)
.error(R.mipmap.ic_launcher)
.priority(Priority.HIGH)
.diskCacheStrategy(DiskCacheStrategy.NONE);
Glide.with(view.getContext()).applyDefaultRequestOptions(options).load(u).transition(new DrawableTransitionOptions().crossFade(1000)).into(view);
}
}
(注:Glide 是 v4.0.0)
(4)上层 MainActivity.java:
package zhangphil.test;
import android.databinding.DataBindingUtil;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import zhangphil.test.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
User user = new User();
binding.setUser(user);
user.id.set(1);
user.name.set("zhangphil");
//user.url.set("http://avatar.csdn.net/9/7/A/1_zhangphil.jpg");
user.url.set("https://www.baidu.com/img/bd_logo1.png");
}
}
代码运行结果:

在 activity_main.xml 里面的 ImageView,由于定义了 app:imageUrl="@{user.url}"
这使得 user.url 这个数据字段成为方法 imageUrl 的被观察者,BindingAdapter 两者绑定在一起,user.url 的动态变化,直接导致 imageUrl 的调用。同时,user.url 将作为一个字符串变量传递给 imageUrl 方法,在 imageUrl 方法里面,反向的将 Glide 加载的图片赋值给绑定的 ImageView。
附录:
1,《Android 官方 DataBinding 简例(一)》链接:http://blog.csdn.net/zhangphil/article/details/77322530
2,《Android 官方 DataBinding(二):动态数据更新 notifyPropertyChanged》链接:http://blog.csdn.net/zhangphil/article/details/77328688
3,《Android 官方 DataBinding(三):RecyclerView 使用 ViewDataBinding 更新数据》链接:http://blog.csdn.net/zhangphil/article/details/77367432
今天的关于android.databinding.InverseBindingListener的实例源码和android databinding原理的分享已经结束,谢谢您的关注,如果想了解更多关于AbstractMethodError: abstract method "androidx.databinding.ViewDataBinding androidx.databind...、Android Data Binding 系列 (二) -- Binding 与 Observer 实现原理、Android 官方 DataBinding(三):RecyclerView 使用 ViewDataBinding 更新数据、Android 官方 DataBinding(四):BindingAdapter的相关知识,请在本站进行查询。
本文标签: