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

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 类的子类。

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

图7-4 Shader 及它的子类

 

下面对其子类简单说明一下。

BitmapShader主要用来渲染图像。

LinearGradient 用来进行梯度渲染。

RadialGradient 用来进行环形渲染。

SweepGradient 用来进行梯度渲染。

ComposeShader则是一个混合渲染。

 

有了上面的基础,我们就可以来实现比较复杂的图形。如图7-5所示:

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

图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()中启动动画,就只能看到第一张图片。

解决的办法,在其它事件中响应触发动画。

 

你可能感兴趣的:(Android开发,android应用,经验分享)