逐帧动画:
补间动画
结合动画
自定义动画
属性动画一
属性动画二
动画三
动画四

  • 逐帧动画

    • 语法格式

      • MainActivity代码
  • 补间动画Tween
    • Animation子类
    • interpolator简单理解
      • 代码
    • 结合动画
      • MainActivity代码
    • 自定义补间动画
      • MyAnimation代码
      • MainActivity代码
      • activity_mainxml
  • 属性动画
    • 定义属性动画的方法
    • 使用属性动画的步骤
    • 属性动画和补间动画的区别
    • 属性动画的API
    • 属性动画一
      • ShapeHolder代码
      • MainActivity代码
    • 属性动画二
      • ShapeHolder代码
      • MainActivity代码
  • 使用SurfaceView实现动画
    • 使用自定义View绘制图片的 缺陷
    • 动画三
      • FishView代码
    • 动画四
      • MainActivity代码

逐帧动画

定义逐帧动画,只要在<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(){@Overridepublic 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@Overrideprotected 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
{@Overridepublic 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(){@Overridepublic void handleMessage(Message msg){if (msg.what == 0x123){flower.startAnimation(reverse);}}};bn.setOnClickListener(new OnClickListener(){@Overridepublic void onClick(View arg0){flower.startAnimation(anim);// 设置3.5秒后启动第二个动画new Timer().schedule(new TimerTask(){@Overridepublic 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;@Overridepublic 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(){@Overridepublic 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(){@Overridepublic void onClick(View v){// 开始播放蝴蝶振翅的逐帧动画butterfly.start();  // ②// 通过定制器控制每0.2秒运行一// 次TranslateAnimation动画new Timer().schedule(new TimerTask(){@Overridepublic 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;}@Overridepublic 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参数代表了对目标组件所做的改变.*/@Overrideprotected 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
{@Overridepublic 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
{@Overridepublic 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;@Overridepublic 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();}@Overridepublic 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、stretchAnim2bouncer.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(){// 当动画结束时@Overridepublic 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;}@Overrideprotected 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 SurfaceViewimplements 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,并将该类的实例作为其Callbackholder = 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被创建时回调该方法@Overridepublic 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;}@Overridepublic void run(){SurfaceHolder surfaceHolder = holder;// 重复绘图循环,直到线程停止while (!done){// 锁定SurfaceView,并返回到要绘图的CanvasCanvas 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;@Overridepublic 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(){@Overridepublic 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(){@Overridepublic void surfaceChanged(SurfaceHolder holder, int format,int width, int height){drawBack(holder);}@Overridepublic void surfaceCreated(final SurfaceHolder myHolder){ }@Overridepublic 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);}
}

Android中动画的详细讲解相关推荐

  1. Android webservice的用法详细讲解

    Android webservice的用法详细讲解 看到有很多朋友对WebService还不是很了解,在此就详细的讲讲WebService,争取说得明白吧.此文章采用的项目是我毕业设计的webserv ...

  2. android中Adapter适配器的讲解

    android中Adapter适配器的讲解 Adapter(适配器的讲解) 适配器就我自己来看,我觉得这是一个非常重要的知识点,Adapter是用来帮助填出数据的中间桥梁,简单点说吧:将各种数据以合适 ...

  3. android webview详情,Android中的WebView详细介绍

    Android中WebView的详细解释: 1. 概念: WebView(网络视图)能加载显示网页,可以将其视为一个浏览器.它使用了WebKit渲染引擎加载显示网页. 2. 使用方法: (1).实例化 ...

  4. python中ix用法_Python: pandas中ix的详细讲解

    Python: pandas中ix的详细讲解 发布时间:2018-09-21 15:59, 浏览次数:2372 , 标签: Python pandas ix 在上一篇博客 中,我们已经仔细讲解了ilo ...

  5. vue中computed的详细讲解

    vue中computed的详细讲解 1.定义 2.用法 3.computed的响应式依赖(缓存) 4.应用场景 1.定义 computed是vue的计算属性,是根据依赖关系进行缓存的计算,只有在它的相 ...

  6. python isdigit和isnumeric区别_isdigit()、isdecimal()和isnumeric python中区别【详细讲解】...

    今天爱分享给大家带来isdigit().isdecimal()和isnumeric python中区别[详细讲解],希望能够帮助到大家. 1.函数介绍 isdecimal(...) | S.isdec ...

  7. isnumeric用法python_isdigit()、isdecimal()和isnumeric python中区别【详细讲解】

    今天爱分享给大家带来isdigit().isdecimal()和isnumeric python中区别[详细讲解],希望能够帮助到大家. 1.函数介绍 isdecimal(...) | S.isdec ...

  8. Android蓝牙BLE的详细讲解

    我今天分享的主题是 Android 上低功耗蓝牙的实践.这个主题比较小众.我在过去的一年多的时间里,主要是在做低功耗蓝牙相关的开发.接触过程中发现,BLE 的开发和通常的 Android APP 的开 ...

  9. Android中的JSON详细总结

    1.JSON(JavaScript Object Notation) 定义: 一种轻量级的数据交换格式,具有良好的可读和便于快速编写的特性.业内主流技术为其提供了完整的解决方案(有点类似于正则表达式, ...

最新文章

  1. android ui布局开发,Android UI设计初步(基本布局)
  2. Depth by Poking:从自监督抓取学习深度估计
  3. jQuery学习笔记--JqGrid相关操作 方法列表 备忘 重点讲解(超重要)
  4. STM32串口的部分映射与完全映射
  5. 螺钉装弹垫平垫机器人_【经验总结】什么时候用平垫,什么时候用弹垫?
  6. 设计模式01-模板设计模式
  7. 看朋友日志发现的一个ios下block相关的内存管理问题,非常奇怪,请大家帮忙一起来回答!...
  8. BZOJ2592: [Usaco2012 Feb]Symmetry
  9. VMware搭建KMS服务器(VLMCSD)
  10. 执行git reset --hard后文件的恢复
  11. 两耳不闻窗外事 一心只读圣贤书
  12. 大学计算机学习心得1000字,大学计算机学习心得体会
  13. Swift中键盘的弹出隐藏,页面抬高,Return键等的配置
  14. 如何提取视频中的音频?自用方法介绍
  15. 【朝花夕拾】Android自定义View篇之(十一)View的滑动,弹性滑动与自定义PagerView...
  16. C++调用Boost库编写Python扩展模块
  17. UE4 循环滚动Led效果材质
  18. 如何选购台式电脑和笔记本?购买时应注意什么
  19. 计算机专业四大名校排名,美国CS专业四大名校!总有一款适合你!
  20. Leetcode May Challenge - 05/07: Cousins in Binary Tree(Python)

热门文章

  1. 七牛云 DV OV EV SSL 证书上线,限时折扣低至 6.75 折!
  2. xilinx的VDMA驱动分析
  3. [Fabric] First-NetWork(byfn.sh文件分析)
  4. matlab 地震属性分析,地震属性优化在储层预测中的应用
  5. 微信 Ubuntu个人服务器环境搭建
  6. STM32编程(一)STM32 GPIO配置的4大步骤
  7. LAMP架构介绍与网站部署
  8. VUE、微信小程序for循环动态变量取值
  9. 论文笔记:Once for All- Train One Network and Specialize it for Efficient Deployment
  10. 同事表白失败,我用Python帮他操作一波后直接步入热恋,不要偷偷收藏啊