使用 GroundOverlay 的脉冲动画
Pulse animation using GroundOverlay
我需要用脉冲动画显示位置A和位置B。我可以使用下面的代码来实现。但我面临的问题是 GroundOverlay 在缩放级别更改时更改其大小。如果位置 A 和 B 彼此靠近(即地图放大级别高),则脉冲半径太大。当我缩小时,它变得太小了。
无论地图的缩放级别如何,如何使叠加层的大小保持不变。
以下代码引用自这里:Animated Transparent Circle on Google Maps v2 is NOT animating correctly
private void showRipples(LatLng latLng, int color) {
GradientDrawable d = new GradientDrawable();
d.setShape(GradientDrawable.OVAL);
d.setSize(500, 500);
d.setColor(ContextCompat.getColor(Activity.this, color));
d.setStroke(0, Color.TRANSPARENT);
final Bitmap bitmap = Bitmap.createBitmap(d.getIntrinsicWidth()
, d.getIntrinsicHeight()
, Bitmap.Config.ARGB_8888);
// Convert the drawable to bitmap
final Canvas canvas = new Canvas(bitmap);
d.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
d.draw(canvas);
// Radius of the circle
final int radius = getResources().getDimensionPixelSize(R.dimen.ripple_radius);
// Add the circle to the map
final GroundOverlay circle = googleMap.addGroundOverlay(new GroundOverlayOptions()
.position(latLng, 2 * radius).image(BitmapDescriptorFactory.fromBitmap(bitmap)));
// Prep the animator
PropertyValuesHolder radiusHolder = PropertyValuesHolder.ofFloat("radius", 1, radius);
PropertyValuesHolder transparencyHolder = PropertyValuesHolder.ofFloat("transparency", 0, 1);
ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
valueAnimator.setRepeatMode(ValueAnimator.RESTART);
valueAnimator.setValues(radiusHolder, transparencyHolder);
valueAnimator.setDuration(DURATION);
valueAnimator.setEvaluator(new FloatEvaluator());
valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float animatedRadius = (float) valueAnimator.getAnimatedValue("radius");
float animatedAlpha = (float) valueAnimator.getAnimatedValue("transparency");
circle.setDimensions(animatedRadius * 2);
circle.setTransparency(animatedAlpha);
}
});
// start the animation
valueAnimator.start();
}
[这是我在两个位置相距较远时得到的结果][]1
If the two locations are close to each other I get this behaviour
对于第一张图片,如果我放大,那么我会看到脉冲动画。
有没有一种方法可以使脉冲半径保持不变,而不管缩放级别如何?
假设您希望叠加圆半径为固定尺寸(相对于
屏幕像素) 例如屏幕宽度的 1/10(当前缩放)。
// compute width of visible region
// get lat-lng of left and right points
LatLng left = googleMap.getProjection().getVisibleRegion().farLeft;
LatLng right = googleMap.getProjection().getVisibleRegion().farRight;
// compute distance between points
float[] results = new float[1];
Location.distanceBetween(left.latitude, left.longitude,right.latitude,right.longitude, results);
// scale to desired relative radius size
float scaledRadius = results[0] * 0.10F;
// and use that for radius - taken from OP code and use 'scaledRadius'
final GroundOverlay circle = googleMap.addGroundOverlay(new GroundOverlayOptions()
.position(latLng, scaledRadius).image(BitmapDescriptorFactory.fromBitmap(bitmap)));
此示例使用宽度作为缩放轴,但您也可以使用高度或对角线(通过使用投影的不同点)。
'far' 的使用可以替换为 'near' - 它用于解决倾斜问题,因此您必须进行试验。
所以现在你的资源值是一个比例因子而不是绝对半径值 - 所以对于这个例子你可以将资源值设置为 0.10F 并使用它在哪里hard-coded以上。
如果您希望脉冲(和覆盖)工作 after/during 缩放,那么您需要使用 'onCameraIdle' 事件——对 scaledRadius 使用与上面相同的计算,例如:
public void onCameraIdle() {
if (circle != null) {
// compute scaled radius as in above code...
// The 1-argument version is specifically width
circle.setDimensions(scaledRadius);
}
}
发生这种情况是因为 GroundOverlay
与 google 地图一起缩放。为避免这种情况,您应该为每个缩放级别重新创建覆盖,并为该缩放级别和纬度修正半径(meters_to_pixels
在示例源代码中)。为避免 GroundOverlay
重新创建,您应该存储创建的 GroundOverlay
对象并在创建新对象之前将其删除。为此,您需要对 showRipples()
方法进行一些更改 - 它应该 returns 创建覆盖。带有一个标记的完整源代码示例:
public class MainActivity extends AppCompatActivity implements OnMapReadyCallback {
private static final String TAG = MainActivity.class.getSimpleName();
private final LatLng RED_MARKER = new LatLng(-37.884312, 145.000623);
private GoogleMap mGoogleMap;
private MapFragment mMapFragment;
private GroundOverlay mRedPoint = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMapFragment = (MapFragment) getFragmentManager()
.findFragmentById(R.id.map_fragment);
mMapFragment.getMapAsync(this);
}
@Override
public void onMapReady(GoogleMap googleMap) {
mGoogleMap = googleMap;
mGoogleMap.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() {
@Override
public void onCameraIdle() {
// if overlay already exists - remove it
if (mRedPoint != null) {
mRedPoint.remove();
}
mRedPoint = showRipples(RED_MARKER, Color.RED);
}
});
mGoogleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(RED_MARKER, 16));
}
private GroundOverlay showRipples(LatLng latLng, int color) {
GradientDrawable d = new GradientDrawable();
d.setShape(GradientDrawable.OVAL);
d.setSize(500, 500);
d.setColor(color);
d.setStroke(0, Color.TRANSPARENT);
final Bitmap bitmap = Bitmap.createBitmap(d.getIntrinsicWidth()
, d.getIntrinsicHeight()
, Bitmap.Config.ARGB_8888);
// Convert the drawable to bitmap
final Canvas canvas = new Canvas(bitmap);
d.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
d.draw(canvas);
// Radius of the circle for current zoom level and latitude (because Earth is sphere at first approach)
double meters_to_pixels = (Math.cos(mGoogleMap.getCameraPosition().target.latitude * Math.PI /180) * 2 * Math.PI * 6378137) / (256 * Math.pow(2, mGoogleMap.getCameraPosition().zoom));
final int radius = (int)(meters_to_pixels * getResources().getDimensionPixelSize(R.dimen.ripple_radius));
// Add the circle to the map
final GroundOverlay circle = mGoogleMap.addGroundOverlay(new GroundOverlayOptions()
.position(latLng, 2 * radius).image(BitmapDescriptorFactory.fromBitmap(bitmap)));
// Prep the animator
PropertyValuesHolder radiusHolder = PropertyValuesHolder.ofFloat("radius", 1, radius);
PropertyValuesHolder transparencyHolder = PropertyValuesHolder.ofFloat("transparency", 0, 1);
ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
valueAnimator.setRepeatMode(ValueAnimator.RESTART);
valueAnimator.setValues(radiusHolder, transparencyHolder);
valueAnimator.setDuration(1000);
valueAnimator.setEvaluator(new FloatEvaluator());
valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float animatedRadius = (float) valueAnimator.getAnimatedValue("radius");
float animatedAlpha = (float) valueAnimator.getAnimatedValue("transparency");
circle.setDimensions(animatedRadius * 2);
circle.setTransparency(animatedAlpha);
}
});
// start the animation
valueAnimator.start();
return circle;
}
}
我需要用脉冲动画显示位置A和位置B。我可以使用下面的代码来实现。但我面临的问题是 GroundOverlay 在缩放级别更改时更改其大小。如果位置 A 和 B 彼此靠近(即地图放大级别高),则脉冲半径太大。当我缩小时,它变得太小了。
无论地图的缩放级别如何,如何使叠加层的大小保持不变。
以下代码引用自这里:Animated Transparent Circle on Google Maps v2 is NOT animating correctly
private void showRipples(LatLng latLng, int color) {
GradientDrawable d = new GradientDrawable();
d.setShape(GradientDrawable.OVAL);
d.setSize(500, 500);
d.setColor(ContextCompat.getColor(Activity.this, color));
d.setStroke(0, Color.TRANSPARENT);
final Bitmap bitmap = Bitmap.createBitmap(d.getIntrinsicWidth()
, d.getIntrinsicHeight()
, Bitmap.Config.ARGB_8888);
// Convert the drawable to bitmap
final Canvas canvas = new Canvas(bitmap);
d.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
d.draw(canvas);
// Radius of the circle
final int radius = getResources().getDimensionPixelSize(R.dimen.ripple_radius);
// Add the circle to the map
final GroundOverlay circle = googleMap.addGroundOverlay(new GroundOverlayOptions()
.position(latLng, 2 * radius).image(BitmapDescriptorFactory.fromBitmap(bitmap)));
// Prep the animator
PropertyValuesHolder radiusHolder = PropertyValuesHolder.ofFloat("radius", 1, radius);
PropertyValuesHolder transparencyHolder = PropertyValuesHolder.ofFloat("transparency", 0, 1);
ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
valueAnimator.setRepeatMode(ValueAnimator.RESTART);
valueAnimator.setValues(radiusHolder, transparencyHolder);
valueAnimator.setDuration(DURATION);
valueAnimator.setEvaluator(new FloatEvaluator());
valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float animatedRadius = (float) valueAnimator.getAnimatedValue("radius");
float animatedAlpha = (float) valueAnimator.getAnimatedValue("transparency");
circle.setDimensions(animatedRadius * 2);
circle.setTransparency(animatedAlpha);
}
});
// start the animation
valueAnimator.start();
}
[这是我在两个位置相距较远时得到的结果][
If the two locations are close to each other I get this behaviour
对于第一张图片,如果我放大,那么我会看到脉冲动画。
有没有一种方法可以使脉冲半径保持不变,而不管缩放级别如何?
假设您希望叠加圆半径为固定尺寸(相对于 屏幕像素) 例如屏幕宽度的 1/10(当前缩放)。
// compute width of visible region
// get lat-lng of left and right points
LatLng left = googleMap.getProjection().getVisibleRegion().farLeft;
LatLng right = googleMap.getProjection().getVisibleRegion().farRight;
// compute distance between points
float[] results = new float[1];
Location.distanceBetween(left.latitude, left.longitude,right.latitude,right.longitude, results);
// scale to desired relative radius size
float scaledRadius = results[0] * 0.10F;
// and use that for radius - taken from OP code and use 'scaledRadius'
final GroundOverlay circle = googleMap.addGroundOverlay(new GroundOverlayOptions()
.position(latLng, scaledRadius).image(BitmapDescriptorFactory.fromBitmap(bitmap)));
此示例使用宽度作为缩放轴,但您也可以使用高度或对角线(通过使用投影的不同点)。
'far' 的使用可以替换为 'near' - 它用于解决倾斜问题,因此您必须进行试验。
所以现在你的资源值是一个比例因子而不是绝对半径值 - 所以对于这个例子你可以将资源值设置为 0.10F 并使用它在哪里hard-coded以上。
如果您希望脉冲(和覆盖)工作 after/during 缩放,那么您需要使用 'onCameraIdle' 事件——对 scaledRadius 使用与上面相同的计算,例如:
public void onCameraIdle() {
if (circle != null) {
// compute scaled radius as in above code...
// The 1-argument version is specifically width
circle.setDimensions(scaledRadius);
}
}
发生这种情况是因为 GroundOverlay
与 google 地图一起缩放。为避免这种情况,您应该为每个缩放级别重新创建覆盖,并为该缩放级别和纬度修正半径(meters_to_pixels
在示例源代码中)。为避免 GroundOverlay
重新创建,您应该存储创建的 GroundOverlay
对象并在创建新对象之前将其删除。为此,您需要对 showRipples()
方法进行一些更改 - 它应该 returns 创建覆盖。带有一个标记的完整源代码示例:
public class MainActivity extends AppCompatActivity implements OnMapReadyCallback {
private static final String TAG = MainActivity.class.getSimpleName();
private final LatLng RED_MARKER = new LatLng(-37.884312, 145.000623);
private GoogleMap mGoogleMap;
private MapFragment mMapFragment;
private GroundOverlay mRedPoint = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMapFragment = (MapFragment) getFragmentManager()
.findFragmentById(R.id.map_fragment);
mMapFragment.getMapAsync(this);
}
@Override
public void onMapReady(GoogleMap googleMap) {
mGoogleMap = googleMap;
mGoogleMap.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() {
@Override
public void onCameraIdle() {
// if overlay already exists - remove it
if (mRedPoint != null) {
mRedPoint.remove();
}
mRedPoint = showRipples(RED_MARKER, Color.RED);
}
});
mGoogleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(RED_MARKER, 16));
}
private GroundOverlay showRipples(LatLng latLng, int color) {
GradientDrawable d = new GradientDrawable();
d.setShape(GradientDrawable.OVAL);
d.setSize(500, 500);
d.setColor(color);
d.setStroke(0, Color.TRANSPARENT);
final Bitmap bitmap = Bitmap.createBitmap(d.getIntrinsicWidth()
, d.getIntrinsicHeight()
, Bitmap.Config.ARGB_8888);
// Convert the drawable to bitmap
final Canvas canvas = new Canvas(bitmap);
d.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
d.draw(canvas);
// Radius of the circle for current zoom level and latitude (because Earth is sphere at first approach)
double meters_to_pixels = (Math.cos(mGoogleMap.getCameraPosition().target.latitude * Math.PI /180) * 2 * Math.PI * 6378137) / (256 * Math.pow(2, mGoogleMap.getCameraPosition().zoom));
final int radius = (int)(meters_to_pixels * getResources().getDimensionPixelSize(R.dimen.ripple_radius));
// Add the circle to the map
final GroundOverlay circle = mGoogleMap.addGroundOverlay(new GroundOverlayOptions()
.position(latLng, 2 * radius).image(BitmapDescriptorFactory.fromBitmap(bitmap)));
// Prep the animator
PropertyValuesHolder radiusHolder = PropertyValuesHolder.ofFloat("radius", 1, radius);
PropertyValuesHolder transparencyHolder = PropertyValuesHolder.ofFloat("transparency", 0, 1);
ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
valueAnimator.setRepeatMode(ValueAnimator.RESTART);
valueAnimator.setValues(radiusHolder, transparencyHolder);
valueAnimator.setDuration(1000);
valueAnimator.setEvaluator(new FloatEvaluator());
valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float animatedRadius = (float) valueAnimator.getAnimatedValue("radius");
float animatedAlpha = (float) valueAnimator.getAnimatedValue("transparency");
circle.setDimensions(animatedRadius * 2);
circle.setTransparency(animatedAlpha);
}
});
// start the animation
valueAnimator.start();
return circle;
}
}