如何在谷歌地图自定义标记中的圆形图像周围产生涟漪?

M.Y*_*ran 0 android google-maps marker

这可能是一个愚蠢的问题,但我找不到任何解决方案,以前关于这个问题的答案现在让我发布我的问题,我需要在谷歌地图标记中的圆形图像周围产生涟漪效应,现在让我发布我迄今为止尝试过的内容:

  @Override
    public void onLocationChanged(final Location location) {
        mLastLocation=location;
        GradientDrawable d = new GradientDrawable();
        d.setShape(GradientDrawable.OVAL);
        d.setSize(500,500);
        d.setColor(0x555751FF);
        d.setStroke(5, Color.TRANSPARENT);

        Bitmap bitmap = Bitmap.createBitmap(d.getIntrinsicWidth()
                , d.getIntrinsicHeight()
                , Bitmap.Config.ARGB_8888);
        final Canvas canvas = new Canvas(bitmap);
        d.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        d.draw(canvas);
        final int radius = 100;
        final GroundOverlay circle = mMap.addGroundOverlay(new GroundOverlayOptions()
                .position(new LatLng(location.getLatitude(),location.getLongitude()), 2 * radius).image(BitmapDescriptorFactory.fromBitmap(createMarker(TrackingActivity.this,new LatLng(location.getLatitude(),location.getLongitude())))));
        ValueAnimator valueAnimator = new ValueAnimator();
        valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
        valueAnimator.setRepeatMode(ValueAnimator.RESTART);
        valueAnimator.setIntValues(0, radius);
        valueAnimator.setDuration(3000);
        valueAnimator.setEvaluator(new IntEvaluator());
        valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                float animatedFraction = valueAnimator.getAnimatedFraction();
                    circle.setTransparency(animatedFraction);
              //  circle.setDimensions(animatedFraction * radius * 2);
            }
        });
        valueAnimator.start();
        //       mMap.addMarker(new MarkerOptions().position(new LatLng(location.getLatitude(),location.getLongitude())).icon(BitmapDescriptorFactory.fromBitmap(createMarker(this,new LatLng(location.getLatitude(),location.getLongitude())))));
        closeDialog(); 


   public  Bitmap createMarker(Context context, LatLng point) {
        int px = context.getResources().getDimensionPixelSize(R.dimen.map_marker_diameter);
        View markerView = ((LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.circle_imgview, null);
        markerView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        markerView.layout(0, 0, px, px);
        markerView.buildDrawingCache();
        CircleImageView bedNumberTextView = markerView.findViewById(R.id.circleimg);
        Bitmap mDotMarkerBitmap = Bitmap.createBitmap(px, px, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(mDotMarkerBitmap);
           Picasso.with(this).load("https://fww").into(bedNumberTextView);
        markerView.draw(canvas);
        return mDotMarkerBitmap;
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的代码使我的整个圆形图像产生涟漪,但我只想在我的圆形图像周围产生涟漪,我该如何实现这一点。即使我尝试使用第三方涟漪动画并将我的图像包裹在该动画中,但它无法实现我如何实现这个朋友。提前致谢!!

Jik*_*iks 5

试试这个代码。它很容易实现。我希望此代码对您有所帮助。

MapRipple.class

public class MapRipple {
    private GoogleMap mGoogleMap;
    private LatLng mLatLng, mPrevLatLng;
    private BitmapDescriptor mBackgroundImageDescriptor;  
    private float mTransparency = 0.5f;                  
    private volatile double mDistance = 100;             
    private int mNumberOfRipples = 1;                    
    private int mFillColor = Color.TRANSPARENT;           
    private int mStrokeColor = Color.BLACK;               
    private int mStrokeWidth = 5;                       
    private long mDurationBetweenTwoRipples = 1000;      
    private long mRippleDuration = 2000;                 
    private ValueAnimator mAnimators[];
    private Handler mHandlers[];
    private GroundOverlay mGroundOverlays[];
    private GradientDrawable mBackground;
    private boolean isAnimationRunning = false;

    public MapRipple(GoogleMap googleMap, LatLng latLng, Context context) {
        mGoogleMap = googleMap;
        mLatLng = latLng;
        mPrevLatLng = latLng;
        mBackground = (GradientDrawable) ContextCompat.getDrawable(context, R.drawable.map_background);
        mAnimators = new ValueAnimator[4];
        mHandlers = new Handler[4];
        mGroundOverlays = new GroundOverlay[4];
    }


    public MapRipple withTransparency(float transparency) {
        mTransparency = transparency;
        return this;
    }


    public MapRipple withDistance(double distance) {

        mDistance = distance;
        return this;
    }


    public MapRipple withLatLng(LatLng latLng) {
        mPrevLatLng = mLatLng;
        mLatLng = latLng;
        return this;
    }


    public MapRipple withNumberOfRipples(int numberOfRipples) {
        if (numberOfRipples > 4 || numberOfRipples < 1) {
            numberOfRipples = 4;
        }
        mNumberOfRipples = numberOfRipples;
        return this;
    }


    public MapRipple withFillColor(int fillColor) {
        mFillColor = fillColor;
        return this;
    }


    public MapRipple withStrokeColor(int strokeColor) {
        mStrokeColor = strokeColor;
        return this;
    }


    @Deprecated
    public void withStrokewidth(int strokeWidth) {
        mStrokeWidth = strokeWidth;
    }


    public MapRipple withStrokeWidth(int strokeWidth) {
        mStrokeWidth = strokeWidth;
        return this;
    }


    public MapRipple withDurationBetweenTwoRipples(long durationBetweenTwoRipples) {
        mDurationBetweenTwoRipples = durationBetweenTwoRipples;
        return this;
    }


    public boolean isAnimationRunning() {
        return isAnimationRunning;
    }


    public MapRipple withRippleDuration(long rippleDuration) {
        mRippleDuration = rippleDuration;
        return this;
    }

    private final Runnable mCircleOneRunnable = new Runnable() {
        @Override
        public void run() {
            mGroundOverlays[0] = mGoogleMap.addGroundOverlay(new GroundOverlayOptions()
                    .position(mLatLng, (int) mDistance)
                    .transparency(mTransparency)
                    .image(mBackgroundImageDescriptor));
            startAnimation(0);
        }
    };

    private final Runnable mCircleTwoRunnable = new Runnable() {
        @Override
        public void run() {
            mGroundOverlays[1] = mGoogleMap.addGroundOverlay(new GroundOverlayOptions()
                    .position(mLatLng, (int) mDistance)
                    .transparency(mTransparency)
                    .image(mBackgroundImageDescriptor));
            startAnimation(1);
        }
    };

    private final Runnable mCircleThreeRunnable = new Runnable() {
        @Override
        public void run() {
            mGroundOverlays[2] = mGoogleMap.addGroundOverlay(new GroundOverlayOptions()
                    .position(mLatLng, (int) mDistance)
                    .transparency(mTransparency)
                    .image(mBackgroundImageDescriptor));
            startAnimation(2);
        }
    };

    private final Runnable mCircleFourRunnable = new Runnable() {
        @Override
        public void run() {
            mGroundOverlays[3] = mGoogleMap.addGroundOverlay(new GroundOverlayOptions()
                    .position(mLatLng, (int) mDistance)
                    .transparency(mTransparency)
                    .image(mBackgroundImageDescriptor));
            startAnimation(3);
        }
    };

    private void startAnimation(final int numberOfRipple) {
        ValueAnimator animator = ValueAnimator.ofInt(0, (int) mDistance);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.setRepeatMode(ValueAnimator.RESTART);
        animator.setDuration(mRippleDuration);
        animator.setEvaluator(new IntEvaluator());
        animator.setInterpolator(new LinearInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                int animated = (int) valueAnimator.getAnimatedValue();
                mGroundOverlays[numberOfRipple].setDimensions(animated);
                if (mDistance - animated <= 10) {
                    if (mLatLng != mPrevLatLng) {
                        mGroundOverlays[numberOfRipple].setPosition(mLatLng);
                    }
                }
            }
        });
        animator.start();
        mAnimators[numberOfRipple] = animator;
    }

    private void setDrawableAndBitmap() {
        mBackground.setColor(mFillColor);
        mBackground.setStroke(UiUtil.dpToPx(mStrokeWidth), mStrokeColor);
        mBackgroundImageDescriptor = UiUtil.drawableToBitmapDescriptor(mBackground);
    }


    public void stopRippleMapAnimation() {
        if (isAnimationRunning) {
            try {
                for (int i = 0; i < mNumberOfRipples; i++) {
                    if (i == 0) {
                        mHandlers[i].removeCallbacks(mCircleOneRunnable);
                        mAnimators[i].cancel();
                        mGroundOverlays[i].remove();
                    }
                    if (i == 1) {
                        mHandlers[i].removeCallbacks(mCircleTwoRunnable);
                        mAnimators[i].cancel();
                        mGroundOverlays[i].remove();
                    }
                    if (i == 2) {
                        mHandlers[i].removeCallbacks(mCircleThreeRunnable);
                        mAnimators[i].cancel();
                        mGroundOverlays[i].remove();
                    }
                    if (i == 3) {
                        mHandlers[i].removeCallbacks(mCircleFourRunnable);
                        mAnimators[i].cancel();
                        mGroundOverlays[i].remove();
                    }
                }
            } catch (Exception e) {
                //no need to handle it
            }
        }
        isAnimationRunning = false;
    }

    public void startRippleMapAnimation() {
        if (!isAnimationRunning) {
            setDrawableAndBitmap();
            for (int i = 0; i < mNumberOfRipples; i++) {
                if (i == 0) {
                    mHandlers[i] = new Handler();
                    mHandlers[i].postDelayed(mCircleOneRunnable, mDurationBetweenTwoRipples * i);
                }
                if (i == 1) {
                    mHandlers[i] = new Handler();
                    mHandlers[i].postDelayed(mCircleTwoRunnable, mDurationBetweenTwoRipples * i);
                }
                if (i == 2) {
                    mHandlers[i] = new Handler();
                    mHandlers[i].postDelayed(mCircleThreeRunnable, mDurationBetweenTwoRipples * i);
                }
                if (i == 3) {
                    mHandlers[i] = new Handler();
                    mHandlers[i].postDelayed(mCircleFourRunnable, mDurationBetweenTwoRipples * i);
                }
            }
        }
        isAnimationRunning = true;
    }
}
Run Code Online (Sandbox Code Playgroud)

R.drawable.map_background

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <size
        android:width="150dp"
        android:height="150dp" />
    <stroke
        android:width="0.5dp"
        android:color="#000000" />
</shape>
Run Code Online (Sandbox Code Playgroud)

PolyUtil类

public class PolyUtil {

        private PolyUtil() {}


        private static double tanLatGC(double lat1, double lat2, double lng2, double lng3) {
            return (tan(lat1) * sin(lng2 - lng3) + tan(lat2) * sin(lng3)) / sin(lng2);
        }


        private static double mercatorLatRhumb(double lat1, double lat2, double lng2, double lng3) {
            return (mercator(lat1) * (lng2 - lng3) + mercator(lat2) * lng3) / lng2;
        }


        private static boolean intersects(double lat1, double lat2, double lng2,
                                          double lat3, double lng3, boolean geodesic) {
            // Both ends on the same side of lng3.
            if ((lng3 >= 0 && lng3 >= lng2) || (lng3 < 0 && lng3 < lng2)) {
                return false;
            }
            // Point is South Pole.
            if (lat3 <= -PI/2) {
                return false;
            }
            // Any segment end is a pole.
            if (lat1 <= -PI/2 || lat2 <= -PI/2 || lat1 >= PI/2 || lat2 >= PI/2) {
                return false;
            }
            if (lng2 <= -PI) {
                return false;
            }
            double linearLat = (lat1 * (lng2 - lng3) + lat2 * lng3) / lng2;
            // Northern hemisphere and point under lat-lng line.
            if (lat1 >= 0 && lat2 >= 0 && lat3 < linearLat) {
                return false;
            }
            // Southern hemisphere and point above lat-lng line.
            if (lat1 <= 0 && lat2 <= 0 && lat3 >= linearLat) {
                return true;
            }
            // North Pole.
            if (lat3 >= PI/2) {
                return true;
            }

            return geodesic ?
                tan(lat3) >= tanLatGC(lat1, lat2, lng2, lng3) :
                mercator(lat3) >= mercatorLatRhumb(lat1, lat2, lng2, lng3);
        }

        public static boolean containsLocation(LatLng point, List<LatLng> polygon, boolean geodesic) {
            return containsLocation(point.latitude, point.longitude, polygon, geodesic);
        }


        public static boolean containsLocation(double latitude, double longitude, List<LatLng> polygon, boolean geodesic) {
            final int size = polygon.size();
            if (size == 0) {
                return false;
            }
            double lat3 = toRadians(latitude);
            double lng3 = toRadians(longitude);
            LatLng prev = polygon.get(size - 1);
            double lat1 = toRadians(prev.latitude);
            double lng1 = toRadians(prev.longitude);
            int nIntersect = 0;
            for (LatLng point2 : polygon) {
                double dLng3 = wrap(lng3 - lng1, -PI, PI);
                // Special case: point equal to vertex is inside.
                if (lat3 == lat1 && dLng3 == 0) {
                    return true;
                }
                double lat2 = toRadians(point2.latitude);
                double lng2 = toRadians(point2.longitude);
                // Offset longitudes by -lng1.
                if (intersects(lat1, lat2, wrap(lng2 - lng1, -PI, PI), lat3, dLng3, geodesic)) {
                    ++nIntersect;
                }
                lat1 = lat2;
                lng1 = lng2;
            }
            return (nIntersect & 1) != 0;
        }

        private static final double DEFAULT_TOLERANCE = 0.1;  // meters.


        public static boolean isLocationOnEdge(LatLng point, List<LatLng> polygon, boolean geodesic,
                                               double tolerance) {
            return isLocationOnEdgeOrPath(point, polygon, true, geodesic, tolerance);
        }


        public static boolean isLocationOnEdge(LatLng point, List<LatLng> polygon, boolean geodesic) {
            return isLocationOnEdge(point, polygon, geodesic, DEFAULT_TOLERANCE);
        }


        public static boolean isLocationOnPath(LatLng point, List<LatLng> polyline,
                                               boolean geodesic, double tolerance) {
            return isLocationOnEdgeOrPath(point, polyline, false, geodesic, tolerance);
        }


        public static boolean isLocationOnPath(LatLng point, List<LatLng> polyline,
                                               boolean geodesic) {
            return isLocationOnPath(point, polyline, geodesic, DEFAULT_TOLERANCE);
        }

        private static boolean isLocationOnEdgeOrPath(LatLng point, List<LatLng> poly, boolean closed,
                                                      boolean geodesic, double toleranceEarth) {
            int idx = locationIndexOnEdgeOrPath(point, poly, closed, geodesic, toleranceEarth);

            return (idx >= 0);
        }


        public static int locationIndexOnPath(LatLng point, List<LatLng> poly,
                                              boolean geodesic, double tolerance) {
            return locationIndexOnEdgeOrPath(point, poly, false, geodesic, tolerance);
        }


        public static int locationIndexOnPath(LatLng point, List<LatLng> polyline,
                                              boolean geodesic) {
            return locationIndexOnPath(point, polyline, geodesic, DEFAULT_TOLERANCE);
        }


        private static int locationIndexOnEdgeOrPath(LatLng point, List<LatLng> poly, boolean closed,
                                                     boolean geodesic, double toleranceEarth) {
            int size = poly.size();
            if (size == 0) {
                return -1;
            }
            double tolerance = toleranceEarth / EARTH_RADIUS;
            double havTolerance = hav(tolerance);
            double lat3 = toRadians(point.latitude);
            double lng3 = toRadians(point.longitude);
            LatLng prev = poly.get(closed ? size - 1 : 0);
            double lat1 = toRadians(prev.latitude);
            double lng1 = toRadians(prev.longitude);
            int idx = 0;
            if (geodesic) {
                for (LatLng point2 : poly) {
                    double lat2 = toRadians(point2.latitude);
                    double lng2 = toRadians(point2.longitude);
                    if (isOnSegmentGC(lat1, lng1, lat2, lng2, lat3, lng3, havTolerance)) {
                        return Math.max(0, idx - 1);
                    }
                    lat1 = lat2;
                    lng1 = lng2;
                    idx++;
                }
            } else {

                double minAcceptable = lat3 - tolerance;
                double maxAcceptable = lat3 + tolerance;
                double y1 = mercator(lat1);
                double y3 = mercator(lat3);
                double[] xTry = new double[3];
                for (LatLng point2 : poly) {
                    double lat2 = toRadians(point2.latitude);
                    double y2 = mercator(lat2);
                    double lng2 = toRadians(point2.longitude);
                    if (max(lat1, lat2) >= minAcceptable && min(lat1, lat2) <= maxAcceptable) {
                        // We offset longitudes by -lng1; the implicit x1 is 0.
                        double x2 = wrap(lng2 - lng1, -PI, PI);
                        double x3Base = wrap(lng3 - lng1, -PI, PI);
                        xTry[0] = x3Base;
                        // Also explore wrapping of x3Base around the world in both directions.
                        xTry[1] = x3Base + 2 * PI;
                        xTry[2] = x3Base - 2 * PI;
                        for (double x3 : xTry) {
                            double dy = y2 - y1;
                            double len2 = x2 * x2 + dy * dy;
                            double t = len2 <= 0 ? 0 : clamp((x3 * x2 + (y3 - y1) * dy) / len2, 0, 1);
                            double xClosest = t * x2;
                            double yClosest = y1 + t * dy;
                            double latClosest = inverseMercator(yClosest);
                            double havDist = havDistance(lat3, latClosest, x3 - xClosest);
                            if (havDist < havTolerance) {
                                return Math.max(0, idx - 1);
                            }
                        }
                    }
                    lat1 = lat2;
                    lng1 = lng2;
                    y1 = y2;
                    idx++;
                }
            }
            return -1;
        }


        private static double sinDeltaBearing(double lat1, double lng1, double lat2, double lng2,
                                              double lat3, double lng3) {
            double sinLat1 = sin(lat1);
            double cosLat2 = cos(lat2);
            double cosLat3 = cos(lat3);
            double lat31 = lat3 - lat1;
            double lng31 = lng3 - lng1;
            double lat21 = lat2 - lat1;
            double lng21 = lng2 - lng1;
            double a = sin(lng31) * cosLat3;
            double c = sin(lng21) * cosLat2;
            double b = sin(lat31) + 2 * sinLat1 * cosLat3 * hav(lng31);
            double d = sin(lat21) + 2 * sinLat1 * cosLat2 * hav(lng21);
            double denom = (a * a + b * b) * (c * c + d * d);
            return denom <= 0 ? 1 : (a * d - b * c) / sqrt(denom);
        }

        private static boolean isOnSegmentGC(double lat1, double lng1, double lat2, double lng2,
                                             double lat3, double lng3, double havTolerance) {
            double havDist13 = havDistance(lat1, lat3, lng1 - lng3);
            if (havDist13 <= havTolerance) {
                return true;
            }
            double havDist23 = havDistance(lat2, lat3, lng2 - lng3);
            if (havDist23 <= havTolerance) {
                return true;
            }
            double sinBearing = sinDeltaBearing(lat1, lng1, lat2, lng2, lat3, lng3);
            double sinDist13 = sinFromHav(havDist13);
            double havCrossTrack = havFromSin(sinDist13 * sinBearing);
            if (havCrossTrack > havTolerance) {
                return false;
            }
            double havDist12 = havDistance(lat1, lat2, lng1 - lng2);
            double term = havDist12 + havCrossTrack * (1 - 2 * havDist12);
            if (havDist13 > term || havDist23 > term) {
                return false;
            }
            if (havDist12 < 0.74) {
                return true;
            }
            double cosCrossTrack = 1 - 2 * havCrossTrack;
            double havAlongTrack13 = (havDist13 - havCrossTrack) / cosCrossTrack;
            double havAlongTrack23 = (havDist23 - havCrossTrack) / cosCrossTrack;
            double sinSumAlongTrack = sinSumFromHav(havAlongTrack13, havAlongTrack23);
            return sinSumAlongTrack > 0;  // Compare with half-circle == PI using sign of sin().
        }


        public static List<LatLng> simplify(List<LatLng> poly, double tolerance) {
            final int n = poly.size();
            if (n < 1) {
                throw new IllegalArgumentException("Polyline must have at least 1 point");
            }
            if (tolerance <= 0) {
                throw new IllegalArgumentException("Tolerance must be greater than zero");
            }

            boolean closedPolygon = isClosedPolygon(poly);
            LatLng lastPoint = null;

           if (closedPolygon) {
                final