Выделение области карты на GoogleMap

Наверняка многие сталкивались с такой штукой как рисование на карте, может не в разработке, но в использовании каких либо приложений, их просто куча. Заказчики бывает насмотрятся на такие приложения и потом хотят себе такое же, а ты сидишь такой, не знаешь за что хвататься, а тут еще и эта фича которую ты вообще без понятия как реализовывать. По этому это маленькая заметка будет о том как рисовать радиус на карте, сохранять список выделенных координат и дальше что-то с ними делать, отправлять на сервер, или же расчитывать какие-то параметры, это уже как говорится на выбор разработчика.

image


В общем в нашем MainActivity нам нужно будет подключить карту, и после этого отследить onTouch событие когда пользователь будет рисовать на карте, а потом что бы после того как будет нарисован какой-то круг, вся область за этим кругом была затемнена, а сам круг был обычного цвета карты, что бы был акцент именно на этой области. В общем как на скриншоте. По этому давайте подключим все библиотеки, у нас это ButterKnife и maps api.

app/build.gradle

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:26.1.0'

    implementation 'com.google.android.gms:play-services-location:9.6.1'
    implementation 'com.google.android.gms:play-services-maps:9.6.1'

    compile 'com.jakewharton:butterknife:8.8.1'
    annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
}

ButterKnife используем для удобства подключения вьюх и остальных ресурсов в проект. Карты очевидно используем для отображения карт 🙂

Дальше нам нужно нарисовать нашу разметку, в ней у нас будет карта и фрейм леяут поверх нее.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

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

    <FrameLayout
        android:id="@+id/fram_map"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >

        <Button
            android:id="@+id/drawBtn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="onDrawClick"
            android:text="Free Draw" />
    </FrameLayout>

</FrameLayout>

Как я и говорил, у нас будет карта, она будет у нас основным объектом на экране, поверх у нас FrameLayout в котором кнопка по нажатию на которую мы будем чистить карту и потом рисован на ней.

MainActivity.java

import android.graphics.Point;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;

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.LatLng;
import com.google.android.gms.maps.model.Polygon;
import com.google.android.gms.maps.model.PolygonOptions;
import com.google.android.gms.maps.model.PolylineOptions;

import java.util.ArrayList;
import java.util.List;

import butterknife.BindColor;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;

public class MainActivity extends AppCompatActivity implements OnMapReadyCallback, View.OnTouchListener {

    private GoogleMap googleMap;

    private int source = 0;
    private int destination = 1;

    private boolean isMapMoveable = false;
    private boolean screenLeave = false;

    private ArrayList<LatLng> latLngArrayList = new ArrayList<>();

    @BindView(R.id.fram_map)
    FrameLayout framMap;
    @BindView(R.id.drawBtn)
    Button drawBtn;
    @BindColor(R.color.colorPrimary)
    int colorPrimary;
    @BindColor(R.color.transparentGray)
    int transparentGray;

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

        SupportMapFragment customMapFragment = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map));
        customMapFragment.getMapAsync(this);
    }

    @OnClick(R.id.drawBtn)
    public void onDrawClick() {
        isMapMoveable = true;
        drawBtn.setVisibility(View.GONE);
        latLngArrayList.removeAll(latLngArrayList);
        googleMap.clear();
    }

    public void drawMap() {
        if (latLngArrayList.size() > 1) {
            googleMap.addPolyline(new PolylineOptions().add(
                    latLngArrayList.get(source),
                    latLngArrayList.get(destination))
                    .width(20)
                    .color(colorPrimary)
            );
            source++;
            destination++;
        }
    }

    private List<LatLng> createOuterBounds() {
        final float delta = 0.01f;

        return new ArrayList<LatLng>() {{
            add(new LatLng(90 - delta, -180 + delta));
            add(new LatLng(0, -180 + delta));
            add(new LatLng(-90 + delta, -180 + delta));
            add(new LatLng(-90 + delta, 0));
            add(new LatLng(-90 + delta, 180 - delta));
            add(new LatLng(0, 180 - delta));
            add(new LatLng(90 - delta, 180 - delta));
            add(new LatLng(90 - delta, 0));
            add(new LatLng(90 - delta, -180 + delta));
        }};
    }


    private void drawFinalPolygon() {
        latLngArrayList.add(latLngArrayList.get(0));

        PolygonOptions polygonOptions = new PolygonOptions();
        polygonOptions.fillColor(transparentGray);
        polygonOptions.addAll(createOuterBounds());
        polygonOptions.strokeColor(colorPrimary);
        polygonOptions.strokeWidth(20);
        polygonOptions.addHole(latLngArrayList);

        Polygon polygon = googleMap.addPolygon(polygonOptions);

        for(LatLng latLng : polygon.getPoints()) {
            Log.e("latitude", "" + latLng.latitude);
            Log.e("longitude", "" + latLng.longitude);
        }
    }

    @Override
    public void onMapReady(final GoogleMap googleMap) {
        this.googleMap = googleMap;
        framMap.setOnTouchListener(this);
    }

    @Override
    public boolean onTouch(View view, MotionEvent event) {
        if (isMapMoveable) {
            Point point = new Point(Math.round(event.getX()), Math.round(event.getY()));
            LatLng latLng = googleMap.getProjection().fromScreenLocation(point);
            double latitude = latLng.latitude;
            double longitude = latLng.longitude;

            int eventaction = event.getAction();
            switch (eventaction) {
                case MotionEvent.ACTION_DOWN:
                    screenLeave = false;
                case MotionEvent.ACTION_MOVE:
                    latLngArrayList.add(new LatLng(latitude, longitude));
                    screenLeave = false;
                    drawMap();
                case MotionEvent.ACTION_UP:
                    if (!screenLeave) {
                        screenLeave = true;
                    } else {
                        isMapMoveable = false;
                        source = 0;
                        destination = 1;
                        drawBtn.setVisibility(View.VISIBLE);
                        drawFinalPolygon();
                    }
                    break;
                default:
                    break;
            }

            if (isMapMoveable) {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }
}

Рассмотрим все по порядку. В самом верху класса мы определили все переменные и ресурсы типа карты, GoogleMap и ArrayList в который мы будем сохранять наши координаты.
Дальше в методе onCreate() мы подключили SupportMapFragment и ButterKnife.
Метод onDrawClick() обрабатывает клик по кнопке, чистит карту делает карту рисовабельной и удаляет все из списка.
drawMap() — метод который добавляет полигоны на карту по ходу дела рисования пальцем на карте.
createOuterBounds() — метод который зарисовывает всю область вокруг нарисованного круга.
drawFinalPolygon() — очевидно рисует финальную версию полигона на карте по координатам которые мы сохранили в ArrayList и заканчивает круг в начальную точку с которой он начинался. Ну и дальше по желанию распечатывает в лог координаты которые были использованы на карте.
onMapReady() — метод класса GoogleMap, он вызывается когда карта готова к использованию.
onTouch() — метод в котором происходит вся магия, в начале мы проверяем если карта готова к началу рисования, то есть проверяется isMapMoveable, если да, тогда мы начинаем отслеживать нажатие на экран и отслеживать координаты на карте. По окончанию когда мы отпускаем карту по событию MotionEvent.ACTION_UP мы делаем isMapMoveable = false для того что бы мы снова могли двигать картой. И делаем кнопку снова видимой для очистки экрана и новой возможности для рисования.

Не забываем что нам нужно в AndroidManifest прописать доступ в интернет и мета-данные для работы с картой.

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="project.dajver.com.drawgesturesmap">

    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="@string/google.maps.api.key"/>

    </application>

</manifest>

Исходники:
GitHub && Мой блог