咱们的目的是研究Button
设置android:background
的过程以及实现。php
首先要分两种状况。
第一种状况,Button
所在的Activity
继承于AppCompatActivity
,这时使用Android Studio
中的Layout Inspector
工具解析屏幕中的控件,会发现Button
被替换成了AppCompatButton
。html
第二种状况,Button
所在的Activity
继承于Activity
,这种状况下,解析屏幕,Button
没有被替换。java
那么首先研究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
Button
的构造方法最终是调用TextView
的四个参数构造方法。app
public TextView( Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes){}复制代码
通过对这个构造方法里检索,并无发现与background
有关的代码,那么,真正的与background
有关的代码必定在TextView
的父类View
里。工具
在View
中的四个参数构造方法中,找到了这样的代码:学习
int attr = a.getIndex(i);
switch (attr) {
case com.android.internal.R.styleable.View_background:
background = a.getDrawable(attr);
break;
...
}复制代码
追踪的过程按下不表,这里说一说最终的结果。ui
若是这个drawable
是ColorDrawable
的话,那么就会实例化一个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
中的静态方法createFromResourceStream
和createFromXml
方法。
这里就是先实例化一个Bitmap
对象,而后判断这个Bitmap
是否为.9
图。若是是的话,则根据Bitmap
生成一个NinePatchDrawable
,不然,生成一个BitmapDrawable
。
这里最终是调用了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
。
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
属性对应的方法:
在GradientDrawable
中使用了一个GradientState
内部类对象保存状态(即在xml
里设置的那些属性)。
最后就是在GradientDrawable
的draw
方法中进行绘制。
好了,这就是为Button
设置一个android:background
的大体过程。
想要了解更多的话,请移步本人的学习笔记,若是以为有帮助的话,请点一个star。