Android中动画的详细讲解

这里写图片描述逐帧动画:
这里写图片描述补间动画
这里写图片描述结合动画
这里写图片描述自定义动画
这里写图片描述属性动画一
这里写图片描述属性动画二
这里写图片描述动画三
这里写图片描述动画四

逐帧动画

定义逐帧动画,只要在<animation-list……>;元素中使用<item…/>子元素定义动画的全部帧就可以了!

语法格式:

<?xml version="1.0" encoding="utf-8"?>
<!-- 指定动画循环播放 -->
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <!-- 添加多个帧 -->
    <item android:drawable="图片" android:duration="60" />
    </animation-list >

MainActivity代码:

public class MainActivity extends Activity
{
    private MyView myView;
    private AnimationDrawable anim;
    private MediaPlayer bomb;
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
        // 使用FrameLayout布局管理器,它允许组件自己控制位置
        FrameLayout frame = new FrameLayout(this);
        setContentView(frame);
        // 设置使用背景
        frame.setBackgroundResource(R.drawable.back);
        // 加载音效
        bomb = MediaPlayer.create(this, R.raw.bomb);
        myView = new MyView(this);
        // 设置myView用于显示blast动画
        myView.setBackgroundResource(R.anim.blast);
        // 设置myView默认为隐藏
        myView.setVisibility(View.INVISIBLE);
        // 获取动画对象
        anim = (AnimationDrawable) myView.getBackground();
        frame.addView(myView);
        frame.setOnTouchListener(new OnTouchListener()
        {
            @Override
            public boolean onTouch(View source, MotionEvent event)
            {
                // 只处理按下事件(避免每次产生两个动画效果)
                if (event.getAction() == MotionEvent.ACTION_DOWN)
                {
                    // 先停止动画播放
                    anim.stop();
                    float x = event.getX();
                    float y = event.getY();
                    // 控制myView的显示位置
                    myView.setLocation((int) y - 40, (int) x - 20);
                    myView.setVisibility(View.VISIBLE);
                    // 启动动画
                    anim.start();
                    // 播放音效
                    bomb.start();
                }
                return false;
            }
        });
    }
    // 定义一个自定义View,该自定义View用于播放“爆炸”效果
    class MyView extends ImageView
    {
        public MyView(Context context)
        {
            super(context);
        }
        // 定义一个方法,该方法用于控制MyView的显示位置
        public void setLocation(int top, int left)
        {
            this.setFrame(left-100, top-100, left + 100, top + 100);
        }
        // 重写该方法,控制如果动画播放到最后一帧时,隐藏该View
        @Override
        protected void onDraw(Canvas canvas) // ①
        {
            try
            {
                Field field = AnimationDrawable.class
                        .getDeclaredField("mCurFrame");
                field.setAccessible(true);
                // 获取anim动画的当前帧
                int curFrame = field.getInt(anim);
                // 如果已经到了最后一帧
                if (curFrame == anim.getNumberOfFrames() - 1)
                {
                    // 让该View隐藏
                    setVisibility(View.INVISIBLE);
                }
            }
            catch (Exception e)
            {
            }
            super.onDraw(canvas);
        }
    }
}

补间动画(Tween)

资源定义完成后,可以使用AnimationUtils工具类加载指定的动画资源。

Animation子类:

  1. AlphaAnimation:
  2. ScaleAnimation:
  3. TranslateAnimation:
  4. RotateAnimation:

interpolator简单理解

interpolator:根据特定的算法计算出整个动画所需要动态插入帧的密度和位置。负责控制动画的变化速度。使其动画效果更流畅。

代码

public class MainActivity extends Activity
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        final ImageView flower = (ImageView)
                findViewById(R.id.flower);
        // 加载第一份动画资源
        final Animation anim = AnimationUtils
                .loadAnimation(this, R.anim.anim);
        // 设置动画结束后保留结束状态
        anim.setFillAfter(true);
        // 加载第二份动画资源
        final Animation reverse = AnimationUtils.loadAnimation(this
                , R.anim.reverse);
        // 设置动画结束后保留结束状态
        reverse.setFillAfter(true);
        Button bn = (Button) findViewById(R.id.bn);
        final Handler handler = new Handler()
        {
            @Override
            public void handleMessage(Message msg)
            {
                if (msg.what == 0x123)
                {
                    flower.startAnimation(reverse);
                }
            }
        };
        bn.setOnClickListener(new OnClickListener()
        {
            @Override
            public void onClick(View arg0)
            {
                flower.startAnimation(anim);
                // 设置3.5秒后启动第二个动画
                new Timer().schedule(new TimerTask()
                {
                    @Override
                    public void run()
                    {
                        handler.sendEmptyMessage(0x123);
                    }
                }, 3500);
            }
        });
    }
}

结合动画

MainActivity代码:

public class MainActivity extends Activity
{
    // 记录蝴蝶ImageView当前的位置
    private float curX = 0;
    private float curY = 30;
    // 记录蝴蝶ImageView下一个位置的座标
    float nextX = 0;
    float nextY = 0;
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        // 获取显示蝴蝶的ImageView组件
        final ImageView imageView = (ImageView)
                findViewById(R.id.butterfly);
        final Handler handler = new Handler()
        {
            @Override
            public void handleMessage(Message msg)
            {
                if (msg.what == 0x123)
                {
                    // 横向上一直向右飞
                    if (nextX > 320)
                    {
                        curX = nextX = 0;
                    }
                    else
                    {
                        nextX += 8;
                    }
                    // 纵向上可以随机上下
                    nextY = curY + (float) (Math.random() * 10 - 5);
                    // 设置显示蝴蝶的ImageView发生位移改变
                    TranslateAnimation anim = new TranslateAnimation(
                            curX, nextX, curY, nextY);
                    curX = nextX;
                    curY = nextY;
                    anim.setDuration(200);
                    // 开始位移动画
                    imageView.startAnimation(anim); // ①
                }
            }
        };
        final AnimationDrawable butterfly = (AnimationDrawable)
                imageView.getBackground();
        imageView.setOnClickListener(new OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                // 开始播放蝴蝶振翅的逐帧动画
                butterfly.start();  // ②
                // 通过定制器控制每0.2秒运行一
                // 次TranslateAnimation动画
                new Timer().schedule(new TimerTask()
                {
                    @Override
                    public void run()
                    {
                        handler.sendEmptyMessage(0x123);
                    }
                }, 0, 200);
            }
        });
    }
}

自定义补间动画:

自定义补间动画需要继承Animation,重写抽象方法applyTransformation(float interpolatedTime,Transformation t)方法,参数说明:

  1. interpolatedTime:代表动画的时间进行比,无论动画实际的持续时间如何,当动画播放时,该参数总是从0变化到1
  2. Transformation:代表补间动画在不同时刻对图形或者组件的变形程度

Camera常用方法如下:

  1. getMatrix(Matrix matrix):将Camera所做的变换应用到指定matrix上。

  2. rotateX(float deg):使目标组件沿X轴旋转。

  3. rotateY(float deg):使目标组件沿Y轴旋转。

  4. rotateZ(float deg):使目标组件沿Z轴旋转。

  5. translate(float x,float y,float z):使目标组件在三维空间里进行位移变换

  6. applyToCanvas(Canvas canvas):把Camera所做的变换应用到Canvas上。

MyAnimation代码:

public class MyAnimation extends Animation
{
    private float centerX;
    private float centerY;
    // 定义动画的持续事件
    private int duration;
    private Camera camera = new Camera();
    public MyAnimation(float x, float y, int duration)
    {
        this.centerX = x;
        this.centerY = y;
        this.duration = duration;
    }
    @Override
    public void initialize(int width, int height
            , int parentWidth, int parentHeight)
    {
        super.initialize(width, height, parentWidth, parentHeight);
        // 设置动画的持续时间
        setDuration(duration);
        // 设置动画结束后效果保留
        setFillAfter(true);
        setInterpolator(new LinearInterpolator());
    }
    /*
     * 该方法的interpolatedTime代表了抽象的动画持续时间,不管动画实际持续时间多长,
     * interpolatedTime参数总是从0(动画开始时)~1(动画结束时)
     * Transformation参数代表了对目标组件所做的改变.
     */
    @Override
    protected void applyTransformation(float interpolatedTime
            , Transformation t)
    {
        camera.save();
        // 根据interpolatedTime时间来控制X、Y、Z上的偏移
        camera.translate(100.0f - 100.0f * interpolatedTime,
                150.0f * interpolatedTime - 150,
                80.0f - 80.0f * interpolatedTime);
        // 设置根据interpolatedTime时间在Y轴上旋转不同角度
        camera.rotateY(360 * (interpolatedTime));
        // 设置根据interpolatedTime时间在X轴上旋转不同角度
        camera.rotateX((360 * interpolatedTime));
        // 获取Transformation参数的Matrix对象
        Matrix matrix = t.getMatrix();
        camera.getMatrix(matrix);
        matrix.preTranslate(-centerX, -centerY);
        matrix.postTranslate(centerX, centerY);
        camera.restore();
    }
}

MainActivity代码:

public class MainActivity extends Activity
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        // 获取ListView组件
        ListView list = (ListView) findViewById(R.id.list);
        WindowManager windowManager = (WindowManager)
                getSystemService(WINDOW_SERVICE);
        Display display = windowManager.getDefaultDisplay();
        DisplayMetrics metrice = new DisplayMetrics();
        // 获取屏幕的宽和高
        display.getMetrics(metrice);
        // 设置对ListView组件应用动画
        list.setAnimation(new MyAnimation(metrice.xdpi / 2
                , metrice.ydpi / 2, 3500));
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
<ListView  
    android:id="@+id/list"
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:entries="@array/bookArray"
    />
</LinearLayout>

属性动画

定义属性动画的方法:

  1. 使用ValurAnimator或者ObjectAnimator的静态工厂方法来创建动画
  2. 使用资源文件来定义动画

使用属性动画的步骤

  1. 创建ValurAniamtor或ObjectAnimator对象,即可从XML资源加载,也可以使用ValurAniamtor或ObjectAnimator的静态工厂方法创建
  2. 根据需要设置Animator属性值
  3. 如果需要监听Animator的改变事件,需要为Animator设置事件监听器
  4. 如果有多个动画需要按次序或者同时处理,则需要使用AnimatorSet组合这些动画
  5. 调用Animator的start()方法来启动。

    属性动画和补间动画的区别:

  6. 补间动画只能定义两个关键帧在“透明度
    ”“旋转”“缩放”“位移”4个方面变化。属性动画可以定义任何属性的变化

  7. 补间动画只对UI组件执行动画,但属性动画几乎可以对任何对象执行动画,不管是否显示在屏幕上

属性动画的API:

  1. Animator:它提供创建属性动画的基类,通常用于被继承并重写它的相关方法
  2. ValueAnimator:用于计算相关属性值
  3. ObjectAnimator:ValueAnimator的子类,允许程序员对指定对象的属性执行动画。
  4. AnimatorSet:Animator的子类,用于组合多个Animator,并指定多个Animator按次序播放还是同时播放。
  5. IntEvaluator:用于计算int类型的计算器
  6. FloatEvaluator:用于计算float类型属性值的计算器
  7. ArgbEvaluator:用于计算以十六进制形式表示的颜色值的计算器
  8. TypeEvaluator:计算器接口,开发者可以通过实现该接口来实现自定义计算器。

    属性动画一

    ShapeHolder代码:
public class ShapeHolder
{
    private float x = 0, y = 0;
    private ShapeDrawable shape;
    private int color;
    private RadialGradient gradient;
    private float alpha = 1f;
    private Paint paint;

    public ShapeHolder(ShapeDrawable s)
    {
        shape = s;
    }

    public float getX()
    {
        return x;
    }

    public void setX(float x)
    {
        this.x = x;
    }

    public float getY()
    {
        return y;
    }

    public void setY(float y)
    {
        this.y = y;
    }

    public ShapeDrawable getShape()
    {
        return shape;
    }

    public void setShape(ShapeDrawable shape)
    {
        this.shape = shape;
    }

    public int getColor()
    {
        return color;
    }

    public void setColor(int color)
    {
        this.color = color;
    }

    public RadialGradient getGradient()
    {
        return gradient;
    }

    public void setGradient(RadialGradient gradient)
    {
        this.gradient = gradient;
    }

    public float getAlpha()
    {
        return alpha;
    }

    public void setAlpha(float alpha)
    {
        this.alpha = alpha;
    }

    public Paint getPaint()
    {
        return paint;
    }

    public void setPaint(Paint paint)
    {
        this.paint = paint;
    }
}

MainActivity代码;

public class MainActivity extends Activity
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        // 获取ListView组件
        ListView list = (ListView) findViewById(R.id.list);
        WindowManager windowManager = (WindowManager)
                getSystemService(WINDOW_SERVICE);
        Display display = windowManager.getDefaultDisplay();
        DisplayMetrics metrice = new DisplayMetrics();
        // 获取屏幕的宽和高
        display.getMetrics(metrice);
        // 设置对ListView组件应用动画
        list.setAnimation(new MyAnimation(metrice.xdpi / 2
                , metrice.ydpi / 2, 3500));
    }
}

属性动画二:

ShapeHolder代码:

同属性动画一中的ShapeHolder代码:

MainActivity代码:

public class MainActivity extends Activity
{
    // 定义小球的大小的常量
    static final float BALL_SIZE = 50F;
    // 定义小球从屏幕上方下落到屏幕底端的总时间
    static final float FULL_TIME = 3000;
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        LinearLayout container = (LinearLayout)
            findViewById(R.id.container);
        // 设置该窗口显示MyAnimationView组件
        container.addView(new MyAnimationView(this));
    }
    public class MyAnimationView extends View
    {
        public final ArrayList<ShapeHolder> balls
                = new ArrayList<ShapeHolder>();
        public MyAnimationView(Context context)
        {
            super(context);
            // 加载动画资源
            ObjectAnimator colorAnim = (ObjectAnimator) AnimatorInflater
                .loadAnimator(MainActivity.this, R.animator.color_anim);
            colorAnim.setEvaluator(new ArgbEvaluator());
            // 对该View本身应用属性动画
            colorAnim.setTarget(this);
            // 开始指定动画
            colorAnim.start();
        }
        @Override
        public boolean onTouchEvent(MotionEvent event)
        {
            // 如果触碰事件不是按下、移动事件
            if (event.getAction() != MotionEvent.ACTION_DOWN
                && event.getAction() != MotionEvent.ACTION_MOVE)
            {
                return false;
            }
            //  在事件发生点添加一个小球(用一个圆形代表)
            ShapeHolder newBall = addBall(event.getX(), event.getY());
            // 计算小球下落动画开始时的y座标
            float startY = newBall.getY();
            // 计算小球下落动画结束时的y座标(落到屏幕最下方,就是屏幕高度减去小球高度)
            float endY = getHeight() - BALL_SIZE;
            // 获取屏幕高度
            float h = (float) getHeight();
            float eventY = event.getY();
            // 计算动画的持续时间
            int duration = (int) (FULL_TIME * ((h - eventY) / h));
            // 定义小球“落下”的动画:
            // 让newBall对象的y属性从事件发生点变化到屏幕最下方
            ValueAnimator fallAnim = ObjectAnimator.ofFloat(
                newBall, "y", startY, endY);
            // 设置fallAnim动画的持续时间
            fallAnim.setDuration(duration);
            // 设置fallAnim动画的插值方式:加速插值
            fallAnim.setInterpolator(new AccelerateInterpolator());
            // 定义小球“压扁”的动画:该动画控制小球的x座标“向左移”半个球
            ValueAnimator squashAnim1 = ObjectAnimator.ofFloat(newBall
                    , "x", newBall.getX(), newBall.getX() - BALL_SIZE / 2);
            // 设置squashAnim1动画持续时间
            squashAnim1.setDuration(duration / 4);
            // 设置squashAnim1动画重复1次
            squashAnim1.setRepeatCount(1);
            // 设置squashAnim1动画的重复方式
            squashAnim1.setRepeatMode(ValueAnimator.REVERSE);
            // 设置squashAnim1动画的插值方式:减速插值
            squashAnim1.setInterpolator(new DecelerateInterpolator());
            // 定义小球“压扁”的动画:该动画控制小球的宽度加倍
            ValueAnimator squashAnim2 = ObjectAnimator.ofFloat(newBall,
                    "width", newBall.getWidth()
                    , newBall.getWidth() + BALL_SIZE);
            // 设置squashAnim2动画持续时间
            squashAnim2.setDuration(duration / 4);
            // 设置squashAnim2动画重复1次
            squashAnim2.setRepeatCount(1);
            // 设置squashAnim2动画的重复方式
            squashAnim2.setRepeatMode(ValueAnimator.REVERSE);
            // 设置squashAnim2动画的插值方式:减速插值
            squashAnim2.setInterpolator(new DecelerateInterpolator());
            // 定义小球“拉伸”的动画:该动画控制小球的y座标“向下移”半个球
            ObjectAnimator stretchAnim1 = ObjectAnimator.ofFloat(newBall
                    , "y", endY, endY + BALL_SIZE / 2);
            // 设置stretchAnim1动画持续时间
            stretchAnim1.setDuration(duration / 4);
            // 设置stretchAnim1动画重复1次
            stretchAnim1.setRepeatCount(1);
            // 设置stretchAnim1动画的重复方式
            stretchAnim1.setRepeatMode(ValueAnimator.REVERSE);
            // 设置stretchAnim1动画的插值方式:减速插值
            stretchAnim1.setInterpolator(new DecelerateInterpolator());
            // 定义小球“拉伸”的动画:该动画控制小球的高度减半
            ValueAnimator stretchAnim2 = ObjectAnimator.ofFloat(newBall,
                    "height", newBall.getHeight()
                    , newBall.getHeight() - BALL_SIZE / 2);
            // 设置stretchAnim2动画持续时间
            stretchAnim2.setDuration(duration / 4);
            // 设置squashAnim2动画重复1次
            stretchAnim2.setRepeatCount(1);
            // 设置squashAnim2动画的重复方式
            stretchAnim2.setRepeatMode(ValueAnimator.REVERSE);
            // 设置squashAnim2动画的插值方式:减速插值
            stretchAnim2.setInterpolator(new DecelerateInterpolator());
            // 定义小球“弹起”的动画
            ObjectAnimator bounceBackAnim = ObjectAnimator.ofFloat(
                    newBall , "y", endY, startY);
            // 设置持续时间
            bounceBackAnim.setDuration(duration);
            // 设置动画的插值方式:减速插值
            bounceBackAnim.setInterpolator(new DecelerateInterpolator());
            // 使用AnimatorSet按顺序播放“掉落/压扁&拉伸/弹起动画
            AnimatorSet bouncer = new AnimatorSet();
            // 定义在squashAnim1动画之前播放fallAnim下落动画
            bouncer.play(fallAnim).before(squashAnim1);
            // 由于小球在“屏幕”下方弹起时,小球要被压扁
            // 即:宽度加倍、x座标左移半个球,高度减半、y座标下移半个球
            // 因此此处指定播放squashAnim1的同时
            // 还播放squashAnim2、stretchAnim1、stretchAnim2
            bouncer.play(squashAnim1).with(squashAnim2);
            bouncer.play(squashAnim1).with(stretchAnim1);
            bouncer.play(squashAnim1).with(stretchAnim2);
            // 指定播放stretchAnim2动画之后,播放bounceBackAnim弹起动画
            bouncer.play(bounceBackAnim).after(stretchAnim2);
            // 定义对newBall对象的alpha属性执行从1到0的动画(即定义渐隐动画)
            ObjectAnimator fadeAnim = ObjectAnimator.ofFloat(newBall
                    , "alpha", 1f, 0f);
            // 设置动画持续时间
            fadeAnim.setDuration(250);
            // 为fadeAnim动画添加监听器
            fadeAnim.addListener(new AnimatorListenerAdapter()
            {
                // 当动画结束时
                @Override
                public void onAnimationEnd(Animator animation)
                {
                    // 动画结束时将该动画关联的ShapeHolder删除
                    balls.remove(((ObjectAnimator) animation).getTarget());
                }
            });
            // 再次定义一个AnimatorSet来组合动画
            AnimatorSet animatorSet = new AnimatorSet();
            // 指定在播放fadeAnim之前,先播放bouncer动画
            animatorSet.play(bouncer).before(fadeAnim);
            // 开发播放动画
            animatorSet.start();
            return true;
        }
        private ShapeHolder addBall(float x, float y)
        {
            // 创建一个椭圆
            OvalShape circle = new OvalShape();
            // 设置该椭圆的宽、高
            circle.resize(BALL_SIZE, BALL_SIZE);
            // 将椭圆包装成Drawable对象
            ShapeDrawable drawable = new ShapeDrawable(circle);
            // 创建一个ShapeHolder对象
            ShapeHolder shapeHolder = new ShapeHolder(drawable);
            // 设置ShapeHolder的x、y座标
            shapeHolder.setX(x - BALL_SIZE / 2);
            shapeHolder.setY(y - BALL_SIZE / 2);
            int red = (int) (Math.random() * 255);
            int green = (int) (Math.random() * 255);
            int blue = (int) (Math.random() * 255);
            // 将red、green、blue三个随机数组合成ARGB颜色
            int color = 0xff000000 + red << 16 | green << 8 | blue;
            // 获取drawable上关联的画笔
            Paint paint = drawable.getPaint();
            // 将red、green、blue三个随机数除以4得到商值组合成ARGB颜色
            int darkColor = 0xff000000 | red / 4 << 16
                | green / 4 << 8 | blue / 4;
            // 创建圆形渐变
            RadialGradient gradient = new RadialGradient(
                37.5f, 12.5f, BALL_SIZE, color, darkColor
                , Shader.TileMode.CLAMP);
            paint.setShader(gradient);
            // 为shapeHolder设置paint画笔
            shapeHolder.setPaint(paint);
            balls.add(shapeHolder);
            return shapeHolder;
        }
        @Override
        protected void onDraw(Canvas canvas)
        {
            // 遍历balls集合中的每个ShapeHolder对象
            for (ShapeHolder shapeHolder : balls)
            {
                // 保存canvas的当前座标系统
                canvas.save();
                // 座标变换:将画布座标系统平移到shapeHolder的X、Y座标处
                canvas.translate(shapeHolder.getX()
                        , shapeHolder.getY());
                // 将shapeHolder持有的圆形绘制在Canvas上
                shapeHolder.getShape().draw(canvas);
                // 恢复Canvas座标系统
                canvas.restore();
            }
        }
    }
}

使用SurfaceView实现动画

使用自定义View绘制图片的 缺陷

  1. View缺乏双缓冲机制
  2. 当程序需要更新View上的图片时,程序必须重新绘制View上显示的整张图片
  3. 新线程无法直接更新View组件

    动画三

    FishView代码:
public class FishView extends SurfaceView
        implements SurfaceHolder.Callback
{
    private SurfaceHolder holder;
    private UpdateViewThread updateThread;
    private boolean hasSurface;
    private Bitmap back;
    private Bitmap[] fishs;
    private int fishIndex = 0; // 定义变量记录绘制第几张鱼的图片
    // 下面定义2个变量,记录鱼的初始位置
    private float fishX = 778;
    private float fishY = 500;
    private float fishSpeed = 6; // 鱼的游动速度
    // 定义鱼游动的角度
    private int fishAngle = new Random().nextInt(60);
    Matrix matrix = new Matrix();
    public FishView(Context ctx, AttributeSet set)
    {
        super(ctx, set);
        // 获取该SurfaceView对应的SurfaceHolder,并将该类的实例作为其Callback
        holder = getHolder();
        holder.addCallback(this);
        hasSurface = false;
        back = BitmapFactory.decodeResource(ctx.getResources()
                , R.drawable.fishbg);
        fishs = new Bitmap[10];
        // 初始化鱼游动动画的10张图片
        for(int i = 0 ; i < 10 ; i++)
        {
            try
            {
                int fishId = (Integer)R.drawable.class
                        .getField("fish" + i).get(null);
                fishs[i] = BitmapFactory.decodeResource(
                        ctx.getResources(), fishId);
            }
            catch(Exception e){
                e.printStackTrace();
            }
        }
    }

    public void resume()
    {
        // 创建和启动图像更新线程
        if (updateThread == null)
        {
            updateThread = new UpdateViewThread();
            if (hasSurface == true)
                updateThread.start();
        }
    }

    public void pause()
    {
        // 停止图像更新线程
        if (updateThread != null)
        {
            updateThread.requestExitAndWait();
            updateThread = null;
        }
    }

    // 当SurfaceView被创建时回调该方法
    @Override
    public void surfaceCreated(SurfaceHolder holder)
    {
        hasSurface = true;
        resume();
    }

    // 当SurfaceView将要被销毁时回调该方法
    public void surfaceDestroyed(SurfaceHolder holder)
    {
        hasSurface = false;
        pause();
    }

    // 当SurfaceView发生改变时回调该方法
    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h)
    {
        if (updateThread != null)
            updateThread.onWindowResize(w, h);
    }

    class UpdateViewThread extends Thread
    {
        // 定义一个记录图形是否更新完成的旗标
        private boolean done;
        UpdateViewThread()
        {
            super();
            done = false;
        }

        @Override
        public void run()
        {
            SurfaceHolder surfaceHolder = holder;
            // 重复绘图循环,直到线程停止
            while (!done)
            {
                // 锁定SurfaceView,并返回到要绘图的Canvas
                Canvas canvas = surfaceHolder.lockCanvas();  // ①
                // 绘制背景图片
                canvas.drawBitmap(back, 0, 0, null);
                // 如果鱼“游出”屏幕之外,重新初始鱼的位置
                if(fishX < 0)
                {
                    fishX = 778;
                    fishY = 500;
                    fishAngle = new Random().nextInt(60);
                }
                if(fishY < 0)
                {
                    fishX = 778;
                    fishY = 500;
                    fishAngle = new Random().nextInt(60);

                }
                // 使用Matrix来控制鱼的旋转角度和位置
                matrix.reset();
                matrix.setRotate(fishAngle);
                matrix.postTranslate(fishX -= fishSpeed * Math
                    .cos(Math.toRadians(fishAngle))
                    , fishY -= fishSpeed * Math.sin(Math.toRadians(fishAngle)));
                canvas.drawBitmap(fishs[fishIndex++ % fishs.length], matrix, null);
                // 解锁Canvas,并渲染当前图像
                surfaceHolder.unlockCanvasAndPost(canvas);  // ②
                try
                {
                    Thread.sleep(60);
                }
                catch (InterruptedException e){}
            }
        }

        public void requestExitAndWait()
        {
            // 把这个线程标记为完成,并合并到主程序线程
            done = true;
            try
            {
                join();
            }
            catch (InterruptedException ex){}
        }

        public void onWindowResize(int w, int h){
            // 处理SurfaceView的大小改变事件
        }
    }
}

动画四

MainActivity代码:

public class MainActivity extends Activity
{
    private SurfaceHolder holder;
    private Paint paint;
    final int HEIGHT = 320;
    final int WIDTH = 768;
    final int X_OFFSET = 5;
    private int cx = X_OFFSET;
    // 实际的Y轴的位置
    int centerY = HEIGHT / 2;
    Timer timer = new Timer();
    TimerTask task = null;
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        final SurfaceView surface = (SurfaceView)
                findViewById(R.id.show);
        // 初始化SurfaceHolder对象
        holder = surface.getHolder();
        paint = new Paint();
        paint.setColor(Color.GREEN);
        paint.setStrokeWidth(3);
        Button sin = (Button)findViewById(R.id.sin);
        Button cos = (Button)findViewById(R.id.cos);
        OnClickListener listener = (new OnClickListener()
        {
            @Override
            public void onClick(final View source)
            {
                drawBack(holder);
                cx = X_OFFSET;
                if(task != null)
                {
                    task.cancel();
                }
                task = new TimerTask()
                {
                    public void run()
                    {
                        int cy = source.getId() == R.id.sin ? centerY
                                - (int)(100 * Math.sin((cx - 5) * 2
                                * Math.PI / 150))
                                : centerY - (int)(100 * Math.cos ((cx - 5)
                                * 2 * Math.PI / 150));
                        Canvas canvas = holder.lockCanvas(new Rect(cx ,
                                cy - 2  , cx + 2, cy + 2));
                        canvas.drawPoint(cx , cy , paint);
                        cx ++;
                        if (cx > WIDTH)
                        {
                            task.cancel();
                            task = null;
                        }
                        holder.unlockCanvasAndPost(canvas);
                    }
                };
                timer.schedule(task , 0 , 30);
            }
        });
        sin.setOnClickListener(listener);
        cos.setOnClickListener(listener);
        holder.addCallback(new Callback()
        {
            @Override
            public void surfaceChanged(SurfaceHolder holder, int format,
                                       int width, int height)
            {
                drawBack(holder);
            }
            @Override
            public void surfaceCreated(final SurfaceHolder myHolder){ }
            @Override
            public void surfaceDestroyed(SurfaceHolder holder)
            {
                timer.cancel();
            }
        });
    }
    private void drawBack(SurfaceHolder holder)
    {
        Canvas canvas = holder.lockCanvas();
        // 绘制白色背景
        canvas.drawColor(Color.WHITE);
        Paint p = new Paint();
        p.setColor(Color.BLACK);
        p.setStrokeWidth(2);
        // 绘制座标轴
        canvas.drawLine(X_OFFSET , centerY , WIDTH , centerY , p);
        canvas.drawLine(X_OFFSET , 40 , X_OFFSET , HEIGHT , p);
        holder.unlockCanvasAndPost(canvas);
        holder.lockCanvas(new Rect(0 , 0 , 0 , 0));
        holder.unlockCanvasAndPost(canvas);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章