Trabajar con la interfaz en el SDK de Google Maps para Android

Este artículo será útil para aquellos que no hayan utilizado previamente el SDK de Google Maps en su trabajo.

Debajo del cortador, se describen los métodos básicos para trabajar con el mapa, como agregar y administrar marcadores, formas de mover la cámara sobre el mapa, controlar el zoom, construir una ruta y geocodificar. Así como limitaciones y formas de sortearlos.

imagen
Fuente

Para escribir el artículo, me inspiré en mi propia experiencia, que obtuve al escribir una solicitud de mensajería utilizando mapas de Google en su trabajo. Por lo tanto, todas las capturas de pantalla y una posible mención de la lógica empresarial ocurrirán en el contexto de la construcción de una interfaz de mensajería.

Desafortunadamente, el SDK de Google Maps para Android no le permite cambiar la posición de los botones de control, los llamados Controles de IU, estos incluyen: IndoorLevelPicker: muestra el plano de planta de los edificios, Brújula: la brújula, el botón Mi ubicación: vaya al mapa en la ubicación actual, Barra de herramientas del mapa: botones para construir la ruta y abrir el mapa, así como ZoomControls: aumente y disminuya la escala del mapa.

Usando la barra de herramientas Mapa y ZoomControls como ejemplo, veamos qué dificultades pueden surgir debido a la incapacidad de cambiar la posición de los controles y cómo solucionarlo.

imagen
Problemas al visualizar controles de IU desde el SDK (resaltado en naranja) y sus contrapartes personalizadas (resaltado en verde)

En este caso, tenemos en la esquina inferior derecha un botón (botón de acción flotante) para ir a la lista de direcciones de pedidos de entrega, en la imagen de la izquierda puede ver que ZoomControls estaba debajo y es prácticamente inaccesible para hacer clic. En la imagen de la derecha, cuando hace clic en el marcador, aparecen botones de la barra de herramientas Mapa, también aparecen debajo del botón para ir a la lista de pedidos.

Solución

Lo primero que debemos hacer es ocultar la visualización de los botones originales. Puede hacer esto anulando el método onMapReady, se llama cuando la tarjeta está lista para usar.

No mostrar botones Control de zoom y no mostrar botones construyen una ruta desde el SDK
private GoogleMap mMap; private UiSettings uiSettings; @Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; uiSettings = mMap.getUiSettings(); //   Zoom uiSettings.setZoomControlsEnabled(false); //         SDK uiSettings.setMapToolbarEnabled(false); } 


Agregamos los botones necesarios al diseño, donde deben estar de acuerdo con nuestro diseño:

imagen
Ubicación de botones de control de mapas personalizados

Luego, en el método onCreateView, especifique las acciones que deberían ocurrir cuando se hace clic en nuestros botones:

Controladores para los botones para aumentar y disminuir la escala, así como para construir una ruta
 private ImageButton imageButtonZoomIn; private ImageButton imageButtonZoomOut; private ImageButton imageButtonRoute; private GoogleMap mMap; @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { //  imageButtonZoomIn = view.findViewById(R.id.imageButtonZoomIn); imageButtonZoomIn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mMap.animateCamera(CameraUpdateFactory.zoomIn()); } }); //  imageButtonZoomOut = view.findViewById(R.id.imageButtonZoomOut); imageButtonZoomOut.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mMap.animateCamera(CameraUpdateFactory.zoomOut()); } }); //      imageButtonRoute = view.findViewById(R.id.imageButtonRoute); imageButtonRoute.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String latitude = String.valueOf(activMarker.getPosition().latitude); String longitude = String.valueOf(activMarker.getPosition().longitude); Uri gmmIntentUri = Uri.parse("google.navigation:q=" + latitude + "," + longitude); Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri); mapIntent.setPackage("com.google.android.apps.maps"); try{ if (mapIntent.resolveActivity(Objects.requireNonNull(getActivity()).getPackageManager()) != null) { startActivity(mapIntent); } }catch (NullPointerException e){ Log.e(TAG, "onClick: NullPointerException: Couldn't open map." + e.getMessage() ); Toast.makeText(getActivity(), "Couldn't open map", Toast.LENGTH_SHORT).show(); } } }); } 


La peculiaridad del método animateCamera es que la escala cambia suavemente y no al instante, y si, por ejemplo, necesita desactivar la animación de un botón de zoom específico cuando se alcanza la escala máxima o mínima, debe redefinir el método onCameraIdle , que se llama cuando cambia la escala del mapa.

Activando y desactivando los botones de zoom
  @Override public void onCameraIdle() { if (mMap.getCameraPosition().zoom == mMap.getMinZoomLevel()){ //  ,     imageButtonZoomOut.setEnabled(false); imageButtonZoomIn.setEnabled(true); }else if (mMap.getCameraPosition().zoom == mMap.getMaxZoomLevel()){ //  ,     imageButtonZoomOut.setEnabled(true); imageButtonZoomIn.setEnabled(false); }else { //       imageButtonZoomOut.setEnabled(true); imageButtonZoomIn.setEnabled(true); } } 


Para realizar cualquier acción con un marcador (excepto arrastrar y soltar), por ejemplo, cree un nuevo pedido, elimine un marcador colocado al azar, vaya a un pedido existente o llame al teléfono como se indica en el pedido, agregue los botones de control apropiados al diseño y registre sus controladores.

imagen
Botones de control de marcador

Procesar un clic en un mapa para agregar un marcador y mostrar botones de control personalizados
 private GoogleMap mMap; private ImageButton imageButtonRoute; @Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; mMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() { @Override public void onMapClick(LatLng latLng) { imageButtonRoute.setVisibility(View.GONE); if (myMarker !=null){ //   myMarker.remove(); } //    myMarker = mMap.addMarker(new MarkerOptions() .position(latLng) //   .title(Objects.requireNonNull(getContext()).getString(R.string.title_on_marker_to_new_order)) // true ,         .draggable(true)); myMarker.setTag(null); //    imageButtonAddMarker.setVisibility(View.VISIBLE); imageButtonRemoveMarker.setVisibility(View.VISIBLE); } }); } 


Indicamos qué queremos hacer con el marcador cuando hacemos clic en el botón Agregar orden
 private ImageButton imageButtonAddMarker; private Marker myMarker; @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { imageButtonAddMarker = view.findViewById(R.id.imageButtonAddMarker); imageButtonAddMarker.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (myMarker !=null && myMarker.getTag()==null) { //    imageButtonAddMarker.setVisibility(View.GONE); imageButtonRemoveMarker.setVisibility(View.GONE); //  ,          listener.openOrderContentsFragmentFromMap(null, myMarker); //   ,     myMarker.remove(); } } }); } 


Otra característica es que no hay ningún botón en el SDK para eliminar el marcador en la tarjeta. Para hacer esto, también hacemos nuestro propio botón:

Especifique qué queremos hacer con el marcador cuando hacemos clic en el botón Eliminar marcador
 private ImageButton imageButtonRemoveMarker; private Marker myMarker; @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { imageButtonRemoveMarker = view.findViewById(R.id.imageButtonRemoveMarker); imageButtonRemoveMarker.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (myMarker !=null && myMarker.getTag()==null){ //   imageButtonAddMarker.setVisibility(View.GONE); imageButtonRemoveMarker.setVisibility(View.GONE); //    myMarker.remove(); } } }); } 


Al hacer clic en el marcador, se abre su título, al hacer clic en el cual también se puede usar para realizar cualquier acción, cuando hago clic en el título de un nuevo marcador, creo un nuevo pedido para la entrega al servicio de mensajería, y en el marcador de un pedido existente, abro información de entrega detallada, incluida una lista bienes.

Establezca la acción al hacer clic en la ventana de información del marcador
 private GoogleMap mMap; @Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; mMap.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() { @Override public void onInfoWindowClick(Marker marker) { if (marker.getTag()==null) { //    : //      listener.openOrderContentsFragmentFromMap(null, marker); if (myMarker != null) { //   ,     myMarker.remove(); } } else { //     : //         listener.openOrderContentsFragment((Long) marker.getTag()); } } }); } 


El proceso de enviar varios marcadores (leer la lista de pedidos) a la tarjeta no es diferente en principio de la salida de un marcador. Un marcador consta de coordenadas (posición), un título (título), un pequeño texto debajo de un título (fragmento) y una etiqueta (setTag); se puede utilizar para identificar muchos marcadores en el mapa.

imagen
Varios marcadores de mapa

Dibujar múltiples marcadores con coordenadas dadas
 public void drawListMarker(List<InfoMarker> latLngList) { if (latLngList == null || latLngList.size() == 0) { return; } //    mMap.clear(); LatLngBounds.Builder builder = new LatLngBounds.Builder(); boolean fiarstGreean = true; int count = 1; for (InfoMarker latLng : latLngList) { BitmapDescriptor icon; if (fiarstGreean){ //      //..           icon = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN); fiarstGreean = false; } else { icon = latLng.getIcon(); } //      mMap.addMarker(new MarkerOptions().position(latLng.getLatLng()).title(String.valueOf(count)).snippet(latLng.getTitle()).icon(icon)).setTag(latLng.getIdOrder()); builder.include(latLng.getLatLng()); count++; } //       CameraUpdate cameraUpdate; if (loaded) { cameraUpdate = CameraUpdateFactory .newLatLngBounds(builder.build(), 100); } else { cameraUpdate = CameraUpdateFactory.newLatLng(builder.build().getCenter()); } mMap.moveCamera(cameraUpdate); mMap.animateCamera(CameraUpdateFactory.zoomIn()); mMap.animateCamera(CameraUpdateFactory.zoomTo(10), 1000, null); } 


Algunas palabras sobre geocodificador

Se utiliza un geocodificador para obtener una dirección basada en coordenadas. Al poner un marcador en el mapa y hacer clic en el botón Agregar orden, obtenemos las coordenadas geográficas del punto deseado, es decir latitud y longitud Pero para la conveniencia del usuario, será bueno mostrar la dirección en forma legible para humanos, es decir, por ejemplo, país, ciudad, calle, casa.

El SDK de Google Maps contiene la clase Geocoder , al llamar a su método getFromLocation puede obtener una matriz de direcciones en las coordenadas especificadas.

Para no bloquear el subproceso de la interfaz de usuario durante mucho tiempo, especialmente si Internet es lento o inaccesible, mediante llamadas, utilizaremos RxJava:

imagen
La dirección del punto resultante en el mapa basada en coordenadas geográficas

Usando Java RX para acceder al geocodificador de Google
 LatLng position = myMarker.getPosition(); Location location = new Location("new"); location.setLatitude(position.latitude); location.setLongitude(position.longitude); LocationRepostiory locationRepostiory = new LocationRepostiory(context, location); locationRepostiory.getLastLocation(). observeOn(SchedulerProvider.getInstance().ui()). subscribeOn(SchedulerProvider.getInstance().computation()). subscribe(locationString -> { if(editTextAddress.length()==0){ //       ,      editTextAddress.setText(locationString); } }, throwable -> { }); 


El texto de la clase LocationRepostiory en el que ocurre la geocodificación inversa
 public class LocationRepostiory { private Context context; private Location location; public LocationRepostiory(Context context, Location location) { this.context = context; this.location = location; } public Single<String> getLastLocation() { return Single.create(this::subscribeOnLocation); } private void subscribeOnLocation(SingleEmitter<String> e) { Geocoder geocoder = new Geocoder(context, Locale.getDefault()); String errorMessage = ""; List<Address> addresses = null; try { //     addresses = geocoder.getFromLocation( location.getLatitude(), location.getLongitude(), // In this sample, get just a single address. 1); } catch (IOException ioException) { //,      I/O. errorMessage = context.getString(R.string.service_not_available); Log.e(TAG, errorMessage, ioException); } catch (IllegalArgumentException illegalArgumentException) { // ,       errorMessage = context.getString(R.string.invalid_lat_long_used); Log.e(TAG, errorMessage + ". " + "Latitude = " + location.getLatitude() + ", Longitude = " + location.getLongitude(), illegalArgumentException); } //  ,     if (addresses == null || addresses.size() == 0) { if (errorMessage.isEmpty()) { errorMessage = context.getString(R.string.no_address_found); Log.e(TAG, errorMessage); } } else { Address address = addresses.get(0); ArrayList<String> addressFragments = new ArrayList<String>(); //      . for (int i = 0; i <= address.getMaxAddressLineIndex(); i++) { e.onSuccess(address.getAddressLine(i)); } } } } 

Source: https://habr.com/ru/post/484100/


All Articles