Databinding简单的分析ImageView属性android:src="@{resImgId}"的调用流程

首先看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);
    }




你可能感兴趣的:(Databinding简单的分析ImageView属性android:src="@{resImgId}"的调用流程)