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