zoukankan      html  css  js  c++  java
  • 八、图形与图像处理(2)

    8、逐帧(Frame)动画
    要求开发者把动画过程的没涨静态图片都收集起来,然后由Android开控制依次显示这些静态图片,与放电影的原理一样。

    9、AnimationDrawable与逐帧动画
    只要在<animation-list.../>元素中使用<item.../>子元素定义动画的全部帧,并指定各帧的持续时间即可。语法格式如下:

    <?xml version="1.0" encoding="utf-8"?>
    <animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot=["true" | "false"] >
    <item android:drawable="@[package:]drawable/drawable_resource_name"
    android:duration="integer" />
    </animation-list>

    android:oneshot控制该动画是否循环播放(true:不会循环播放)。每个<item/>子元素添加一帧。

    【提示】Android完全支持在Java代码中创建逐帧动画,如果开发者喜欢的话,完全可以先创建AnimationDrawable对象,
    然后调用addFrame(Drawable frame, int duration)向该动画中添加帧,每调用一次addFrame方法,就像<animation-list.../>
    元素中添加一个<item.../>子元素。

    一旦程序获取了AnimationDrawable对象之后,接下来就可用ImageView把AnimationDrawable显示出来---习惯上把AnimationDrawable
    设置成ImageView的背景即可。

    范例:功夫熊猫动画。

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <!-- 指定动画循环播放   fat_po.xml -->
     3 <animation-list xmlns:android="http://schemas.android.com/apk/res/android"
     4     android:oneshot="false">
     5     <!-- 添加多个帧 -->
     6     <item android:drawable="@drawable/fat_po_f01" android:duration="60" />
     7     <item android:drawable="@drawable/fat_po_f02" android:duration="60" />
     8     <item android:drawable="@drawable/fat_po_f03" android:duration="60" />
     9     <item android:drawable="@drawable/fat_po_f26" android:duration="60" />    
    10     <item android:drawable="@drawable/fat_po_f27" android:duration="60" />                                                                    
    11 </animation-list>
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="#fff"
        android:orientation="vertical" >
        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:orientation="horizontal" >
            <Button
                android:id="@+id/play"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/play" />
            <Button
                android:id="@+id/stop"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/stop" />
        </LinearLayout>
    
        <ImageView
            android:id="@+id/anim"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@anim/fat_po"
            android:scaleType="center" />
    </LinearLayout>
     1 // 获取AnimationDrawable动画对象,并设置到ImageView背景上。
     2 final AnimationDrawable anim = (AnimationDrawable) imageView
     3                 .getBackground();
     4         play.setOnClickListener(new OnClickListener() {
     5             @Override
     6             public void onClick(View v) {
     7                 // 开始播放动画
     8                 anim.start();
     9             }
    10         });
    11         stop.setOnClickListener(new OnClickListener() {
    12             @Override
    13             public void onClick(View v) {
    14                 // 停止播放动画
    15                 anim.stop();
    16             }
    17         });

    【注意】AnimationDrawable代表的动画默认是不播放的,必须在程序中启动动画播放才可以。
    AnimationDrawable提供了如下两个方法来开始、停止动画。
    start():开始播放动画。
    stop():停止播放动画。

    范例:在指定点爆炸
    爆炸效果实际上是一个逐帧动画,开发者需要收集从开始爆炸到爆炸结束的所有静态图片,再
    将这些图片定义成一个逐帧动画,接着在碰撞点播放该逐帧动画即可。

    <!-- resanimlast.xml -->
    <?xml version="1.0" encoding="utf-8"?>
    <!-- 定义动画只播放一次,不循环 -->
    <animation-list xmlns:android="http://schemas.android.com/apk/res/android"
        android:oneshot="true" >
        <item android:drawable="@drawable/bom_f01" android:duration="80" />
        <item android:drawable="@drawable/bom_f02" android:duration="80" />
        <item android:drawable="@drawable/bom_f03" android:duration="80" />
        <item android:drawable="@drawable/bom_f26" android:duration="80" />
        <item android:drawable="@drawable/bom_f27" android:duration="80" />
    </animation-list>
    public class Blast extends Activity {
        private MyView myView;
        private AnimationDrawable anim;
        private MediaPlayer bomb;
    
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            // 使用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, top, left + 40, top + 40);
            }
    
            // 重写该方法,控制如果动画播放到最后一帧时,隐藏该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);
            }
        }
    }

    10、补间(Tween)动画
    补间动画就是指开发者只需指定动画开始、动画结束“关键帧”,而动画变化的“中间帧”由系统计算并补齐,
    这就是把Tween动画翻译为“补间动画”的原因。

    11、Tween动画与Interpolator

         对于补间动画而言,开发者无需"逐一"定义动画过程中的每一帧。只要定义动画开始、结束的关键帧,并指定动画的持续时间即可。
     

    Interpolator根据特定算法计算出整个动画所需要动态插入帧的密度和位置,简单说,
    Interpolator负责控制动画的变化速度,这就使得基本的动画效果(Alpha、Scale、Translate、Rotate)
    能以匀速变化、加速、减速、抛物线速度等各种速度变化。
    Interpolator是一个接口,它定义了所有Interpolator都需要实现的方法:float getInterpolation(float input),
    开发者完全可以通过实现Interpolator来控制动画的变化速度。
    Android为Interpolator提供了如下几个实现类:分别用于实现不同动画变化速度。
    ● LinearInterpolator:动画以均匀的速度改变。
    ● AccelerateInterpolator:在动画开始的地方改变速度较慢,然后开始加速。
    ● AccelerateDecelerateInterpolator:在动画开始、结束的地方改变速度较慢,在中间的时候加速。
    ● CycleInterpolator:运动循环播放特定的次数,变化速度按正玄曲线改变。
    ● DecelerateInterpolator:在动画开始的地方改变速度较快,然后开始减慢。
    为了实现动画资源文件中指定补间动画所使用的Interpolator,定义补间动画的<set.../>元素支持一个
    android:interpolator属性,该属性的属性值可以指定为Android默认支持的Interpolator。
    ● @android:anim/linear_interpolator
    ● @android:anim/accelerate_interpolator
    ● @android:anim/accelerate_decelerate_interpolator
    ......
    其实上面的写法很有规律,它们就是把系统提供的Interpolator实现类的类名的驼峰写法改为
    中划线写法即可。
    一旦在程序中通过AnimationUtils得到代表补间动画的Animation之后,接下来就可调用View
    的startAnimation(Animation anim)方法开始对该View执行动画了。

    12、位置、大小、旋转度、透明度改变的补间动画
    虽然Android允许在程序中创建Animation对象,但实际上一般都会采用动画资源文件来定义补间动画。

    实例:介绍补间动画(包括两个动画资源文件)
    第一个动画资源文件控制图片以旋转的方式缩小,该动画资源文件如下:

    <!-- resanimanim.xml -->
    <?xml version="1.0" encoding="UTF-8"?>
    <!-- 指定动画匀速改变 -->
    <set xmlns:android="http://schemas.android.com/apk/res/android"
        android:interpolator="@android:anim/linear_interpolator">
        <!-- 定义缩放变换 -->
        <scale android:fromXScale="1.0"  
            android:toXScale="0.01"  
            android:fromYScale="1.0"  
            android:toYScale="0.01"  
            android:pivotX="50%" 
            android:pivotY="50%" 
            android:fillAfter="true" 
            android:duration="3000"/> 
        <!-- 定义透明度的变换 -->
        <alpha 
            android:fromAlpha="1" 
            android:toAlpha="0.05" 
            android:duration="3000"/> 
        <!-- 定义旋转变换 -->
        <rotate 
            android:fromDegrees="0" 
            android:toDegrees="1800" 
            android:pivotX="50%" 
            android:pivotY="50%" 
            android:duration="3000"/>
    </set>

    上面的动画资源指定动画匀速变化,同时进行缩放、透明度改变、旋转三种改变,动画持续时间为三秒。

    第二个动画资源则控制图片以动画的方式恢复回来,对应的动画资源如下: esanim everse.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- 指定动画匀速改变 -->
    <set xmlns:android="http://schemas.android.com/apk/res/android"
        android:interpolator="@android:anim/linear_interpolator"
        android:startOffset="3000">
        <!-- 定义缩放变换 -->
        <scale android:fromXScale="0.01"  
            android:toXScale="1"  
            android:fromYScale="0.01"  
            android:toYScale="1"  
            android:pivotX="50%" 
            android:pivotY="50%" 
            android:fillAfter="true" 
            android:duration="3000"/> 
        <!-- 定义透明度的变换 -->
        <alpha 
            android:fromAlpha="0.05" 
            android:toAlpha="1" 
            android:duration="3000"/> 
        <!-- 定义旋转变换 -->
        <rotate 
            android:fromDegrees="1800" 
            android:toDegrees="0" 
            android:pivotX="50%" 
            android:pivotY="50%" 
            android:duration="3000"/> 
    </set>

    定义动画资源之后,接下来就可以利用AnimationUtils工具类来加载指定的动画资源,加载成功后会返回一个Animation,
    该对象即可控制图片或视图播放动画。

    public class TweenAnim 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);
                }
            });
        }
    }

    范例:蝴蝶飞舞
    蝴蝶飞行时的振翅效果是逐帧动画;蝴蝶飞行时的位置改变是补间动画。

    <!-- 
    esanimutterfly.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/butterfly_f01" android:duration="120" />
        <item android:drawable="@drawable/butterfly_f02" android:duration="120" />
        <item android:drawable="@drawable/butterfly_f03" android:duration="120" />
        <item android:drawable="@drawable/butterfly_f04" android:duration="120" />
        <item android:drawable="@drawable/butterfly_f05" android:duration="120" />
        <item android:drawable="@drawable/butterfly_f06" android:duration="120" />                                                                
    </animation-list>

    定义了上面逐帧动画的动画资源后,接下来在程序中使用一个ImageView显示该动画资源即可。
    这就可以看到蝴蝶“振翅”效果了。由于蝴蝶飞舞主要是位移改变,接下来可以在程序中通过
    TranslateAnimation以动画的方式改变ImageView的位置,这样就达到蝴蝶飞舞的效果了。

    <ImageView
       android:id="@+id/butterfly"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:background="@anim/butterfly" />
    public class Butterfly 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);
                }
            });
        }
    }

           上面程序①位于Handler的消息处理方法内,这样程序每隔0.2秒即对该ImageView执行一次位移动画;
    程序②,用于播放butterfly动画(蝴蝶振翅效果)。运行上面程序,单击蝴蝶,即可看到屏幕有只蝴蝶从左向右飞舞。

    13、自定义补间动画
           Android提供了Animation作为补间动画抽象基类。而且为该抽象基类提供了AlphaAnimation、RotateAnimation、
    ScaleAnimation、TranslateAnimation四个实现类。这四个实现类只是补间动画的四种基本形式:透明度改变、旋转、
    缩放、位移,在实际项目中可能还需要一些更复杂的动画,比如让图片在"三维"空间内进行旋转动画等。这就需要开发
    着自己开发补间动画了。

    import android.graphics.Camera;
    import android.graphics.Matrix;
    import android.view.animation.Animation;
    import android.view.animation.LinearInterpolator;
    import android.view.animation.Transformation;
    
    // 自定义动画类
    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();
        }
    }
     1 import android.app.Activity;
     2 import android.os.Bundle;
     3 import android.util.DisplayMetrics;
     4 import android.view.Display;
     5 import android.view.WindowManager;
     6 import android.widget.ListView;
     7  
     8 public class ListViewTween extends Activity {
     9     @Override
    10     public void onCreate(Bundle savedInstanceState) {
    11         super.onCreate(savedInstanceState);
    12         setContentView(R.layout.main);
    13         // 获取ListView组件
    14         ListView list = (ListView) findViewById(R.id.list);
    15         WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
    16         Display display = windowManager.getDefaultDisplay();
    17         DisplayMetrics metrice = new DisplayMetrics();
    18         // 获取屏幕的宽和高
    19         display.getMetrics(metrice);
    20         // 设置对ListView组件应用动画
    21         list.setAnimation(new MyAnimation(metrice.xdpi / 2, metrice.ydpi / 2,
    22                 3500));
    23     }
    24 }
    <!-- main.xml -->
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout 
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical" >
        <ListView
            android:id="@+id/list"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:entries="@array/bookArray" />
    </LinearLayout>
    <!-- res/values/arrays.xml -->
    <?xml version="1.0" encoding="UTF-8"?>
    <resources>
        <string-array name="bookArray">
            <item>疯狂Java讲义</item>
            <item>轻量级Java EE企业应用实战</item>
            <item>经典Java EE企业应用实战</item>
            <item>疯狂Ajax讲义</item>
            <item>疯狂Android讲义</item>
        </string-array>
    </resources>

  • 相关阅读:
    从helloworld回顾程序的编译过程之二
    笔试题知识点记录
    jQuery面试题与答案
    hdu2586(How far away ?)
    解析PHP跳出循环的方法以及continue、break、exit的区别介绍
    如何查看任何一下网站的全部二级域名?
    urlencode()与urldecode()
    基于 LaravelAdmin 在十分钟内搭建起功能齐全的后台模板
    第一种方式:cookie的优化与购物车实例
    右键菜单的响应问题
  • 原文地址:https://www.cnblogs.com/androidsj/p/5007798.html
Copyright © 2011-2022 走看看