How can I draw a static target circle on Google Maps?

前端 未结 1 1207
有刺的猬
有刺的猬 2020-12-22 02:28

I\'ve been looking into drawing a circle for a static radius on top of Google Maps and all the answers I came across describe drawing markers and circles which are tied to a

相关标签:
1条回答
  • 2020-12-22 02:50

    You can create a custom View to draw the circle. I have based my example on Draw transparent circle filled outside

    Here you can find a tutorial about how to create custom views.

    In my example I am creating a custom RadarOverlayView with a radius parameter that I use to compute the area.

    My custom view code:

    public class RadarOverlayView extends LinearLayout {
        private Bitmap windowFrame;
        private float radius = 0f;
        private int centerX = 0;
        private int centerY = 0;
    
        public RadarOverlayView(Context context) {
            super(context);
        }
    
        public RadarOverlayView(Context context, AttributeSet attrs) {
            super(context, attrs);
            TypedArray a = context.getTheme().obtainStyledAttributes(
                    attrs, R.styleable.RadarOverlayView, 0, 0);
    
            try {
                radius = a.getDimension(R.styleable.RadarOverlayView_radius, 0f);
            } finally {
                a.recycle();
            }
        }
    
        @Override
        protected void dispatchDraw(Canvas canvas) {
            super.dispatchDraw(canvas);
    
            if (windowFrame == null) {
                createWindowFrame();
            }
            canvas.drawBitmap(windowFrame, 0, 0, null);
        }
    
        @Override
        public boolean isEnabled() {
            return false;
        }
    
        @Override
        public boolean isClickable() {
            return false;
        }
    
        protected void createWindowFrame() {
            windowFrame = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
            Canvas osCanvas = new Canvas(windowFrame);
    
            centerX = getWidth() / 2;
            centerY = getHeight() / 2;
    
            if (radius > 0) {
                Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    
                // Draw the circunference
                paint.setStyle(Paint.Style.STROKE);
                paint.setColor(Color.RED);
                paint.setAlpha(200);
                paint.setStrokeWidth(5);
                osCanvas.drawCircle(centerX, centerY, radius, paint);
    
                // Draw the circle
                paint.setStyle(Paint.Style.FILL);
                paint.setColor(Color.RED);
                paint.setAlpha(100);
                osCanvas.drawCircle(centerX, centerY, radius, paint);
    
                // Draw the center icon
                paint.setAlpha(255);
                Bitmap centerBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
                osCanvas.drawBitmap(centerBitmap, centerX - centerBitmap.getWidth() / 2,
                        centerY - centerBitmap.getHeight() / 2,
                        paint);
            }
        }
    
        @Override
        public boolean isInEditMode() {
            return true;
        }
    
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            super.onLayout(changed, l, t, r, b);
            windowFrame = null;
        }
    
        public float getRadius() {
            return radius;
        }
    
        public int getCenterX() {
            return centerX;
        }
    
        public int getCenterY() {
            return centerY;
        }
    }
    

    My attrs.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <declare-styleable name="RadarOverlayView">
            <attr name="radius" format="dimension" />
        </declare-styleable>
    </resources>
    

    My activity_maps.xml layout:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                    xmlns:tools="http://schemas.android.com/tools"
                    xmlns:app="http://schemas.android.com/apk/res-auto"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:orientation="vertical">
        <fragment
            android:id="@+id/map"
            android:name="myPackage.MySupportMapFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".MapsActivity"/>
        <myPackage.RadarOverlayView
            android:id="@+id/radar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_centerInParent="true"
            app:radius="150dp" />
    </RelativeLayout>
    

    My Activity:

    public class MapsActivity extends FragmentActivity implements GoogleMap.OnCameraChangeListener {
        private GoogleMap mMap;
        private RadarOverlayView radarView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_maps);
            radarView = (RadarOverlayView) findViewById(R.id.radar);
            setUpMapIfNeeded();
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            setUpMapIfNeeded();
        }
    
        private void setUpMapIfNeeded() {
            if (mMap == null) {
                mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map))
                        .getMap();
                if (mMap != null) {
                    setUpMap();
                }
            }
        }
    
        private void setUpMap() {
            mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
            mMap.getUiSettings().setAllGesturesEnabled(true);
            mMap.getUiSettings().setZoomControlsEnabled(true);
    
            mMap.setOnCameraChangeListener(this);
        }
    
        @Override
        public void onCameraChange(final CameraPosition cameraPosition) {
            // Compute the area of the circle each time the camera changes
    
            LatLng center = mMap.getProjection().fromScreenLocation(
                    new Point(radarView.getCenterX(), radarView.getCenterY()));
            LatLng right = mMap.getProjection().fromScreenLocation(
                    new Point(radarView.getCenterX() + Math.round(radarView.getRadius()),
                            radarView.getCenterY()));
    
            Location locationCenter = new Location("center");
            locationCenter.setLatitude(center.latitude);
            locationCenter.setLongitude(center.longitude);
    
            Location locationRight = new Location("right");
            locationRight.setLatitude(right.latitude);
            locationRight.setLongitude(right.longitude);
    
            double geoRadius = locationCenter.distanceTo(locationRight);
            double geoArea = Math.PI * Math.pow(geoRadius, 2);
    
            // Uncomment to inspect the difference between
            // RadarOverlayView circle and geographic circle:
            // mMap.clear();
            // Circle circle = mMap.addCircle(new CircleOptions()
            //        .center(cameraPosition.target)
            //        .radius(geoRadius)
            //        .strokeColor(Color.GREEN)
            //        .fillColor(Color.BLUE));
    
            Toast.makeText(this, "Area: " + geoArea, Toast.LENGTH_SHORT).show();
        }
    }
    

    The result looks like this and shows a Toast with the area covered by the circle each time the camera changes:

    Limitations:

    The example is drawing a perfect circle in a View, but this circle is not guaranteed to be geographically accurate depending on the zoom level.

    You can see that, at high zoom levels, there is a huge difference between the circle drawn by the custom view and a geographically accurate circle (based on the camera target and radius) if you uncomment the mMap.addCircle code on the onCameraChange method:

    This difference that is caused by the projection of the map (WGS84), is huge at high zoom levels, and decreases at lower zoom levels:

    0 讨论(0)
提交回复
热议问题