4

Basically I want to be able to use Google Maps v2 to draw RADAR images over the Map, with good performance and no flickering or delay of the Bitmap images.

I'm currently using Maps v1 to do this, and it works great, but Maps v2 is not well suited to do that.

You would think that GroundOverlay or TileOverlay might work, but from what I've read from people who have tried using them, you have to remove() the previous image before you draw the next one, or do some kludge of trying to draw the Bitmap image as a Marker or something. It has to have good performance.

So I'd like to be able to use either a custom View, or an ImageView and use it's onDraw() to draw the Bitmap and have it be visible on the Map and move with it.

Like:

<Outer Container>

    <fragment .. />

    <com.my.customView .. />

    or

    <ImageView .. />

</Outer Container>

I've looked everywhere over the web for possible examples of the details of how to do something like that, with Maps v2, but have found nothing.

I use several custom Views in my current application so I'm familiar with those, and I have the Bounding Box details of the Bitmaps and I'm pretty sure that I can use the Projection information to help draw them.

Another issue is how to call invalidate() so that the onDraw() will be called.

I just need some example code that would show how to use the onDraw() from some kind of a View based object that I could try to see if it works, but one that doesn't try to use 'GroundOverlay', 'TileOverlay' or "Marker', I'd really appreciate it!

Thanks!

user1572522
  • 569
  • 2
  • 11
  • 26

1 Answers1

6

If you familiar with implementing custom view, you can create custom view which extends MapView class for full control of drawing on view canvas. But because MapView extends FrameLayout which is ViewGroup, you should override dispatchDraw() method, not onDraw() and implement within it radar drawing. Something like that:

@Override
public void dispatchDraw(Canvas canvas) {
    super.dispatchDraw(canvas);
    canvas.save();
    drawRadarOverTheMap(canvas);
    canvas.restore();
}

and you need to call it via invalidate() on every map move/zoom/rotate. For detect map move/zoom/rotate you need GoogleMap (exactly GoogleMap.setOnCameraMoveListener() method). You can declare GoogleMap object inside your custom MapView class and set it via setter, but better if custom MapView class will implement OnMapReadyCallback interface and get it in onMapReady() callback.

Full source code of MapView-based custom view (e.g. RadarMapView) can be like that:

public class RadarMapView extends MapView implements OnMapReadyCallback {

    private OnMapReadyCallback mMapReadyCallback;
    private GoogleMap mGoogleMap;
    private Marker mMarker;
    private Paint mPaintRadar;

    public RadarMapView(@NonNull Context context) {
        super(context);
        init();
    }

    public RadarMapView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public RadarMapView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    public RadarMapView(@NonNull Context context, @Nullable GoogleMapOptions options) {
        super(context, options);
        init();
    }

    @Override
    public void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        canvas.save();
        drawRadarOverTheMap(canvas);
        canvas.restore();
    }

    private void drawRadarOverTheMap(Canvas canvas) {
        if (mGoogleMap == null) {
            return;
        }

        final float centerX = getX() + getWidth() / 2;
        final float centerY = getY() + getHeight() / 2;

        canvas.drawCircle(centerX, centerY, 150, mPaintRadar);
        canvas.drawCircle(centerX, centerY, 300, mPaintRadar);
        canvas.drawCircle(centerX, centerY, 450, mPaintRadar);
    }

    private void init() {
        setWillNotDraw(false);

        mPaintRadar = new Paint();
        mPaintRadar.setColor(Color.GREEN);
        mPaintRadar.setStyle(Paint.Style.STROKE);
        mPaintRadar.setStrokeWidth(10);
    }

    @Override
    public void getMapAsync(OnMapReadyCallback callback) {
        mMapReadyCallback = callback;
        super.getMapAsync(this);
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        mGoogleMap = googleMap;
        mGoogleMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() {
            @Override
            public void onCameraMove() {
                invalidate();
            }
        });
        if (mMapReadyCallback != null) {
            mMapReadyCallback.onMapReady(googleMap);
        }
    }

}

And you can use it in .xml file:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="{your_package_name}.MainActivity">

    <{your_package_name}.RadarMapView
        android:id="@+id/mapview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        />
</RelativeLayout>

for MainActivity like:

public class MainActivity extends AppCompatActivity {

    private static final String MAP_VIEW_BUNDLE_KEY = "MapViewBundleKey";

    private GoogleMap mGoogleMap;
    private RadarMapView mMapView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Bundle mapViewBundle = null;
        if (savedInstanceState != null) {
            mapViewBundle = savedInstanceState.getBundle(MAP_VIEW_BUNDLE_KEY);
        }

        mMapView = (RadarMapView) findViewById(R.id.mapview);
        mMapView.onCreate(mapViewBundle);
        mMapView.getMapAsync(new OnMapReadyCallback() {
            @Override
            public void onMapReady(GoogleMap googleMap) {
                mGoogleMap = googleMap;
            }
        });

    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        Bundle mapViewBundle = outState.getBundle(MAP_VIEW_BUNDLE_KEY);
        if (mapViewBundle == null) {
            mapViewBundle = new Bundle();
            outState.putBundle(MAP_VIEW_BUNDLE_KEY, mapViewBundle);
        }

        mMapView.onSaveInstanceState(mapViewBundle);
    }

    @Override
    protected void onResume() {
        super.onResume();
        mMapView.onResume();
    }

    @Override
    protected void onStart() {
        super.onStart();
        mMapView.onStart();
    }

    @Override
    protected void onStop() {
        super.onStop();
        mMapView.onStop();
    }
    @Override
    protected void onPause() {
        mMapView.onPause();
        super.onPause();
    }
    @Override
    protected void onDestroy() {
        mMapView.onDestroy();
        super.onDestroy();
    }
    @Override
    public void onLowMemory() {
        super.onLowMemory();
        mMapView.onLowMemory();
    }

}

and you should get something like that:

Radar MapView

Also you can draw bitmap Radar image on custom view canvas via canvas.drawBitmap() method.

And also you can place ImageView over MapView or MapFragment (e.g. within RelativeLayout)

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment
        android:id="@+id/map"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        class="com.google.android.gms.maps.MapFragment"
    />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:src="@drawable/radar"
    />

</RelativeLayout>

and draw (or even just set) Radar images on ImageView.

Andrii Omelchenko
  • 13,183
  • 12
  • 43
  • 79
  • Thanks a lot for your answer! I'll check it out! – user1572522 May 29 '18 at 20:06
  • Thanks for your answer Andrii! Sorry for the delay, but do you have a complete example project anywhere? I'm having trouble getting anything to build. There is so little out there on doing this. Thanks!! – user1572522 Jun 07 '18 at 13:41
  • @user1572522 Just create new project with empty Activity add new class `RadarMapView.java` and replace `MainActivity.java` and `activity_main.xml` code by code from answer. Source code from answer is full complete (except `{your_package_name}.MainActivity` - you should replace it by yours). – Andrii Omelchenko Jun 07 '18 at 14:04
  • Thanks Andrii! I got it to build. The google-play-services.jar I built with the project didn't have OnCameraMoveListener, so I changed it to use OnCameraChangeListener and it worked. Thanks for you help! – user1572522 Jun 09 '18 at 14:55