Android中动画的详细讲解
16lz
2021-01-26
逐帧动画:
补间动画
结合动画
自定义动画
属性动画一
属性动画二
动画三
动画四
-
-
- 逐帧动画
- 语法格式
- MainActivity代码
- 语法格式
- 补间动画Tween
- Animation子类
- interpolator简单理解
- 代码
- 结合动画
- MainActivity代码
- 自定义补间动画
- MyAnimation代码
- MainActivity代码
- activity_mainxml
- 属性动画
- 定义属性动画的方法
- 使用属性动画的步骤
- 属性动画和补间动画的区别
- 属性动画的API
- 属性动画一
- ShapeHolder代码
- MainActivity代码
- 属性动画二
- ShapeHolder代码
- MainActivity代码
- 使用SurfaceView实现动画
- 使用自定义View绘制图片的 缺陷
- 动画三
- FishView代码
- 动画四
- MainActivity代码
- 逐帧动画
-
逐帧动画
定义逐帧动画,只要在;元素中使用
- 子元素定义动画的全部帧就可以了!
语法格式:
<?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子类:
- AlphaAnimation:
- ScaleAnimation:
- TranslateAnimation:
- 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)方法,参数说明:
- interpolatedTime:代表动画的时间进行比,无论动画实际的持续时间如何,当动画播放时,该参数总是从0变化到1
- Transformation:代表补间动画在不同时刻对图形或者组件的变形程度
Camera常用方法如下:
getMatrix(Matrix matrix):将Camera所做的变换应用到指定matrix上。
rotateX(float deg):使目标组件沿X轴旋转。
rotateY(float deg):使目标组件沿Y轴旋转。
rotateZ(float deg):使目标组件沿Z轴旋转。
translate(float x,float y,float z):使目标组件在三维空间里进行位移变换
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>
属性动画
定义属性动画的方法:
- 使用ValurAnimator或者ObjectAnimator的静态工厂方法来创建动画
- 使用资源文件来定义动画
使用属性动画的步骤
- 创建ValurAniamtor或ObjectAnimator对象,即可从XML资源加载,也可以使用ValurAniamtor或ObjectAnimator的静态工厂方法创建
- 根据需要设置Animator属性值
- 如果需要监听Animator的改变事件,需要为Animator设置事件监听器
- 如果有多个动画需要按次序或者同时处理,则需要使用AnimatorSet组合这些动画
调用Animator的start()方法来启动。
属性动画和补间动画的区别:
补间动画只能定义两个关键帧在“透明度
”“旋转”“缩放”“位移”4个方面变化。属性动画可以定义任何属性的变化- 补间动画只对UI组件执行动画,但属性动画几乎可以对任何对象执行动画,不管是否显示在屏幕上
属性动画的API:
- Animator:它提供创建属性动画的基类,通常用于被继承并重写它的相关方法
- ValueAnimator:用于计算相关属性值
- ObjectAnimator:ValueAnimator的子类,允许程序员对指定对象的属性执行动画。
- AnimatorSet:Animator的子类,用于组合多个Animator,并指定多个Animator按次序播放还是同时播放。
- IntEvaluator:用于计算int类型的计算器
- FloatEvaluator:用于计算float类型属性值的计算器
- ArgbEvaluator:用于计算以十六进制形式表示的颜色值的计算器
- 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 balls = new ArrayList(); 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绘制图片的 缺陷
- View缺乏双缓冲机制
- 当程序需要更新View上的图片时,程序必须重新绘制View上显示的整张图片
- 新线程无法直接更新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); }}
更多相关文章
- Android(安卓)自定义控件属性,自定义Dialog定位
- android中自定义的控件,使用自定义属性attrs.xml
- Android(安卓)Widget ListView添加点击事件
- Android中的android:layout_weight使用详解
- android 如何连真机测试
- android eclipse环境搭建
- Mac OS X下设置android NDK的环境(详细实测有图)
- Android7.1启动系统App必须配置加密
- Android(安卓)Api demo系列(一) (App>Activity>Animation)