首先看activity_main.xml中的代码:

                                                


这里定义了一个int类型的变量resImgId,和MainActivity类型的变量。

并且给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);


我们找到生成的binding类中的setResImgId(int resId)方法,这个方法是根据xml中的resImgId属性会自动生成setter和getter方法,属于Databinding的基本概念。
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);    }




更多相关文章

  1. Android在任意位置获取应用程序Context
  2. Android(安卓)SDK离线安装方法详解(加速安装)
  3. 获得手机相关信息的实现方法
  4. Android(安卓)Handler.removeMessage移除所有postDelayed的问题
  5. Android(安卓)之 自定义标签 和 自定义组件 TypedArray
  6. 阿里Android(安卓)26条规范经验及优化
  7. Android(安卓)Studio一些使用快捷键
  8. Android中存储目录
  9. android操作SQLite

随机推荐

  1. Android中proguard的作用
  2. android jni 包裹文件(jni wrapper) 以 spe
  3. CardView兼容处理
  4. 图解Android(安卓)- Zygote, System Serv
  5. React-native Android(安卓)react-native
  6. 关于调节亮度在小米手机上异常的情况
  7. ConnectivityManager详解
  8. Android(安卓)获取全国城市列表类似于联
  9. Android(安卓)SharedPreference 源码分析
  10. Android(安卓)Studio中使用*.jar,*.aar和*