Android中Button设置background过程的研究

咱们的目的是研究Button设置android:background的过程以及实现。php

首先要分两种状况。
第一种状况,Button所在的Activity继承于AppCompatActivity,这时使用Android Studio中的Layout Inspector工具解析屏幕中的控件,会发现Button被替换成了AppCompatButtonhtml

第二种状况,Button所在的Activity继承于Activity,这种状况下,解析屏幕,Button没有被替换。java

Button

那么首先研究Button的源码,Button的源码不多,只是实现了4个构造方法和一个getAccessibilityClassName方法。android

而可以产生Button与其父类(TextView)的差别是体如今第二个构造方法里git

public Button(Context context, AttributeSet attrs) {
    this(context, attrs, com.android.internal.R.attr.buttonStyle);
}复制代码

这里,将defStyleAttr(即默认的Style)设置为com.android.internal.R.attr.buttonStyle,因此正是由于这个属性,Button才与TextView显示得不同。github

TextView

Button的构造方法最终是调用TextView的四个参数构造方法。app

public TextView( Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes){}复制代码

通过对这个构造方法里检索,并无发现与background有关的代码,那么,真正的与background有关的代码必定在TextView的父类View里。工具

View

View中的四个参数构造方法中,找到了这样的代码:学习

int attr = a.getIndex(i);
switch (attr) {
    case com.android.internal.R.styleable.View_background:
        background = a.getDrawable(attr);
        break;
    ...
}复制代码

追踪的过程按下不表,这里说一说最终的结果。ui

若是这个drawableColorDrawable的话,那么就会实例化一个ColorDrawable对象,不然,就会调用loadDrawableForCookie方法,即从XML中或者resources stream中加载drawable

loadDrawableForCookie方法中的主要逻辑是这样的:

if (file.endsWith(".xml")) {
    final XmlResourceParser rp = loadXmlResourceParser(
            file, id, value.assetCookie, "drawable");
    dr = Drawable.createFromXml(wrapper, rp, theme);
    rp.close();
} else {
    final InputStream is = mAssets.openNonAsset(
            value.assetCookie, file, AssetManager.ACCESS_STREAMING);
    dr = Drawable.createFromResourceStream(wrapper, value, is, file, null);
    is.close();
}复制代码

那么,接下来咱们就了解一下Drawable中的静态方法createFromResourceStreamcreateFromXml方法。

createFromResourceStream

这里就是先实例化一个Bitmap对象,而后判断这个Bitmap是否为.9图。若是是的话,则根据Bitmap生成一个NinePatchDrawable,不然,生成一个BitmapDrawable

createFromXml

这里最终是调用了DrawableInflater.inflateFromXml方法,这个方法返回一个drawable,可是其实返回的是它的子类。下面这段代码就是根据XML中的TAG进行断定这个drawable是属于什么类型的。

private Drawable inflateFromTag(@NonNull String name) {
    switch (name) {
        case "selector":
            return new StateListDrawable();
        case "animated-selector":
            return new AnimatedStateListDrawable();
        case "level-list":
            return new LevelListDrawable();
        case "layer-list":
            return new LayerDrawable();
        case "transition":
            return new TransitionDrawable();
        case "ripple":
            return new RippleDrawable();
        case "color":
            return new ColorDrawable();
        case "shape":
            return new GradientDrawable();
        case "vector":
            return new VectorDrawable();
        case "animated-vector":
            return new AnimatedVectorDrawable();
        case "scale":
            return new ScaleDrawable();
        case "clip":
            return new ClipDrawable();
        case "rotate":
            return new RotateDrawable();
        case "animated-rotate":
            return new AnimatedRotateDrawable();
        case "animation-list":
            return new AnimationDrawable();
        case "inset":
            return new InsetDrawable();
        case "bitmap":
            return new BitmapDrawable();
        case "nine-patch":
            return new NinePatchDrawable();
        default:
            return null;
    }
}复制代码

到这里,终于见到了咱们熟悉的东西了。咱们这里就研究一下shape所对应的GradientDrawable

GradientDrawable

Gradient这个词咱们就很熟悉了,它就是shape中的渐变属性嘛,以下面的代码:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
    <!-- 设置边框 -->
    <stroke android:width="2dp" android:color="@android:color/black" />
    <!-- 设置颜色渐变 -->
    <gradient android:angle="45" android:endColor="@android:color/holo_orange_dark" android:startColor="@android:color/holo_blue_light" android:type="linear" />
    <corners android:radius="@dimen/activity_horizontal_margin" />
</shape>复制代码

效果图以下,是一个从橘黄色到蓝色的渐变:

GradientDrawable中提供了一些和xml属性对应的方法:

  • setCornerRadii - 设置边角半径
  • setStroke - 设置边框(宽度+颜色)
  • setSize
  • setShape - RECTANGLE | OVAL | LINE | RING
  • ...

GradientDrawable中使用了一个GradientState内部类对象保存状态(即在xml里设置的那些属性)。

最后就是在GradientDrawabledraw方法中进行绘制。

好了,这就是为Button设置一个android:background的大体过程。

想要了解更多的话,请移步本人的学习笔记,若是以为有帮助的话,请点一个star。

相关文章
相关标签/搜索