前言
通过 属性动画原理解析,我们了解属性动画的整个运行机制,对底层的原理有了一定的了解,也学习一些知识点,但是学习过的知识点在没有自己实践都是别人的,只有自己实践后才属于自己的。接下来分享一些属性动画的一些知识点运用项目中的经历。
实践
实践一:屏幕刷新机制
我们知道属性动画基于屏幕刷新回调机制来实时更改属性的值,而在实际的项目应用用,也会很多实时刷新屏幕的需求,接下来我们通过秒表计时器需求来分析刷新机制在实际应用。
秒表计时器的效果图如下所示:
要实现这个功能就是不停刷新屏幕,更改分针和秒针的旋转角度。要实现这个功能需求,大概有三种方式:
1. Timer+Handler
2. Handler发送延迟消息
3. 利用Choreographer的刷新回调
对于1和2是比较常用的方式,对于3相对用得比较少,只有了解过屏幕刷新机制的才可能会想到用该方法。下面我们就分析3的实现方法:
第一步: 创建实例
private Choreographer choreographer = Choreographer.getInstance();
Choreographer进程启动的时候,内部帮我完成初始化,通过ThreadLocal来进行存储,没有共有构造函数,只能通过Choreographer.getInstance()的方式来获取实例。目前值对外开放了:removeFrameCallback(FrameCallback callback),postFrameCallback(FrameCallback callback),postFrameCallbackDelayed(FrameCallback callback, long delayMillis)。其余的方法都通过 @UnsupportedAppUsage注解限制了外部APP的使用。给我们提供的三个方法里面,最重要的就是FrameCallback,接下来实现回调:
第二步: 实现FrameCallback
在实现FrameCallback之前,先了解FrameCallback的作用
public interface FrameCallback {
//当新的一帧需要显示的时候会调用该方法
//这里frameTimeNanos是纳秒,使用过程中除以1000000转换为毫秒
public void doFrame(long frameTimeNanos);
}
FrameCallback就一个doFrame()回调方法,具体的说明注释说明。需要更详细的可以参考源码的英文注释。
下面实现我们自己的FrameCallback:
private Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
if (state == STATE_START) {
showTime();
choreographer.postFrameCallback(this);
}
}
};
第三步: 实现计时处理逻辑
private void showTime() {
long nowTime = System.currentTimeMillis();
if (preTime == 0) {
preTime = nowTime;
}
allTime += nowTime - preTime;
//转换时间格式
int second = allTime / 1000;
int mill = allTime % 1000;
StringBuilder sb = new StringBuilder();
sb.append(second).append(".");
sb.append(mill / 100).append((mill % 100) / 10).append("s");
//其他业务逻辑处理,具体需求具体处理
Log.d("ChoreographerDemo", sb.toString());
preTime = nowTime;
}
在这里只简单的将计时数据输出,和前面的给的效果图有差距,如果实现上面的效果,自己根据UI提供的图进行秒针和分针的旋转即可。
第四步: 测试
完整代码:
public class ChoreographerActivity extends AppCompatActivity {
private Choreographer choreographer = Choreographer.getInstance();
//开始
private static final int STATE_START = 1;
//暂停
private static final int STATE_PAUSE = 2;
private int state;
//总计时时间
private int allTime;
//上一次时间点
private long preTime;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_choreographer);
}
private Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
if (state == STATE_START) {
showTime();
choreographer.postFrameCallback(this);
}
}
};
private void showTime() {
long nowTime = System.currentTimeMillis();
if (preTime == 0) {
preTime = nowTime;
}
allTime += nowTime - preTime;
//转换时间格式
int second = allTime / 1000;
int mill = allTime % 1000;
StringBuilder sb = new StringBuilder();
sb.append(second).append(".");
sb.append(mill / 100).append((mill % 100) / 10).append("s");
//其他业务逻辑处理,具体需求具体处理
Log.d("ChoreographerDemo", sb.toString());
preTime = nowTime;
}
public void pause(View view) {
state = STATE_PAUSE;
choreographer.removeFrameCallback(mFrameCallback);
}
public void start(View view) {
state = STATE_START;
choreographer.postFrameCallback(mFrameCallback);
}
}
日志数据:
2021-01-19 18:51:41.266 3119-3119/com.water.view.demo D/ChoreographerDemo: 0.00s
2021-01-19 18:51:41.281 3119-3119/com.water.view.demo D/ChoreographerDemo: 0.01s
2021-01-19 18:51:41.300 3119-3119/com.water.view.demo D/ChoreographerDemo: 0.03s
2021-01-19 18:51:41.314 3119-3119/com.water.view.demo D/ChoreographerDemo: 0.04s
2021-01-19 18:51:41.331 3119-3119/com.water.view.demo D/ChoreographerDemo: 0.06s
2021-01-19 18:51:41.348 3119-3119/com.water.view.demo D/ChoreographerDemo: 0.08s
2021-01-19 18:51:41.365 3119-3119/com.water.view.demo D/ChoreographerDemo: 0.09s
2021-01-19 18:51:41.382 3119-3119/com.water.view.demo D/ChoreographerDemo: 0.11s
2021-01-19 18:51:41.398 3119-3119/com.water.view.demo D/ChoreographerDemo: 0.13s
2021-01-19 18:51:41.416 3119-3119/com.water.view.demo D/ChoreographerDemo: 0.15s
2021-01-19 18:51:41.433 3119-3119/com.water.view.demo D/ChoreographerDemo: 0.16s
2021-01-19 18:51:41.450 3119-3119/com.water.view.demo D/ChoreographerDemo: 0.18s
2021-01-19 18:51:41.467 3119-3119/com.water.view.demo D/ChoreographerDemo: 0.20s
2021-01-19 18:51:41.483 3119-3119/com.water.view.demo D/ChoreographerDemo: 0.21s
2021-01-19 18:51:41.500 3119-3119/com.water.view.demo D/ChoreographerDemo: 0.23s
2021-01-19 18:51:41.517 3119-3119/com.water.view.demo D/ChoreographerDemo: 0.25s
小结:
这里目前结合自己的项目经验值分享了屏幕刷新机制的使用,后期发现更多关于动画技能的实践会持续更新。