Databinding简单的分析ImageView属性android:src="@{resImgId}"的调用流程
16lz
2021-01-26
首先看activity_main.xml中的代码:
并且给LinearLayout设置了点击事件,和将resImgId设置到ImageView的属性android:src="@{resImgId}"。
再看看MainActivity中的代码:
public class MainActivity extends AppCompatActivity { Custom binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = DataBindingUtil.setContentView(this, R.layout.activity_main); binding.setClick(this); } public void click(View view) { binding.setResImgId(R.drawable.ic_launcher); }}
上面的代码写好了是不能其作用的,需要
自定义属性默认的android命名空间下,我们会发现并不是所有的属性都能直接通过data binding进行设置,比如margin,padding,还有自定义View的各种属性。遇到这些属性,我们就需要自己去定义它们的绑定方法。Setter就像Data Binding会自动去查找get方法一下,在遇到属性绑定的时候,它也会去自动寻找对应的set方法。
不懂的可以参考http://blog.zhaiyifan.cn/2016/07/06/android-new-project-from-0-p8/,这篇文章的自定义属性,Setter,和BindingMethods等。
public class ImageViewAttrAdapter { @BindingAdapter("android:src") public static void setSrc(ImageView view, Bitmap bitmap) { view.setImageBitmap(bitmap); } @BindingAdapter("android:src") public static void setSrc(ImageView view, int resId) { view.setImageResource(resId); }}
下面分析一下点击LinearLayout,图片是如何被设置到ImageView上面的。
binding.setResImgId(R.drawable.ic_launcher);
public class Custom extends android.databinding.ViewDataBinding
public void setResImgId(int resImgId) { this.mResImgId = resImgId; synchronized(this) { mDirtyFlags |= 0x2L; } super.requestRebind(); }
这里设置了一个mDirtyFlags之后,就开始调用super.requestRebind(),看看对应的这个方法代码,这个方法在其父类ViewDataBinding类里面。
/** * @hide */ protected void requestRebind() { synchronized (this) { if (mPendingRebind) { return; } mPendingRebind = true; } if (USE_CHOREOGRAPHER) { mChoreographer.postFrameCallback(mFrameCallback); } else { mUIThreadHandler.post(mRebindRunnable); } }
重点看执行了mUIThreadHandler.post(mRebindRunnable),这就是在主线程中执行了一个Runnable了。
private final Runnable mRebindRunnable = new Runnable() { @Override public void run() { synchronized (this) { mPendingRebind = false; } if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) { // Nested so that we don't get a lint warning in IntelliJ if (!mRoot.isAttachedToWindow()) { // Don't execute the pending bindings until the View // is attached again. mRoot.removeOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER); mRoot.addOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER); return; } } executePendingBindings(); } };
这里也很简单,重点是executePendingBindings()。
public void executePendingBindings() { if (mIsExecutingPendingBindings) { requestRebind(); return; } if (!hasPendingBindings()) { return; } mIsExecutingPendingBindings = true; mRebindHalted = false; if (mRebindCallbacks != null) { mRebindCallbacks.notifyCallbacks(this, REBIND, null); // The onRebindListeners will change mPendingHalted if (mRebindHalted) { mRebindCallbacks.notifyCallbacks(this, HALTED, null); } } if (!mRebindHalted) { executeBindings(); if (mRebindCallbacks != null) { mRebindCallbacks.notifyCallbacks(this, REBOUND, null); } } mIsExecutingPendingBindings = false; }
一些相关的判断,重点executeBindings()函数。
/** * @hide */ protected abstract void executeBindings();
可以看到这是抽象的,也就是需要调用子类的这个方法的。
@Override protected void executeBindings() { long dirtyFlags = 0; synchronized(this) { dirtyFlags = mDirtyFlags; mDirtyFlags = 0; } android.view.View.OnClickListener androidViewViewOnCli = null; int resImgId = mResImgId; android.graphics.Bitmap bitmap = mBitmap; org.loader.app4.MainActivity click = mClick; java.lang.String nameStu = null; org.loader.app4.Student stu = mStu; if ((dirtyFlags & 0x22L) != 0) { // read resImgId~ resImgId = resImgId; } if ((dirtyFlags & 0x24L) != 0) { // read bitmap~ bitmap = bitmap; } if ((dirtyFlags & 0x28L) != 0) { // read click~ click = click; if (click != null) { // read android.view.View.OnClickListener~click~~click androidViewViewOnCli = (((mAndroidViewViewOnCl == null) ? (mAndroidViewViewOnCl = new OnClickListenerImpl()) : mAndroidViewViewOnCl).setValue(click)); } } if ((dirtyFlags & 0x31L) != 0) { // read stu~ stu = stu; updateRegistration(0, stu); if (stu != null) { // read name~.~stu~ nameStu = stu.getName(); } } // batch finished if ((dirtyFlags & 0x22L) != 0) { // api target 1 重点: org.loader.app4.ImageViewAttrAdapter.setSrc(this.imageView, resImgId); } if ((dirtyFlags & 0x28L) != 0) { // api target 1 this.mboundView1.setOnClickListener(androidViewViewOnCli); } if ((dirtyFlags & 0x31L) != 0) { // api target 1 this.mboundView1.setText(nameStu); } if ((dirtyFlags & 0x24L) != 0) { // api target 1 org.loader.app4.ImageViewAttrAdapter.setSrc(this.mboundView2, bitmap); } }
org.loader.app4.ImageViewAttrAdapter.setSrc(this.imageView, resImgId);
可以看到最终调用了静态方法ImageViewAttrAdapter.setSrc(this.imageView, resImgId)方法了。
没有纠结细节部分,只是简单的分析整个调用过程。
总结:(1)android:src="@{resImgId}",会根据resImgId生成binding的setResImgId(int resId)方法。
(2)@BindingAdapter({"android:src"}),最终生成了相关的代码中,binding的setResImgId(int resId)会调用这个静态setSrc(ImageView view, int resId)方法
(分析过程如上)。
@BindingAdapter({"android:src"}) public static void setSrc(ImageView view, int resId) { view.setImageResource(resId); }
更多相关文章
- Android在任意位置获取应用程序Context
- Android(安卓)SDK离线安装方法详解(加速安装)
- 获得手机相关信息的实现方法
- Android(安卓)Handler.removeMessage移除所有postDelayed的问题
- Android(安卓)之 自定义标签 和 自定义组件 TypedArray
- 阿里Android(安卓)26条规范经验及优化
- Android(安卓)Studio一些使用快捷键
- Android中存储目录
- android操作SQLite