Android Maps v2 - 动画标记

时间:2013-02-01 11:39:13

标签: android android-mapview android-animation android-maps-v2

我正在为地图创建一个群集功能(v2)。我将位置分组为群集,然后将群集显示为自定义标记:

enter image description here

这很好用,但是我希望在创建和拆分群集时创建动画。我通过在标记(注释)上创建UIView动画,成功地在iOS上做到了这一点。我在网上找不到任何代码/提示。

我设法将一个简单的ImageView作为叠加层来模拟一个群集,然后使用TranslateAnimation来获得所需的动画。最后,我删除了这个视图并添加了标记。

是否有更好的方法来标记标记?

3 个答案:

答案 0 :(得分:8)

通过设置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]);
            }
        }
    }
}

答案 1 :(得分:2)

下面的代码是正确的,但我会想到一些东西。我会用16 ms == 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
        }
    }
});

答案 2 :(得分:0)

我找到了一个有效的solution

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个有点慢,也许有更好的方法?