V2 APIを使用してGoogleマップ上のマーカーをアニメーション化する最良の方法は何ですか?
私は、人々の位置を追跡し、互いを見るためにそれらを地図上に表示する地図中心のゲームに取り組んでいます。人々が移動するにつれて、マーカーを現在の位置から最新の位置にアニメーション化したいと思います。すべての人が方向を持っているので、マーカーを適切に回転させる必要があります。
新しいGoogle Maps APIを使用してそれを行う最善の方法は何ですか?
一部のGoogleエンジニアは、Androidのさまざまなバージョンすべてに対して、開始点から終了点までマーカーをアニメーション化する方法に関するいくつかのエレガントなサンプルコードを含むニースのデモビデオを提供しています。
関連するコードは次のとおりです。
https://Gist.github.com/broady/6314689
そして、そのすべての実際のデモビデオをご覧ください。
以下の古い非推奨の回答
ドキュメントでは、マーカーアイコンは変更できないと記載されています。
アイコン
マーカーに表示されるビットマップ。アイコンが未設定のままの場合、デフォルトのアイコンが表示されます。 defaultMarker(float)を使用して、デフォルトアイコンの代替色を指定できます。 マーカーを作成すると、アイコンを変更できません。
おそらくここで説明されているのと同様の方法を使用して、特定のマーカーを追跡する必要があります: マーカーをオブジェクトにリンク 、次にどのマーカーを更新する必要があるかを見つけます。マーカーで.remove()
を呼び出し、必要な「方向」に応じて回転した画像を作成し、その画像で新しいマーカーを作成し、新しいマーカーをマップに追加します。
マップを「クリア」する必要はありません。変更するマーカーを削除し、新しいマーカーを作成して、マップに追加し直すだけです。
残念ながら、新しいMaps APIはまだそれほど柔軟ではありません。うまくいけば、Googleはそれを改善し続けます。
使用例for DiscDev's answer (上記):
LatLng fromLocation = new LatLng(38.5, -100.4); // Whatever Origin coordinates
LatLng toLocation = new LatLng(37.7, -107.7); // Whatever destination coordinates
Marker marker = mMap.addMarker(new MarkerOptions().position(firstLocation));
MarkerAnimation.animateMarkerToICS(marker, toLocation, new LatLngInterpolator.Spherical());
そして、GPS /または位置情報の更新を受信する位置プロバイダーを使用している人のために:
Marker ourGlobalMarker;
// We've got a location from some provider of ours, now we can call:
private void updateMarkerPosition(Location newLocation) {
LatLng newLatLng = new LatLng(newLocation.getLatitude(), newLocation.getLongitude());
if(ourGlobalMarker == null) { // First time adding marker to map
ourGlobalMarker = mMap.addMarker(new MarkerOptions().position(newLatLng));
}
else {
MarkerAnimation.animateMarkerToICS(ourGlobalMarker, newLatLng, new LatLngInterpolator.Spherical());
}
}
重要:
1MarkerAnimation.Java
アニメーションの継続時間がXに設定されており、Xよりも小さいレートで位置の更新を受信している場合、複数のアニメーションがトリガーされ、マーカーアニメーションが少しちらつくことがあります(これはユーザーエクスペリエンスではありません)。
これを回避するには、animationMarkerToICS
メソッド(たとえば、ここでanimationMarkerToICS
を使用しました)は、次のようになります。
完全なメソッド実装:
private static Animator animator; // MAKING ANIMATOR GLOBAL INSTEAD OF LOCAL TO THE STATIC FUNCTION
...
// Ice cream sandwich compatible
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public static void animateMarkerToICS(Marker marker, LatLng finalPosition, final LatLngInterpolator latLngInterpolator) {
TypeEvaluator<LatLng> typeEvaluator = new TypeEvaluator<LatLng>() {
@Override
public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) {
return latLngInterpolator.interpolate(fraction, startValue, endValue);
}
};
Property<Marker, LatLng> property = Property.of(Marker.class, LatLng.class, "position");
// ADD THIS TO STOP ANIMATION IF ALREADY ANIMATING TO AN OBSOLETE LOCATION
if(animator != null && animator.isRunning()) {
animator.cancel();
animator = null;
}
animator = ObjectAnimator.ofObject(marker, property, typeEvaluator, finalPosition);
animator.setDuration((long) ANIMATION_DURATION);
animator.start();
}
楽しい。
マーカーには、API v2のrev.7で追加された新しい関数があります。 Marker.setIcon 。複数のアイコンを使用して方向を表示できます。
//Your code
double bearing = 0.0;
bearing = getBearing(new LatLng(
currentPosition.latitude
,currentPosition.longitude),
new LatLng(
nextPosition.latitude,
nextPosition.longitude));
bearing -= 90;
CameraPosition cameraPosition = new CameraPosition
.Builder()
.target(new LatLng(nextPosition.latitude, nextPosition.longitude))
.bearing((float) bearing)
.zoom(ZOOM_LEVEL).build();
mGoogleMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 5000, null);
animatedMarker(currentPosition,nextPosition,busMarker);
//Method for finding bearing between two points
private float getBearing(LatLng begin, LatLng end) {
double lat = Math.abs(begin.latitude - end.latitude);
double lng = Math.abs(begin.longitude - end.longitude);
if (begin.latitude < end.latitude && begin.longitude < end.longitude)
return (float) (Math.toDegrees(Math.atan(lng / lat)));
else if (begin.latitude >= end.latitude && begin.longitude < end.longitude)
return (float) ((90 - Math.toDegrees(Math.atan(lng / lat))) + 90);
else if (begin.latitude >= end.latitude && begin.longitude >= end.longitude)
return (float) (Math.toDegrees(Math.atan(lng / lat)) + 180);
else if (begin.latitude < end.latitude && begin.longitude >= end.longitude)
return (float) ((90 - Math.toDegrees(Math.atan(lng / lat))) + 270);
return -1;
}
private void animatedMarker(final LatLng startPosition,final LatLng nextPosition,final Marker mMarker)
{
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
final Interpolator interpolator = new AccelerateDecelerateInterpolator();
final float durationInMs = 3000;
final boolean hideMarker = false;
handler.post(new Runnable() {
long elapsed;
float t;
float v;
@Override
public void run() {
// Calculate progress using interpolator
elapsed = SystemClock.uptimeMillis() - start;
t = elapsed / durationInMs;
v = interpolator.getInterpolation(t);
LatLng currentPosition = new LatLng(
startPosition.latitude * (1 - t) + nextPosition.latitude * t,
startPosition.longitude * (1 - t) + nextPosition.longitude * t);
mMarker.setPosition(currentPosition);
// Repeat till progress is complete.
if (t < 1) {
// Post again 16ms later.
handler.postDelayed(this, 16);
} else {
if (hideMarker) {
mMarker.setVisible(false);
} else {
mMarker.setVisible(true);
}
}
}
});
}