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

В общем в нашем 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>