グーグルマップにUFO出現 2020/09/09
動画にしました。

新宿駅付近にUFOが出現してレーザー光線で破壊しています。処理としては、
衛星写真地図にUFOのイラストを描画する。
           レーザー光線を描画する。
           爆発の描画をする。
タイマーで制御する。
UfoViewをViewから派生させて作成する。
MapsActivityでaddContentView()によりUfoViewを取り込む。
UfoView.javaのソースです。

package net.sadaji.mapbasic;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.RectF;
import android.view.View;

import java.util.ArrayList;

public class UfoView  extends View {
    private Bitmap m_bmpUfo, m_bmpExplosion;
    private MapsActivity m_Activity;
    private int m_x =0;
    private int m_y = 500;
    private Path m_Path;
    private ArrayList m_ExplodedPoints = new ArrayList();
    private int m_ExplodedSize = 0;
    private int m_UfoState = 0, m_Attack = 0;
    //コンストラクタ
    UfoView(Context context) {
        super(context);
        m_Activity = (MapsActivity)context;

        BitmapFactory.Options opts = new BitmapFactory.Options();
        opts.inScaled = false;

        m_bmpUfo = BitmapFactory.decodeResource(m_Activity.getResources(),
                R.drawable.ufo, opts);
        m_bmpExplosion  = BitmapFactory.decodeResource(m_Activity.getResources(),
                R.drawable.explosion, opts);
        m_Path = new Path();
    }

    @Override
    protected void onDraw(Canvas canvas){
        drawUfo(canvas);
        drawExplosion(canvas);
        updatePosition(canvas);
}

    private void drawUfo(Canvas canvas){
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        canvas.drawBitmap(m_bmpUfo, m_x, m_y, paint);
    }

    private void drawExplosion(Canvas canvas){
        int size, i;
        Point pt;
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        size = m_ExplodedPoints.size();
        for(i =0; i < size; i++){
            pt  = m_ExplodedPoints.get(i);
            canvas.drawBitmap(m_bmpExplosion, pt.x, pt.y, paint);
        }
    }

    private void drawLaserBeam(Canvas canvas){
        int x, y;
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(20);
        paint.setColor(Color.rgb(255, 255, 0));
        paint.setPathEffect(new DashPathEffect(new float[]{20, 20}, 0));
        m_Path.rewind();
        x = m_x + m_bmpUfo.getWidth() / 2;
        y = m_y + m_bmpUfo.getHeight();
        m_Path.moveTo(x, y);
        m_Path.lineTo(x, y + 200);
        canvas.drawPath(m_Path, paint);
    }


    private void updatePosition(Canvas canvas){
        Point pt = new Point();

        if(m_UfoState == 0) {
            m_x += 10;
            if (m_x > canvas.getWidth()) {
                m_x = 0;
                m_ExplodedPoints.clear();
            }
            m_Attack++;
            if(m_Attack >= 10){
                m_UfoState = 1;
                m_Attack = 0;
            }
        }
        else {
            m_Attack++;
            if (m_Attack >= 10) {
                m_UfoState = 0;
                m_Attack = 0;
                pt.x = m_x;
                pt.y = m_y + m_bmpUfo.getHeight() + 200;
                m_ExplodedPoints.add(pt);
            } else {
                drawLaserBeam(canvas);
            }
        }
    }

}
MapsActivity.javaのソースです。

package net.sadaji.mapbasic;

import androidx.fragment.app.FragmentActivity;

import android.app.AlertDialog;
import android.graphics.Color;
import android.os.Bundle;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptor;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;

import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.LocationSettingsRequest;
import com.google.android.gms.location.LocationSettingsResponse;
import com.google.android.gms.location.SettingsClient;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import androidx.core.app.ActivityCompat;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import com.google.android.gms.maps.model.Marker;

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {

    private GoogleMap mMap;
    private FusedLocationProviderClient mFusedClient;
    private SettingsClient mSettingClient;
    private LocationSettingsRequest mSettingRequst;
    private LocationCallback mLocCallback;
    private LocationRequest mLocReq;
    private Marker mMarker;

    private FrameLayout m_frameLayout;
    private LinearLayout m_linearLayout;
    private Button m_btnCurrentLoc, m_btnSatellite;
    private final int FP = ViewGroup.LayoutParams.MATCH_PARENT;
    private final int WC = ViewGroup.LayoutParams.WRAP_CONTENT;
    private boolean m_bCurrentLocation = true, m_bSatelliteMap = true;
    private UfoView m_UfoView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_maps);
        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);

        mFusedClient = LocationServices.getFusedLocationProviderClient(this);
        mSettingClient = LocationServices.getSettingsClient(this);
        if (ActivityCompat.checkSelfPermission(this,android.Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this,
                    new String[] {android.Manifest.permission.ACCESS_FINE_LOCATION}, 1);
        }

        mLocCallback = new LocationCallback() {
            @Override
            public void onLocationResult(LocationResult locationResult) {
                super.onLocationResult(locationResult);
                if(m_bCurrentLocation == true) {
                    Location loc = locationResult.getLastLocation();
                    LatLng latlng = new LatLng(loc.getLatitude(), loc.getLongitude());
                    mMap.animateCamera(CameraUpdateFactory.newLatLng(latlng));
//                    mMarker.setPosition(latlng);
                }
            }
        };

        mLocReq = new LocationRequest();
        mLocReq.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY).
                setInterval(5000).
                setFastestInterval(1000);

        LocationSettingsRequest.Builder builder =
                new LocationSettingsRequest.Builder();
        mSettingRequst = builder.addLocationRequest(mLocReq).build();

        startFusedLocation();

        //リニアれアウトの配置
        m_frameLayout = new FrameLayout(this);
        m_frameLayout.setLayoutParams(createParam(WC, WC));

        m_linearLayout = new LinearLayout(this);
        m_linearLayout.setOrientation(LinearLayout.HORIZONTAL);
        m_linearLayout.setLayoutParams(createParam(FP, FP));
        m_linearLayout.setBackgroundColor(Color.argb(255, 255, 255, 255));
        //現在位置ボタン
        m_btnCurrentLoc = new Button(this);
        m_btnCurrentLoc.setLayoutParams(createParam(92, 92));
        m_btnCurrentLoc.setBackgroundResource(R.drawable.current_on);
        m_btnCurrentLoc.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                if(m_bCurrentLocation == true){
                    m_bCurrentLocation = false;
//                    mMarker.setVisible(false);
                    m_btnCurrentLoc.setBackgroundResource(R.drawable.current);
                }
                else{
                    m_bCurrentLocation = true;
//                    mMarker.setVisible(true);
                    m_btnCurrentLoc.setBackgroundResource(R.drawable.current_on);
                }
            }
        });
        m_btnCurrentLoc.setPadding(0, 0, 220, 0);
        m_btnCurrentLoc.setEnabled(true);
        m_linearLayout.addView(m_btnCurrentLoc);

        //  ボ衛星写地図ボタン
        m_btnSatellite = new Button(this);
       m_btnSatellite.setLayoutParams(createParam(92, 92));
        m_btnSatellite.setBackgroundResource(R.drawable.satellite);
        m_btnSatellite.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                if(m_bSatelliteMap == true){
                    m_bSatelliteMap = false;
                    m_btnSatellite.setBackgroundResource(R.drawable.satellite);
                    mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
                }
                else{
                    m_bSatelliteMap = true;
                    m_btnSatellite.setBackgroundResource(R.drawable.satellite_on);
                    mMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
                }
            }
        });
        m_btnSatellite.setPadding(0, 0, 0, 0);
        m_btnSatellite.setEnabled(true);
        m_linearLayout.addView(m_btnSatellite);
        m_frameLayout.addView(m_linearLayout);

        addContentView(m_frameLayout, createParam(WC, WC));

        m_UfoView = new UfoView(this);
        addContentView(m_UfoView, createParam(FP, FP));

        //Timer
        final Handler handler = new Handler();
        final Runnable run = new Runnable() {
            @Override
            public void run() {
                // UIスレッド
                handler.postDelayed(this, 200);
                m_UfoView.invalidate();
            }
        };
        handler.post(run);
    }

    private void startFusedLocation() {
        mSettingClient.checkLocationSettings(mSettingRequst)
                .addOnSuccessListener(this,
                        new OnSuccessListener() {
                            @Override
                            public void onSuccess(
                                    LocationSettingsResponse locationSettingsResponse) {
                                        if (ActivityCompat.checkSelfPermission(MapsActivity.this,
                                            android.Manifest.permission.ACCESS_FINE_LOCATION) !=
                                            PackageManager.PERMISSION_GRANTED ) {
                                            return;
                                        }
                                        mFusedClient.requestLocationUpdates(
                                            mLocReq, mLocCallback, Looper.myLooper());
                                    }
                        })
                .addOnFailureListener(this, new OnFailureListener() {
                    @Override
                    public void onFailure(Exception e) {
                        // nothing now
                    }
                });
    }


    /**
     * Manipulates the map once available.
     * This callback is triggered when the map is ready to be used.
     * This is where we can add markers or lines, add listeners or move the camera. In this case,
     * we just add a marker near Sydney, Australia.
     * If Google Play services is not installed on the device, the user will be prompted to install
     * it inside the SupportMapFragment. This method will only be triggered once the user has
     * installed Google Play services and returned to the app.
     */
    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;

        // Add a marker in Tokyo and move the camera
        LatLng tokyo = new LatLng(35.695056, 139.69885);
        mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(tokyo,17f));
//        mMarker = mMap.addMarker(new MarkerOptions().position(tokyo).title("Marker"));
        if(m_bSatelliteMap == true){
            m_btnSatellite.setBackgroundResource(R.drawable.satellite_on);
            mMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
        }
        else{
            m_btnSatellite.setBackgroundResource(R.drawable.satellite);
            mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
        }
        //マーカーの形状変更
//        BitmapDescriptor bd = BitmapDescriptorFactory.fromResource(R.drawable.caddie);
//        mMarker.setIcon(bd);
        mFusedClient.removeLocationUpdates(mLocCallback);
    }

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

    private LinearLayout.LayoutParams createParam(int w, int h){
        return new LinearLayout.LayoutParams(w, h);
    }

}
TimerがタイムアウトするとUfoViewに再描画をさせます。
UfoViewはUFOの出現位置、爆発位置を記録して描画をします。
グーグルマップ上で対戦ゲームなどを作ると面白いかも......