当你想去使用一些常用的图形,ShapeDrawable对象可能会对你有很大的帮助。当然通过ShapeDrawable,你可以通过编程画出任何你想到的图像与样式,因为ShapeDrawable有自己的draw()方法。
ShapeDrawable继承了Drawable,所以你可以调用Drawable里有的函数,使用方法和其他的Drawable的子类差不多。下面我们来介绍它的特色。
通过ShapeDrawable的XML构造文件来了解ShapeDrawable的特性。下面是一个完整图形所包含的所有元素。
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape=["rectangle" | "oval" | "line" | "ring"]> <gradient android:angle="integer" android:centerX="integer" android:centerY="integer" android:centerColor="integer" android:endColor="color" android:gradientRadius="integer" android:startColor="color" android:type=["linear" | "radial" | "sweep"] android:usesLevel=["true" | "false"] /> <solid android:color="color" /> <stroke android:width="integer" android:color="color" android:dashWidth="integer" android:dashGap="integer" /> <padding android:left="integer" android:top="integer" android:right="integer" android:bottom="integer" /> <corners android:radius="integer" android:topLeftRadius="integer" android:topRightRadius="integer" android:bottomLeftRadius="integer" android:bottomRightRadius="integer" /> </shape> |
1)android:shape—定义Shape的类型。有效的值包括:"rectangle"矩形,为默认形状。"oval",椭圆。"line",水平直线。需要<stroke>元素定义线的宽度。"ring",环形。
2)<gradient>—为Shape指定渐变色。
l android:angle—Integer。渐变色的角度值。0表示从左到右,90表示从下到上。必须是45的倍数,默认是0。
l android:centerX—Float。渐变色中心的X相对位置(0-1.0)。当android:type=”linear”时无效。
l android:centerY—Float。渐变色中心的Y相对位置(0-1.0)。当android:type=”linear”时无效。
l android:centerColor—Color。可选的颜色,出现在start和end颜色之间。
l android:endColor—Color。end颜色。
l android:gradientRadius—Float。渐变色的半径。当android:type="radial"时有效,而且必须设置。
l android:startColor—Color。start颜色。
l android:type—Keyword。渐变色的样式。有效值为:"linear",线性渐变,默认值。"radial",环形渐变。start颜色是处于中间的颜色。"sweep",梯度渐变。"sweep"与"radial"不同的是,"radial"的颜色是从内往外渐变,"sweep"的颜色是从0°到360°渐变。
l android:useLevel—Boolean。"true"表示可以当作LevelListDrawable使用。
l android:innerRadius—Dimension。内环的半径。(只能在android:shape="ring"时使用)
l android:innerRadiusRatio—Float。以环的宽度比率来表示内环的半径。例如,如果android:innerRadiusRatio="5",内环半径等于环的宽度除以5。这个值可以被android:innerRadius覆盖。默认值是9。(只能在android:shape="ring"时使用)
l android:thickness—Dimension。环的厚度。(只能在android:shape="ring"时使用)
l android:thicknessRatio—Float。以环的宽度比率来表示环的厚度。例如,如果android:thicknessRatio="2",厚度就等于环的宽度除以2。这个值可以被android:thickness覆盖。默认值是3。(只能在android:shape="ring"时使用)
3)<solid>—填充shape的单一色。
l android:color—Color。这个颜色会应用到shape上。
4)<stroke>— shape的线形。
l android:width—Dimension。线的宽度。
l android:color— Color。线的颜色。
l android:dashGap—Dimension。线断与线段之间的空白距离。仅在android:dashWidth设定时有效。
l android:dashWidth—Dimension。线段的长度。仅在android:dashGap设定时有效。
5)<padding>— Dimension。内部View元素的边距。
6)< corners >—为shape创建圆角。当shape是一个“rectangle”时有效。
l android:radius—Dimension。圆角的半径。会被下面的特性覆盖。
l android:topLeftRadius—Dimension。左上圆角半径。
l android:topRightRadius—Dimension。右上圆角半径。
l android:bottomLeftRadius—Dimension。实际是右下角圆角半径。(android的bug)
l android:bottomRightRadius—Dimension。实际是左下圆角半径。(android的bug)
在代码中可以直接使用ShapeDrawable的XML文件:
Drawable dr = (Drawable)getResources().getDrawable(R.drawable.shape_1); dr.setBounds(x, y, x + width, y + height); dr.draw(canvas); |
下面提供一个例子,使用代码来实现如图7-2的效果:
图7-2 ShapeDrawable效果图
下面是具体的代码。
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <gradient android:startColor="#FFFF0000" android:endColor="#FF0000FF" android:centerColor="#FF00FF00" android:angle="315"/> <corners android:topLeftRadius="8dp" android:bottomLeftRadius="8dp" /> </shape> |
经验分享: 这里对ShapeDrawable的属性进行了详细的解释,是因为在手机软件的设计中,APK包的大小和内存的大小一直是困扰我们的问题,图片又是这些问题中的一个重头戏,而一个简单的xml可以实现一张图片的效果,那么我们就可以减少APK包的大小和内存,何乐而不为呢!同时也很方便的修改,这么有用的东西,希望大家用好它。 同时通过上面的例子我们找到一个Android的问题。android:bottomLeftRadius="8dp",从android的api的说明是左下圆角,而实际设置里却是右下圆角,android:bottomLeftRadius和android:bottomRightRadius是相反的。可能与GradientDrawable.setCornerRadii(float[8] radii)中设置的圆角顺序是相同的,radii是一个长度为8的float数组,设置的是按照顺时针的顺序。 |
上面主要讲的是通过XML文件来产生一个形状的Drawable,我们也可以通过代码来实现。使用ShapeDrawable的构造函数(publicShapeDrawable(Shape s))来定义一个形状,然后使用mShapeDrawable.getPaint().setShader(Shadershader)来设置ShapeDrawable画笔的渲染方式。这里涉及到两个类android.graphics.drawable.Shapes.Shape,android.graphics.Shader。
图7-3是android.graphics.drawable.Shapes.Shape及它的子类。
图7-3 Shape及它的子类
下面对其子类简单说明一下。
PathShape 使用Path类创建几何路径。
RectShape定义一个矩形。
ArcShape定义一个弧形。
OvalShape定义一个椭圆形。
RoundRectShape定义一个圆角矩形。
在Android中提供了Shader类及其子类,主要是用来渲染图像。图7-4说明了Shader 类的子类。
图7-4 Shader 及它的子类
下面对其子类简单说明一下。
BitmapShader主要用来渲染图像。
LinearGradient 用来进行梯度渲染。
RadialGradient 用来进行环形渲染。
SweepGradient 用来进行梯度渲染。
ComposeShader则是一个混合渲染。
有了上面的基础,我们就可以来实现比较复杂的图形。如图7-5所示:
图7-5 实现复杂的图形效果
是不是很炫,下面提供代码以仔细说明。
// import略 public class ShapeDrawable1 extends GraphicsActivity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new SampleView(this)); }
private static class SampleView extends View { private ShapeDrawable[] mDrawables; private static Shader makeSweep() { /* * * 创建SweepGradient并设置渐变的颜色数组 * 第一个 中心点的x坐标 * 第二个 中心点的y坐标 * 第三个 这个也是一个数组用来指定颜色数组的相对位置 * 如果为null 就沿坡度线均匀分布 * 第四个 设置一个位置数组相对应的颜色数组,从0到1.0。 * 如果位置是null,就将颜色自动均匀的分布。 */ return new SweepGradient(150, 25, new int[] { 0xFFFF0000, 0xFF00FF00, 0xFF0000FF, 0xFFFF0000 }, null); } private static Shader makeLinear() { /* * * 创建LinearGradient并设置渐变的颜色数组 * 第一个 起始的x坐标 * 第二个 起始的y坐标 * 第三个 结束的x坐标 * 第四个 结束的y坐标 * 第五个 颜色数组 * 第六个 这个也是一个数组用来指定颜色数组的相对位置 * 如果为null 就沿坡度线均匀分布 * 第七个 渲染模式 */ return new LinearGradient(0, 0, 50, 50, new int[] { 0xFFFF0000, 0xFF00FF00, 0xFF0000FF },null, Shader.TileMode.MIRROR); }
private static Shader makeTiling() { int[] pixels = new int[] { 0xFFFF0000, 0xFF00FF00, 0xFF0000FF, 0}; Bitmap bm = Bitmap.createBitmap(pixels, 2, 2, Bitmap.Config.ARGB_8888); /* 创建一个重复绘画的BitmapShader * */ return new BitmapShader(bm, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); }
/* 自定义一个ShapeDrawable 重写onDraw()方法 * */ private static class MyShapeDrawable extends ShapeDrawable { private Paint mStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG); public MyShapeDrawable(Shape s) { super(s); mStrokePaint.setStyle(Paint.Style.STROKE); } public Paint getStrokePaint() { return mStrokePaint; } @Override protected void onDraw(Shape s, Canvas c, Paint p) { s.draw(c, p); s.draw(c, mStrokePaint); } }
public SampleView(Context context) { super(context); setFocusable(true);
float[] outerR = new float[] { 12, 12, 12, 12, 0, 0, 0, 0 }; RectF inset = new RectF(6, 6, 6, 6); float[] innerR = new float[] { 12, 12, 0, 0, 12, 12, 0, 0 };
Path path = new Path(); path.moveTo(50, 0); path.lineTo(0, 50); path.lineTo(50, 100); path.lineTo(100, 50); path.close();
mDrawables = new ShapeDrawable[7]; mDrawables[0] = new ShapeDrawable(new RectShape()); mDrawables[1] = new ShapeDrawable(new OvalShape()); mDrawables[2] = new ShapeDrawable(new RoundRectShape(outerR, null, null)); mDrawables[3] = new ShapeDrawable(new RoundRectShape(outerR, inset, null)); mDrawables[4] =new ShapeDrawable(new RoundRectShape(outerR, inset, innerR)); /** * 创建一个PathShape,参数说明: * 第一个 PathShape的路径 * 第二个 x坐标缩放的原始比例,例如: ShapeDrawable实际大小为200, * 那么显示出来的Path图形的x轴是原始的200/100倍 * 第三个 y坐标缩放的原始比例 */ mDrawables[5] = new ShapeDrawable(new PathShape(path, 100, 100)); /** * 创建一个弧形ArcShape,参数说明: * 第一个 开始的角度(注意起始角度在右侧水平为0°角,顺时针增大) * 第二个 弧度延伸的角度,正数为顺时针延伸 */ mDrawables[6] = new MyShapeDrawable(new ArcShape(45, -270));
mDrawables[0].getPaint().setColor(0xFFFF0000); mDrawables[1].getPaint().setColor(0xFF00FF00); mDrawables[2].getPaint().setColor(0xFF0000FF); mDrawables[3].getPaint().setShader(makeSweep()); mDrawables[4].getPaint().setShader(makeLinear()); mDrawables[5].getPaint().setShader(makeTiling()); mDrawables[6].getPaint().setColor(0x88FF8844);
PathEffect pe = new DiscretePathEffect(10, 4); PathEffect pe2 = new CornerPathEffect(4); mDrawables[3].getPaint().setPathEffect(new ComposePathEffect(pe2, pe)); MyShapeDrawable msd = (MyShapeDrawable)mDrawables[6]; msd.getStrokePaint().setStrokeWidth(4); }
@Override protected void onDraw(Canvas canvas) { int x = 10; int y = 10; int width = 300; int height = 50; // 画出所有Drawable for (Drawable dr : mDrawables) { dr.setBounds(x, y, x + width, y + height); dr.draw(canvas); y += height + 5; } } } } |
AnimationDrawable是Android实现动画的一种简单的形式,可以用来实现帧动画。后续有一章节会详细介绍Android的动画。
下面我们来实现一个按钮点击后自动播放不同图片的AnimationDrawable的例子:
1)在res/drawable下定义animation_1.xml文件:
<?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false" > <item android:drawable="@drawable/a1" android:duration="500"></item> <item android:drawable="@drawable/a2" android:duration="500"></item> </animation-list> |
根标签为animation-list,其中android:oneshot代表着是否只展示一遍,设置为false会不停的循环播放动画,根标签下,通过item标签对动画中的每一个图片进行声明,android:duration表示展示所用的该图片的时间长度。
2)在java代码中载入:
Button mButton = (Button)findViewById(R.id.your_btn); mButton.setBackgroundResource(R.drawable. animation_1); AnimationDrawable mAnimationDrawable = (AnimationDrawable) mButton.getBackground(); // 或当做AnimationDrawable单独拿出来: AnimationDrawable mAnimationDrawable = (AnimationDrawable) getResources().getDrawable(R.drawable.animation_1); |
3)执行动画
// 动画是否正在运行 if (mAnimationDrawable.isRunning()) { // 停止动画播放 mAnimationDrawable.stop(); } else { // 开始或者继续动画播放 mAnimationDrawable.start(); } |
经验分享: 默认情况下,在onCreate()中执行animation.start();是无效的,因为在onCreate()中AnimationDrawable还没有完全的与Button绑定,在onCreate()中启动动画,就只能看到第一张图片。 解决的办法,在其它事件中响应触发动画。 |