Windows风格的Loading动画Android实现(3)
2015-08-29 17:02
363 查看
前言
最近在学习开源弹幕引擎源码,其中对重绘的控制的很好,值得学习,我把一部分技术应用到我的这个动画里面。第一,在这个弹幕引擎里面,各个弹幕相互独立,独立计算独立绘制。第二,不过度绘制,这种情况是丢帧的反面。简单来说就是不要在16ms内绘制两次,因为当fps大于60之后,人眼就看不出差别来了。效果图
改进
面向对象,让每个点自己计算显示位置,自己绘制每个点的运动轨迹是一样的,只是启动时间,启动位置不同,而这些可以通过时间偏移、位置偏移实现。独立之后逻辑变的简单。
参照系的问题
所有的点都一直有一个平移速度
不再16ms内重绘两次
计算两次更新绘制参数之间的时间间隔,如果小于16ms则让工作线程休眠,得到间隔到了16ms再更新绘制参数。
上篇博客中的uiHander没有必要,用postInvalidate()即可
Demo
Github源码
[code]public class LoadingView extends View { /** * The cy of every point is the same. */ private float cy; /** * The radius of every point is the same. */ private float radius; /** * Used in animation. */ private long startMillis = -1; private long lastMills = -1; /** * Used to make translation more smooth */ private Interpolator enterInterpolator, exitInterpolator; /** * The moving velocity of the point which is not entering or exiting */ private float v; /** * The number of points */ private int pointNum; private HandlerThread workerThread; private Handler workerHandler; private long enterDuration = 600; private long moveDuration = 1800; private long exitDuraion = 600; private long cycle; private float enterDistance; private float exitDistance; private float moveVelocity; private float offsetBetweenPoint; private List<LoadingPoint> points; private float uniformCxOffset; public LoadingView(Context context) { super(context); init(); } public LoadingView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public LoadingView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { pointNum = 4; enterInterpolator = new DecelerateInterpolator(1.6F); exitInterpolator = new AccelerateInterpolator(1.2F); cycle = enterDuration + moveDuration + exitDuraion + enterDuration * (pointNum - 1); points = new ArrayList<>(pointNum); for (int i = 0; i < pointNum; i++) { LoadingPoint point = new LoadingPoint(i); points.add(point); } workerThread = new HandlerThread("workerThread"); workerThread.start(); workerHandler = new Handler(workerThread.getLooper(), new Handler.Callback() { @Override public boolean handleMessage(Message message) { updateDrawParams(); postInvalidate(); return true; } }); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); initSize(); } private void initSize() { cy = getMeasuredHeight() / 2; radius = getMeasuredHeight() / 3; enterDistance = getMeasuredWidth() * 0.425F; float moveDistance = getMeasuredWidth() * 0.15F; exitDistance = getMeasuredWidth() * 0.425F; moveVelocity = moveDistance / moveDuration; uniformCxOffset = - moveDistance * 0.5F; offsetBetweenPoint = moveDistance / 3; } @Override protected void onDraw(Canvas canvas) { for (int i = 0; i < points.size(); i++){ points.get(i).draw(canvas); } workerHandler.sendEmptyMessage(0);//update draw params on worker thread } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); workerThread.quit(); } private void updateDrawParams() { long currentMillis = System.currentTimeMillis(); if (startMillis == -1) { startMillis = currentMillis; } if (lastMills == -1) { lastMills = currentMillis; } else { long timeDelta = currentMillis - lastMills; if (timeDelta < 16) { try { Thread.sleep(16 - timeDelta); } catch (InterruptedException e) { e.printStackTrace(); } } } //acquire current millis again, because this thread may sleep for not invalidating too frequently currentMillis = System.currentTimeMillis(); long passMills = (currentMillis - startMillis) % cycle; for (int i = 0; i < points.size(); i++){ points.get(i).update(passMills); } lastMills = currentMillis; } private class LoadingPoint { private int index; private float translateX; private boolean visible; private float cx; private Paint paint; public LoadingPoint(int i) { index = i; translateX = i * offsetBetweenPoint; paint = new Paint(); paint.setDither(true); paint.setAntiAlias(true); paint.setColor(Color.parseColor("#455A64")); paint.setAlpha(0); } public void update(long passMills) { //做时间偏移 passMills = passMills - index * enterDuration; //还没有出现 if (passMills < 0) { visible = false; return; } visible = true; float enterX = 0; float exitX = 0; if (passMills < enterDuration) { //enter float enterFraction = ((float) passMills) / enterDuration; float interpolatedEnterFraction = enterInterpolator.getInterpolation(enterFraction); enterX = interpolatedEnterFraction * enterDistance; exitX = 0; paint.setAlpha((int) (255 * interpolatedEnterFraction)); } else if (passMills < enterDuration + moveDuration) { enterX = enterDistance; exitX = 0; paint.setAlpha(255); } else { enterX = enterDistance; float exitFraction = ((float) (passMills - enterDuration - moveDuration)) / exitDuraion; float interpolatedExitFraction = exitInterpolator.getInterpolation(exitFraction); exitX = interpolatedExitFraction * exitDistance; paint.setAlpha((int) (255 * (1 - interpolatedExitFraction))); } //move float moveX = passMills * moveVelocity; cx = enterX + moveX + exitX; } public void draw(Canvas canvas) { if (visible) { canvas.save(); //做位置偏移 canvas.translate(translateX + uniformCxOffset, 0); canvas.drawCircle(cx, cy, radius, paint); canvas.restore(); } } } }
相关文章推荐
- Android主题切换—夜间/白天模式探究
- Android主题切换—夜间/白天模式探究
- android 两次点击back键退出程序
- Android-帧动画Animation基本实现
- 在android的项目中要在string.xml 中显示特殊符号
- android studio 用svn,默认无“先update,再commit”按钮。VSC-->Enable
- Android 源码下载
- Android系列之Fragment(二)----Fragment的生命周期和返回栈
- android imageLoader 使用缓存策略
- Android下raw文件夹下获取不到多媒体文件的缩略图
- Android应用插件式开发解决方法
- Android 热更新——非侵入AOP框架
- android的广播事件
- Android应用程序用户界面(五)
- 最新的全网速下载Android Sdk更新与下载
- android service与activity进行通信
- Android中消息系统模型和Handler Looper
- Android-JNI的简单使用
- android平台led开发之内核硬件驱动层
- Android手机在不同分辨率情况下字体自适应大小