【问题标题】:Android Maps v2 - animate markersAndroid Maps v2 - 动画标记
【发布时间】:2013-09-23 16:43:53
【问题描述】:

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

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

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

有没有更好的方法来为标记设置动画?

【问题讨论】:

  • 你是怎么把圆圈弄那么大的?或者你只是给了它一个很大的半径值?
  • 这是一个自定义视图。集群中的位置越多,半径越大。
  • 你如何定义它的大小?你是在谷歌地图上画那些圆圈吗?
  • 我正在使用 new MarkerOptions().position(position).icon(BitmapDescriptorFactory.fromBitmap(getBitmapForCluster(cluster)) - getBitmapForCluster(cluster) 是一种返回位图的自定义方法,在这种情况下圆圈
  • 我以为是Circles。嗯,还有一件事,那些圆圈是否固定在它们的中心位置?另外,什么是appr。这些图像的大小?

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


【解决方案1】:

通过为 Marker 对象设置动画而不是在 GoogleMap 前面添加和删除视图,您走在正确的轨道上,但是如果您使用 Animator 对象为 Marker 设置动画,您可以获得更好的性能。

使用 Handler 和延迟的 Runnable 方法,您可以有效地对目标帧速率进行硬编码。如果您以太低的延迟发布 Runnable,您的动画将需要更长的时间来执行。如果太高,帧速率会太慢,即使在功能强大的设备上也会显得断断续续。使用 Animator 而不是 Handler 和延迟的 Runnable 的优点是它只会调用 onAnimationUpdate() 来绘制下一帧,只要系统可以处理。

在我的集群库 Clusterkraf 中,我使用 ObjectAnimator(来自 NineOldAndroids 以实现向后兼容性)在更改缩放级别时为集群过渡设置动画。它可以在我的 Galaxy Nexus 上平滑地为大约 100 个标记设置动画。

下面是一个 sn-p,概述了如何使其工作。

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]);
            }
        }
    }
}

【讨论】:

  • 这听起来很有希望,我会在下一个版本中尝试一下。谢谢!
【解决方案2】:

下面的代码是正确的,但我会强调一些东西。我会使用 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
        }
    }
});

【讨论】:

    【解决方案3】:

    我找到了一个有效的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个左右有点慢,也许有更好的方法?

    【讨论】:

    • 由于对Marker 的每次更改都涉及 IPC 回到 Play Services Framework 应用程序,我怀疑它会变得更好,而且可能会更糟。
    猜你喜欢
    • 1970-01-01
    • 2013-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多