Android开发艺术探索读书笔记系列
Drawable是一个抽象类,它有众多子类如ShapeDrawable BitmapDrawable等. 它有一个重要的参数是内部宽高,通过getIntrinsicWidth
和getIntrinsicHeight
获取! 内部大小不等于它的大小,一般来说Drawable没有大小,当其作为View的背景时,Drawable会被拉伸至View的同等大小!
Drawable的分类及层次关系
BitmapDrawable
它表示一张图片,实际开发中可以直接引用图片,也可以通过XML的方式描述,这种方式可以实现更多效果,它有众多属性:
- android:src 图片资源id
- android:antialias 是否开启抗锯齿
- android:dither 是否开启抖动效果
- android:filter 是否开启过滤效果
- android:mipMap 纹理映射
- android:tileMode 平铺模式
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/ic_test"
android:antialias="true"
android:dither="true"
android:filter="true"
android:gravity="center"
android:tileMode="mirror"
/>
NinePatchDrawable
NinePatchDrawable表示一张.9格式的图片,各属性和BitmapDrawable一样!
ShapeDrawable
ShapeDrawable可以理解为一种用颜色来构造的图形, 语法比较复杂,其子节点有corners、gradient、padding、size、solid、stroke
, 它有一个属性shape有四个选项:rectangle(矩形)、oval(椭圆)、line(线)、ring(圆环)
, 默认是矩形,当其值是line或ring时必须要有子节点stroke
来指定线的宽度和颜色,否则无效!
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:innerRadius="100dp"
android:shape="ring"
android:antialias="true"
android:useLevel="false">
<!--设置渐变色和solid不能共存-->
<gradient
android:endColor="@color/colorPrimary"
android:centerColor="#000"
android:type="sweep"
android:startColor="#fff" />
<size
android:width="300dp"
android:height="300dp" />
<stroke
android:width="1dp"
android:color="#fff" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="line">
<stroke
android:dashWidth="10dp"
android:dashGap="5dp"
android:width="5dp"
android:color="#868" />
</shape>
LayerDrawable
LayerDrawable对应XML标签的<layer-list>,他是一种层次化的Drawable集合!可以包含多个item,每一个item表示一个Drawable, 下面的item覆盖上面的,从而产生一些叠加效果!
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!--最底层是一层蓝色-->
<item>
<shape android:shape="rectangle">
<solid android:color="@color/colorPrimary" />
</shape>
</item>
<!--然后铺上一层白色 并控制偏移量 内边距-->
<item
android:bottom="5dp"
android:left="8dp"
android:right="2dp">
<shape android:shape="rectangle">
<solid android:color="#ffffff" />
<padding android:left="16dp"
android:right="15dp"
android:bottom="8dp"
/>
</shape>
</item>
</layer-list>
下图是上面几个例子的运行结果(不包过那条线):
StateListDrawable
StateListDrawable对应于<selector>标签, 也是表示Drawable集合! 每个Drawable对应View的一种状态, 系统根据View的状态选择合适的Drawable!
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!--pressed-->
<item android:state_pressed="true" android:drawable="@color/colorPrimary"/>
<!--focused-->
<item android:state_focused="true" android:drawable="@color/colorAccent"/>
<!--default-->
<item android:drawable="@color/gray"/>
</selector>
LevelListDrawable
LevelListDrawable对应于<level-list>标签, 同样表示一个Drawable集合! 每个Drawable都有一个level的概念, 根据不同等级切换不同的Drawable!
当其作为View的背景时, 可以通过Drawable的getLevel和setLevel方法操作, 范围0~10000, 最小等级0,这也是默认值! 当其作为ImageView前景时,也可以使用setImageLevel方法来切换Drawable!
<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@color/colorPrimary"
android:maxLevel="0" />
<item
android:drawable="@color/gray"
android:maxLevel="1" />
</level-list>
TransitionDrawable
TransitionDrawable对应于<transition>, 它用于实现连个Drawable之间的淡入淡出效果.
<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/colorPrimary" />
<item android:drawable="@color/gray" />
</transition>
然后通过View的startTransition和reverseTransition方法来实现淡入淡出的效果以及它的逆过程, 方法参数就是其变换的时长, 单位毫秒!
InsetDrawable
InsetDrawable对应于<inset>, 它可以将其他Drawable内嵌到自己当中,并可以在四周留出一定的间距, 当一个View希望其背景小于自己的实际区域时,可以用InsetDrawable, 当然LayerDrawable也可以!
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetBottom="20dp"
android:insetLeft="30dp"
android:drawable="@drawable/myshape"
/>
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:inset="10dp"
>
<shape android:shape="oval" >
<solid android:color="@color/colorAccent"/>
</shape>
</inset>
ScaleDrawable
ScaleDrawable对应于<scale>, 可根据自己的level将指定的Drawable缩放到一定比例! 属性android:scaleWidth
和android:scaleHeight
分别表示宽高的缩放比例! 有一点比较费解就是 等级为0表示 表示ScaleDrawable不可见,这也是默认值,要想可见,还需手动设置level不为0!
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:scaleWidth="60%"
android:scaleHeight="60%"
android:drawable="@drawable/myshape"
/>
myScale.getBackground().setLevel(1);
上面xml里面表示将图片缩小到原来的40%即缩小了60%, 值越大显示的越小; 下面的level(0 ~ 10000)正好相反,值越大显示越大, 0表示不显示, level设置为10000表示没有缩放效果! 具体level的缩放比例可以在ScaleDrawable的onBoundsChange
方法里看到:
@Override
protected void onBoundsChange(Rect bounds) {
final Drawable d = getDrawable();
final Rect r = mTmpRect;
final boolean min = mState.mUseIntrinsicSizeAsMin;
final int level = getLevel();
//宽度的缩放
int w = bounds.width();
if (mState.mScaleWidth > 0) {
final int iw = min ? d.getIntrinsicWidth() : 0;
w -= (int) ((w - iw) * (MAX_LEVEL - level) * mState.mScaleWidth / MAX_LEVEL);
}
//高度的缩放
int h = bounds.height();
if (mState.mScaleHeight > 0) {
final int ih = min ? d.getIntrinsicHeight() : 0;
h -= (int) ((h - ih) * (MAX_LEVEL - level) * mState.mScaleHeight / MAX_LEVEL);
}
final int layoutDirection = getLayoutDirection();
Gravity.apply(mState.mGravity, w, h, bounds, r, layoutDirection);
if (w > 0 && h > 0) {
d.setBounds(r.left, r.top, r.right, r.bottom);
}
}
以宽度为例MAX_LEVEL为10000, iw一般为0, 缩放比例公式可以简化为w -= (int) (w * (10000 - level) * mState.mScaleWidth / 10000);
ClipDrawable
ClipDrawable对应于<clip>, 它可以根据自己的level(0~10000)来裁剪一个Drawable,默认值0表示完全裁剪即不可见, 10000表示不裁剪! 裁剪方向可以通过android:clipOrientation
和android:gravity
这两个属性来共同控制!clipOrientation表示裁剪方向, gravity比较复杂需要和clipOrientation配合才能起到作用!
gravity属性:
<?xml version="1.0" encoding="utf-8"?>
<clip xmlns:android="http://schemas.android.com/apk/res/android"
android:clipOrientation="vertical"
android:drawable="@drawable/ic_test"
android:gravity="top"
/>
myclip.getBackground().setLevel(8000);//表示裁剪了20%