我正在为地图(v2)创建一个集群功能。我将位置分组到集群中,然后将集群显示为自定义标记:
这很好用,但我想在创建和拆分集群时创建一个动画。我通过在标记(注释)上创建 UIView 动画成功地使用 iOS 做到了这一点。我在网上找不到任何适用于 android 的代码/提示。
我设法得到一个简单的 ImageView 作为覆盖以类似于一个集群,然后使用 aTranslateAnimation
来获得所需的动画。最后我删除了这个视图并添加了标记。
有没有更好的方法来为标记设置动画?
我正在为地图(v2)创建一个集群功能。我将位置分组到集群中,然后将集群显示为自定义标记:
这很好用,但我想在创建和拆分集群时创建一个动画。我通过在标记(注释)上创建 UIView 动画成功地使用 iOS 做到了这一点。我在网上找不到任何适用于 android 的代码/提示。
我设法得到一个简单的 ImageView 作为覆盖以类似于一个集群,然后使用 aTranslateAnimation
来获得所需的动画。最后我删除了这个视图并添加了标记。
有没有更好的方法来为标记设置动画?
通过为 Marker 对象设置动画而不是在 GoogleMap 前面添加和删除视图,您走在正确的轨道上,但是如果您使用 Animator 对象为 Marker 设置动画,您可以获得更好的性能。
使用 Handler 和延迟的 Runnable 方法,您可以有效地对目标帧速率进行硬编码。如果您以太低的延迟发布 Runnable,您的动画将需要更长的时间来执行。如果太高,帧速率会太慢,即使在功能强大的设备上也会显得断断续续。使用 Animator 优于 Handler 和延迟的 Runnable 的优点是它只会调用 onAnimationUpdate() 以在系统可以处理的情况下绘制下一帧。
在我的集群库Clusterkraf中,我使用 ObjectAnimator(来自NineOldAndroids以实现向后兼容性)在更改缩放级别时为集群过渡设置动画。它可以在我的 Galaxy Nexus 上平滑地为大约 100 个标记设置动画。
下面是一个片段,其中概述了如何使其工作。
class ClusterTransitionsAnimation implements AnimatorListener, AnimatorUpdateListener {
private ObjectAnimator animator;
private AnimatedTransitionState state;
private ClusterTransitions transitions;
private Marker[] animatedMarkers;
void animate(ClusterTransitions transitions) {
if (this.state == null) {
Options options = optionsRef.get();
Host host = hostRef.get();
if (options != null && host != null) {
this.state = new AnimatedTransitionState(transitions.animated);
this.transitions = transitions;
animator = ObjectAnimator.ofFloat(this.state, "value", 0f, 1f);
animator.addListener(this);
animator.addUpdateListener(this);
animator.setDuration(options.getTransitionDuration());
animator.setInterpolator(options.getTransitionInterpolator());
host.onClusterTransitionStarting();
animator.start();
}
}
}
@Override
public void onAnimationStart(Animator animator) {
// Add animatedMarkers to map, omitted for brevity
}
@Override
public void onAnimationUpdate(ValueAnimator animator) {
if (state != null && animatedMarkers != null) {
LatLng[] positions = state.getPositions();
for (int i = 0; i < animatedMarkers.length; i++) {
animatedMarkers[i].setPosition(positions[i]);
}
}
}
}
下面的代码是正确的,但我会强调一些东西。我会使用 16 毫秒 == 60fps。人眼无法区分小于 16 毫秒,因此:
final LatLng target = NEW_LOCATION;
final long duration = 400;
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
Projection proj = map.getProjection();
Point startPoint = proj.toScreenLocation(marker.getPosition());
final LatLng startLatLng = proj.fromScreenLocation(startPoint);
final Interpolator interpolator = new LinearInterpolator();
handler.post(new Runnable() {
@Override
public void run() {
long elapsed = SystemClock.uptimeMillis() - start;
float t = interpolator.getInterpolation((float) elapsed / duration);
double lng = t * target.longitude + (1 - t) * startLatLng.longitude;
double lat = t * target.latitude + (1 - t) * startLatLng.latitude;
marker.setPosition(new LatLng(lat, lng));
if (t < 1.0) {
// Post again 16ms later == 60 frames per second
handler.postDelayed(this, 16);
} else {
// animation ended
}
}
});
我找到了一个可行的解决方案:
final LatLng target = NEW_LOCATION;
final long duration = 400;
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
Projection proj = map.getProjection();
Point startPoint = proj.toScreenLocation(marker.getPosition());
final LatLng startLatLng = proj.fromScreenLocation(startPoint);
final Interpolator interpolator = new LinearInterpolator();
handler.post(new Runnable() {
@Override
public void run() {
long elapsed = SystemClock.uptimeMillis() - start;
float t = interpolator.getInterpolation((float) elapsed / duration);
double lng = t * target.longitude + (1 - t) * startLatLng.longitude;
double lat = t * target.latitude + (1 - t) * startLatLng.latitude;
marker.setPosition(new LatLng(lat, lng));
if (t < 1.0) {
// Post again 10ms later.
handler.postDelayed(this, 10);
} else {
// animation ended
}
}
});
同时大约20个有点慢,也许有更好的方法?