Android之缩减apk大小[资源文件篇]:基于自定义Shape设置ShapeDrawable
过去由于设计湿的吹毛求疵,往往不得不多做很多工作。比如下面的这张图片,很典型的按钮背景图片,一般Android会处理成.9文件以供项目使用。
![button_recharge.png](https://images2015.cnblogs.com/blog/781056/201608/781056-20160826085220272-1432884826.png)
但事情往往没有这么简单,需求会要求在不同的地方高度不一致,这个时候.9文件的弱点就出现了。
我们拿到UI那边的标注以及切图时,会默认高度是不会拉伸或者压缩的,下面截图中右中为期望效果。但是如果我们在应用中使用同一张.9图,就不能确保图片是否会高度拉伸。作为替代方案,只能让UI配合给出N套不同高度的.9文件进行适配。
![](https://images2015.cnblogs.com/blog/781056/201608/781056-20160826085221288-56546434.png)
上面这种解决方案无形之中增大了安装包的大小,以前见识过使用xml定义shape实现圆角矩形的示例,可以考虑使用代码动态生成Drawable对象,处理好左右两边的圆角弧度即可。
xml的shape属性对应于ShapeDrawable对象,下面给出ShapeDrawable结合RoundRectShape生成圆角矩形的范例
int outRadius = 100;
int innerRadius = 0;
/*圆角矩形*/
float[] outerRadii = {outRadius, outRadius, 0, 0, 0, 0, outRadius, outRadius};//左上x2,右上x2,右下x2,左下x2,注意顺序(顺时针依次设置)
int spaceBetOutAndInner = outRadius - innerRadius;//内外矩形间距
RectF inset = new RectF(spaceBetOutAndInner, spaceBetOutAndInner, spaceBetOutAndInner, spaceBetOutAndInner);
float[] innerRadii = {innerRadius, innerRadius, 0, 0, 0, 0, 0, 0};//内矩形 圆角半径
RoundRectShape roundRectShape = new RoundRectShape(outerRadii, inset, innerRadii);
ShapeDrawable drawable = new ShapeDrawable(roundRectShape);
drawable.getPaint().setColor(Color.RED);
drawable.getPaint().setAntiAlias(true);
drawable.getPaint().setStyle(Paint.Style.FILL);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
btnActmain.setBackground(drawable);
} else {
btnActmain.setBackgroundDrawable(drawable);
}
查看RoundRectShape源码可知,其在构造函数中传入内外矩形的圆角半径以及内外矩形间距,因为我们这里的圆角半径需要根据drawable的高度动态修改,查看父类RectShape源码有protect 类型的rect()方法实现,
/**
* Returns the RectF that defines this rectangle's bounds.
*/
protected final RectF rect() {
return mRect;
}
所以修改RoundRectShape的构造函数,将传入参数延迟初始化。
另观察RectShape得知在onResize中进行mRect的初始化,因此我们选择在调用父类的onResize之后,传入原RoundRectShape构造方法的相关参数。
@Override
protected void onResize(float width, float height) {
mRect.set(0, 0, width, height);
}
最后,看看自定义CircleArcRectangleShape类的源码实现,我们让其继承至RectShape,并将RoundRectShape的源码复制过来,主体修改部分如下(其中,clone()方法需要修改一下返回值,这里不予贴出):
**
* @Description:
* @author: Xiaoxuan948
* @date: 2016/8/23 9:41
*/
public class CircleArcRectangleShape extends RectShape {
private final UtilsLog lg = UtilsLog.getLogger(CircleArcRectangleShape.class);
//修改构造函数
public CircleArcRectangleShape() {
mPath = new Path();
}
//修改onResize方法
@Override
protected void onResize(float w, float h) {
super.onResize(w, Math.min(w, h));//表示在高大于宽时,默认显示为圆形
initCircleArcRectangleRadius();//onResize中添加这一句即可
RectF r = rect();
mPath.reset();
if (mOuterRadii != null) {
mPath.addRoundRect(r, mOuterRadii, Path.Direction.CW);
} else {
mPath.addRect(r, Path.Direction.CW);
}
if (mInnerRect != null) {
mInnerRect.set(r.left + mInset.left, r.top + mInset.top,
r.right - mInset.right, r.bottom - mInset.bottom);
if (mInnerRect.width() < w && mInnerRect.height() < h) {
if (mInnerRadii != null) {
mPath.addRoundRect(mInnerRect, mInnerRadii, Path.Direction.CCW);
} else {
mPath.addRect(mInnerRect, Path.Direction.CCW);
}
}
}
}
private void initCircleArcRectangleRadius() {
float outRadius = rect().height();
float innerRadius = outRadius;//建议设置成与outRadius一致,可防止内外矩形覆盖异常
float spaceBetOutAndInner = rect().height() / 2 - 1;//内外环的间距,这样设置会使得中间存在一根白线区域
lg.e("initCircleArcRectangleRadius", outRadius, innerRadius, spaceBetOutAndInner);
float[] outerRadii = {outRadius, outRadius, outRadius, outRadius, outRadius, outRadius, outRadius, outRadius};//左上x2,右上x2,右下x2,左下x2,注意顺序(顺时针依次设置)
RectF inset = new RectF(spaceBetOutAndInner, spaceBetOutAndInner, spaceBetOutAndInner, spaceBetOutAndInner);
float[] innerRadii = {innerRadius, innerRadius, innerRadius, innerRadius, innerRadius, innerRadius, innerRadius, innerRadius};//内矩形圆角半径
mOuterRadii = outerRadii;
mInset = inset;
mInnerRadii = innerRadii;
if (inset != null) {
mInnerRect = new RectF();
}
}
}
以后调用下面的代码,系统将自动根据View的高度生成左右为圆角的按钮背景图片,一套代码完美替换冗余的资源文件。
ShapeDrawable drawable = new ShapeDrawable(new CircleArcRectangleShape ());