2

マップ(v2)のクラスター機能を作成しています。場所をクラスターにグループ化し、クラスターをカスタムマーカーとして表示します。

ここに画像の説明を入力してください

これはうまく機能しますが、クラスターが作成されて分割されたときにアニメーションを作成したいと思います。マーカー(注釈)にUIViewアニメーションを作成することで、iOSでこれを正常に実行しました。Android用のコード/ヒントをオンラインで見つけることができませんでした。

クラスターに似たオーバーレイとして単純なImageViewを取得し、を使用しTranslateAnimationて目的のアニメーションを取得することができました。最後に、このビューを削除してマーカーを追加しました。

マーカーをアニメーション化するためのより良い方法はありますか?

4

3 に答える 3

8

GoogleMapの前でビューを追加および削除するのではなく、Markerオブジェクトをアニメーション化することで正しい方向に進んでいますが、Animatorオブジェクトを使用してMarkerをアニメーション化すると、パフォーマンスが向上します。

ハンドラーと遅延実行可能アプローチを使用すると、ターゲットフレームレートを効果的にハードコーディングできます。遅延が短すぎるRunnableを投稿すると、アニメーションの実行に時間がかかります。高すぎるとフレームレートが遅くなり、強力なデバイスでも途切れ途切れに見えます。ハンドラーや遅延ランナブルよりもアニメーターを使用する利点は、システムが処理できる頻度で次のフレームを描画するためにonAnimationUpdate()のみを呼び出すことです。

私のクラスタリングライブラリであるClusterkrafでは、ObjectAnimator(下位互換性のためにNineOldAndroidsから)を使用して、ズームレベルを変更するときのクラスター遷移をアニメーション化しましたGalaxyNexusで約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]);
            }
        }
    }
}
于 2013-04-30T14:42:44.273 に答える
2

以下のコードは正しいですが、私は何かをpuntualizeします。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
        }
    }
});
于 2013-09-23T08:43:53.483 に答える
1

私はうまくいく解決策を見つけました:

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で少し遅いですが、もっと良い方法があるのではないでしょうか。

于 2013-02-01T15:26:40.620 に答える