您的位置:首页 > 其它

第七章 实现炫酷效果—图像和动画(3)

2015-05-31 12:33 399 查看

7.3.6超炫的特效—ShapeDrawable

当你想去使用一些常用的图形,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;
}
}
}
}

7.3.7简单的帧动画—AnimationDrawable

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()中启动动画,就只能看到第一张图片。
解决的办法,在其它事件中响应触发动画。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: