API de Android de Google Maps v2 - Ventana de información interactiva (como en los mapas de Google Android originales)

191

Estoy tratando de personalizar InfoWindowdespués de hacer clic en un marcador con la nueva API de Google Maps v2. Quiero que se vea como en la aplicación de mapas original de Google. Me gusta esto:

Imagen de ejemplo

Cuando tengo ImageButtonadentro, no funciona: todo InfoWindowse elige y no solo el ImageButton. Leí que es porque no hay un Viewsí mismo, sino una instantánea, por lo que los elementos individuales no se pueden distinguir entre sí.

EDITAR: En la documentación (gracias a Disco S2 ):

Como se mencionó en la sección anterior sobre ventanas de información, una ventana de información no es una Vista en vivo, sino que la vista se representa como una imagen en el mapa. Como resultado, los escuchas que establezca en la vista no se tienen en cuenta y no puede distinguir entre los eventos de clic en varias partes de la vista. Se recomienda no colocar componentes interactivos, como botones, casillas de verificación o entradas de texto, dentro de su ventana de información personalizada.

Pero si Google lo usa, debe haber alguna forma de hacerlo. ¿Alguien tiene alguna idea?

usuario1943012
fuente
"no funciona" no es una descripción especialmente útil de sus síntomas. Aquí hay un proyecto de muestra que muestra tener una ventana de información personalizada con una imagen: github.com/commonsguy/cw-omnibus/tree/master/MapsV2/Popups
CommonsWare
8
@CommonsWare Escribí una razón de eso. De la documentación original Nota: La ventana de información que se dibuja no es una vista en vivo. La vista se representa como una imagen (usando View.draw(Canvas)) ... Esto significa que cualquier cambio posterior a la vista no se reflejará en la ventana de información del mapa. Para actualizar la ventana de información más adelante Además, la ventana de información no respetará ninguna de las interactividades típicas de una vista normal, como eventos táctiles o gestuales. Sin embargo, puede escuchar un evento de clic genérico en toda la ventana de información como se describe en la sección a continuación. ... en su ejemplo es solo una vista de texto
user1943012
Hola, ¿cómo conseguiste que el botón de mi ubicación aparezca debajo de la barra de acción en modo de pantalla completa? ¡Gracias!
tkblackbelt
45
No creo que esta pregunta deba cerrarse. Es una pregunta legítima a un problema que también estoy enfrentando.
marienke
12
Puedo ver esto tan a menudo que muchas de las preguntas más útiles con respuesta se han cerrado. Es posible que una pregunta no siempre se ajuste a los requisitos exactos de una pregunta adecuada, pero si tenemos un PROBLEMA bien explicado y una RESPUESTA bien explicada, entonces no hay razón para cerrar la pregunta. La gente usa Stackoverflow el intercambio de conocimientos y el cierre de un hilo pregunta que es tan activa y tan productiva como no parece "fuera de tema" como una idea buena para mi ..
John

Respuestas:

333

Estaba buscando una solución a este problema sin suerte, así que tuve que rodar la mía que me gustaría compartir aquí con ustedes. (Disculpe mi mal inglés) (Es un poco loco responder a otro checo en inglés :-))

Lo primero que intenté fue usar un buen viejo PopupWindow. Es bastante fácil: solo hay que escuchar OnMarkerClickListenery luego mostrar una costumbre PopupWindowsobre el marcador. Algunos otros tipos aquí en StackOverflow sugirieron esta solución y en realidad se ve bastante bien a primera vista. Pero el problema con esta solución aparece cuando comienzas a mover el mapa. Tienes que moverlo de PopupWindowalguna manera, lo cual es posible (escuchando algunos eventos onTouch), pero en mi humilde opinión, no puedes hacer que se vea lo suficientemente bien, especialmente en algunos dispositivos lentos. Si lo hace de la manera simple, "salta" de un lugar a otro. También podría usar algunas animaciones para pulir esos saltos, pero de esta manera PopupWindowsiempre estará "un paso atrás" donde debería estar en el mapa, lo que simplemente no me gusta.

En este punto, estaba pensando en alguna otra solución. Me di cuenta de que en realidad no necesito tanta libertad, para mostrar mis vistas personalizadas con todas las posibilidades que conlleva (como barras de progreso animadas, etc.). Creo que hay una buena razón por la cual incluso los ingenieros de Google no lo hacen de esta manera en la aplicación Google Maps. Todo lo que necesito es un botón o dos en la ventana de información que mostrará un estado presionado y desencadenará algunas acciones al hacer clic. Entonces se me ocurrió otra solución que se divide en dos partes:

Primera parte:
la primera parte es poder capturar los clics en los botones para activar alguna acción. Mi idea es la siguiente:

  1. Mantenga una referencia a la infoWindow personalizada creada en el InfoWindowAdapter.
  2. Envuelva el MapFragment(o MapView) dentro de un ViewGroup personalizado (el mío se llama MapWrapperLayout)
  3. Anule el MapWrapperLayout'dispatchTouchEvent' y (si la ventana de información se muestra actualmente) primero enrute los eventos de movimiento a la ventana de información creada anteriormente. Si no consume los MotionEvents (como porque no hizo clic en ningún área en la que se pueda hacer clic dentro de InfoWindow, etc.), entonces (y solo entonces) deje que los eventos pasen a la superclase de MapWrapperLayout para que finalmente se entregue al mapa.

Aquí está el código fuente de MapWrapperLayout:

package com.circlegate.tt.cg.an.lib.map;

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

import android.content.Context;
import android.graphics.Point;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.RelativeLayout;

public class MapWrapperLayout extends RelativeLayout {
    /**
     * Reference to a GoogleMap object 
     */
    private GoogleMap map;

    /**
     * Vertical offset in pixels between the bottom edge of our InfoWindow 
     * and the marker position (by default it's bottom edge too).
     * It's a good idea to use custom markers and also the InfoWindow frame, 
     * because we probably can't rely on the sizes of the default marker and frame. 
     */
    private int bottomOffsetPixels;

    /**
     * A currently selected marker 
     */
    private Marker marker;

    /**
     * Our custom view which is returned from either the InfoWindowAdapter.getInfoContents 
     * or InfoWindowAdapter.getInfoWindow
     */
    private View infoWindow;    

    public MapWrapperLayout(Context context) {
        super(context);
    }

    public MapWrapperLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MapWrapperLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    /**
     * Must be called before we can route the touch events
     */
    public void init(GoogleMap map, int bottomOffsetPixels) {
        this.map = map;
        this.bottomOffsetPixels = bottomOffsetPixels;
    }

    /**
     * Best to be called from either the InfoWindowAdapter.getInfoContents 
     * or InfoWindowAdapter.getInfoWindow. 
     */
    public void setMarkerWithInfoWindow(Marker marker, View infoWindow) {
        this.marker = marker;
        this.infoWindow = infoWindow;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        boolean ret = false;
        // Make sure that the infoWindow is shown and we have all the needed references
        if (marker != null && marker.isInfoWindowShown() && map != null && infoWindow != null) {
            // Get a marker position on the screen
            Point point = map.getProjection().toScreenLocation(marker.getPosition());

            // Make a copy of the MotionEvent and adjust it's location
            // so it is relative to the infoWindow left top corner
            MotionEvent copyEv = MotionEvent.obtain(ev);
            copyEv.offsetLocation(
                -point.x + (infoWindow.getWidth() / 2), 
                -point.y + infoWindow.getHeight() + bottomOffsetPixels);

            // Dispatch the adjusted MotionEvent to the infoWindow
            ret = infoWindow.dispatchTouchEvent(copyEv);
        }
        // If the infoWindow consumed the touch event, then just return true.
        // Otherwise pass this event to the super class and return it's result
        return ret || super.dispatchTouchEvent(ev);
    }
}

Todo esto hará que las vistas dentro de InfoView estén "en vivo" nuevamente: los OnClickListeners comenzarán a activarse, etc.

Segunda parte: el problema restante es que, obviamente, no puede ver ningún cambio en la interfaz de usuario de su InfoWindow en la pantalla. Para hacerlo, debe llamar manualmente a Marker.showInfoWindow. Ahora, si realiza algún cambio permanente en su InfoWindow (como cambiar la etiqueta de su botón a otra cosa), esto es lo suficientemente bueno.

Pero mostrar un estado de botón presionado o algo de esa naturaleza es más complicado. El primer problema es que (al menos) no pude hacer que la ventana de información muestre el estado presionado del botón normal. Incluso si presioné el botón durante mucho tiempo, simplemente no se presionó en la pantalla. Creo que esto es algo que es manejado por el marco del mapa en sí mismo que probablemente se asegura de no mostrar ningún estado transitorio en las ventanas de información. Pero podría estar equivocado, no intenté descubrir esto.

Lo que hice fue otro truco desagradable: adjunté un OnTouchListenerbotón al botón y cambié manualmente su fondo cuando se presionó el botón o se soltó a dos elementos dibujables personalizados, uno con un botón en un estado normal y el otro en un estado presionado. Esto no es muy agradable, pero funciona :). Ahora pude ver cómo el botón cambiaba de estado normal a presionado en la pantalla.

Todavía hay una última falla: si hace clic en el botón demasiado rápido, no muestra el estado presionado, simplemente permanece en su estado normal (aunque el clic en sí mismo se activa para que el botón "funcione"). Al menos así es como aparece en mi Galaxy Nexus. Así que lo último que hice es retrasar un poco el botón cuando está presionado. Esto también es bastante feo y no estoy seguro de cómo funcionaría en algunos dispositivos más antiguos y lentos, pero sospecho que incluso el marco del mapa hace algo como esto. Puede probarlo usted mismo: cuando hace clic en toda la ventana de información, permanece presionada un poco más, luego los botones normales lo hacen (nuevamente, al menos en mi teléfono). Y así es como funciona incluso en la aplicación original de Google Maps.

De todos modos, escribí una clase personalizada que maneja los cambios de estado de los botones y todas las otras cosas que mencioné, así que aquí está el código:

package com.circlegate.tt.cg.an.lib.map;

import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;

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

public abstract class OnInfoWindowElemTouchListener implements OnTouchListener {
    private final View view;
    private final Drawable bgDrawableNormal;
    private final Drawable bgDrawablePressed;
    private final Handler handler = new Handler();

    private Marker marker;
    private boolean pressed = false;

    public OnInfoWindowElemTouchListener(View view, Drawable bgDrawableNormal, Drawable bgDrawablePressed) {
        this.view = view;
        this.bgDrawableNormal = bgDrawableNormal;
        this.bgDrawablePressed = bgDrawablePressed;
    }

    public void setMarker(Marker marker) {
        this.marker = marker;
    }

    @Override
    public boolean onTouch(View vv, MotionEvent event) {
        if (0 <= event.getX() && event.getX() <= view.getWidth() &&
            0 <= event.getY() && event.getY() <= view.getHeight())
        {
            switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN: startPress(); break;

            // We need to delay releasing of the view a little so it shows the pressed state on the screen
            case MotionEvent.ACTION_UP: handler.postDelayed(confirmClickRunnable, 150); break;

            case MotionEvent.ACTION_CANCEL: endPress(); break;
            default: break;
            }
        }
        else {
            // If the touch goes outside of the view's area
            // (like when moving finger out of the pressed button)
            // just release the press
            endPress();
        }
        return false;
    }

    private void startPress() {
        if (!pressed) {
            pressed = true;
            handler.removeCallbacks(confirmClickRunnable);
            view.setBackground(bgDrawablePressed);
            if (marker != null) 
                marker.showInfoWindow();
        }
    }

    private boolean endPress() {
        if (pressed) {
            this.pressed = false;
            handler.removeCallbacks(confirmClickRunnable);
            view.setBackground(bgDrawableNormal);
            if (marker != null) 
                marker.showInfoWindow();
            return true;
        }
        else
            return false;
    }

    private final Runnable confirmClickRunnable = new Runnable() {
        public void run() {
            if (endPress()) {
                onClickConfirmed(view, marker);
            }
        }
    };

    /**
     * This is called after a successful click 
     */
    protected abstract void onClickConfirmed(View v, Marker marker);
}

Aquí hay un archivo de diseño personalizado de InfoWindow que utilicé:

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

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_marginRight="10dp" >

        <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:text="Title" />

        <TextView
            android:id="@+id/snippet"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="snippet" />

    </LinearLayout>

    <Button
        android:id="@+id/button" 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button" />

</LinearLayout>

Probar el archivo de diseño de actividad ( MapFragmentestar dentro del MapWrapperLayout):

<com.circlegate.tt.cg.an.lib.map.MapWrapperLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/map_relative_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

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

</com.circlegate.tt.cg.an.lib.map.MapWrapperLayout>

Y finalmente, el código fuente de una actividad de prueba, que une todo esto:

package com.circlegate.testapp;

import com.circlegate.tt.cg.an.lib.map.MapWrapperLayout;
import com.circlegate.tt.cg.an.lib.map.OnInfoWindowElemTouchListener;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.InfoWindowAdapter;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;

import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {    
    private ViewGroup infoWindow;
    private TextView infoTitle;
    private TextView infoSnippet;
    private Button infoButton;
    private OnInfoWindowElemTouchListener infoButtonListener;

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

        final MapFragment mapFragment = (MapFragment)getFragmentManager().findFragmentById(R.id.map);
        final MapWrapperLayout mapWrapperLayout = (MapWrapperLayout)findViewById(R.id.map_relative_layout);
        final GoogleMap map = mapFragment.getMap();

        // MapWrapperLayout initialization
        // 39 - default marker height
        // 20 - offset between the default InfoWindow bottom edge and it's content bottom edge 
        mapWrapperLayout.init(map, getPixelsFromDp(this, 39 + 20)); 

        // We want to reuse the info window for all the markers, 
        // so let's create only one class member instance
        this.infoWindow = (ViewGroup)getLayoutInflater().inflate(R.layout.info_window, null);
        this.infoTitle = (TextView)infoWindow.findViewById(R.id.title);
        this.infoSnippet = (TextView)infoWindow.findViewById(R.id.snippet);
        this.infoButton = (Button)infoWindow.findViewById(R.id.button);

        // Setting custom OnTouchListener which deals with the pressed state
        // so it shows up 
        this.infoButtonListener = new OnInfoWindowElemTouchListener(infoButton,
                getResources().getDrawable(R.drawable.btn_default_normal_holo_light),
                getResources().getDrawable(R.drawable.btn_default_pressed_holo_light)) 
        {
            @Override
            protected void onClickConfirmed(View v, Marker marker) {
                // Here we can perform some action triggered after clicking the button
                Toast.makeText(MainActivity.this, marker.getTitle() + "'s button clicked!", Toast.LENGTH_SHORT).show();
            }
        }; 
        this.infoButton.setOnTouchListener(infoButtonListener);


        map.setInfoWindowAdapter(new InfoWindowAdapter() {
            @Override
            public View getInfoWindow(Marker marker) {
                return null;
            }

            @Override
            public View getInfoContents(Marker marker) {
                // Setting up the infoWindow with current's marker info
                infoTitle.setText(marker.getTitle());
                infoSnippet.setText(marker.getSnippet());
                infoButtonListener.setMarker(marker);

                // We must call this to set the current marker and infoWindow references
                // to the MapWrapperLayout
                mapWrapperLayout.setMarkerWithInfoWindow(marker, infoWindow);
                return infoWindow;
            }
        });

        // Let's add a couple of markers
        map.addMarker(new MarkerOptions()
            .title("Prague")
            .snippet("Czech Republic")
            .position(new LatLng(50.08, 14.43)));

        map.addMarker(new MarkerOptions()
            .title("Paris")
            .snippet("France")
            .position(new LatLng(48.86,2.33)));

        map.addMarker(new MarkerOptions()
            .title("London")
            .snippet("United Kingdom")
            .position(new LatLng(51.51,-0.1)));
    }

    public static int getPixelsFromDp(Context context, float dp) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int)(dp * scale + 0.5f);
    }
}

Eso es. Hasta ahora solo probé esto en mi Galaxy Nexus (4.2.1) y Nexus 7 (también 4.2.1), lo intentaré en algún teléfono Gingerbread cuando tenga la oportunidad. Una limitación que encontré hasta ahora es que no puedes arrastrar el mapa desde donde está tu botón en la pantalla y mover el mapa. Probablemente podría superarse de alguna manera, pero por ahora, puedo vivir con eso.

Sé que este es un truco feo, pero simplemente no encontré nada mejor y necesito tanto este patrón de diseño que realmente sería una razón para volver al marco del mapa v1 (que por cierto, realmente me gustaría evitar para una nueva aplicación con fragmentos, etc.). Simplemente no entiendo por qué Google no ofrece a los desarrolladores alguna forma oficial de tener un botón en InfoWindows. Es un patrón de diseño tan común, además, este patrón se usa incluso en la aplicación oficial de Google Maps :). Entiendo las razones por las que no pueden simplemente hacer que sus vistas "vivan" en InfoWindows; esto probablemente mataría el rendimiento al mover y desplazar el mapa. Pero debería haber alguna forma de lograr este efecto sin usar vistas.

elegido007
fuente
66
Solución interesante. Quiero probar esto. ¿Cómo estuvo la actuación?
Patrick
10
necesita reemplazar view.setBackground(bgDrawablePressed);con if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { view.setBackgroundDrawable(bgDrawablePressed); }else{ view.setBackground(bgDrawablePressed); }para trabajar en Android Gingerbread, Nota: hay una ocurrencia más view.setBackgrounden el código.
Balaji
44
KK_07k11A0585: Parece que su implementación de InfoWindowAdapter probablemente sea incorrecta. Hay dos métodos para anular: getInfoWindow () y getInfoContents (). El marco primero llama al método getInfoWindow () y (solo) si devuelve nulo, llama a getInfoContents (). Si devuelve alguna Vista de getInfoWindow, el marco lo colocará dentro del marco de la ventana de información predeterminada, que en su caso no desea. Así que solo devuelve null desde getInfoWindow y devuelve View desde getInfoContent y entonces debería estar bien.
elegido007
18
Si alguien está interesado, estoy usando esta solución en mi propia aplicación: CG Transit (puede encontrarla en Google Play). Después de dos meses, ha sido utilizado por varias decenas de miles de usuarios y nadie se ha quejado aún.
elegido007
2
¿Cómo se determina el desplazamiento inferior si no está utilizando una ventana de información estándar?
Rarw
12

Veo que esta pregunta ya es vieja pero aún así ...

Hicimos una biblioteca de sipmle en nuestra empresa para lograr lo que se desea: una ventana de información interactiva con vistas y todo. Puedes verlo en github .

Espero que ayude :)

dephinera
fuente
¿Es posible usarlo con clusters?
gcolucci
9

Aquí está mi opinión sobre el problema. Creo una AbsoluteLayoutsuperposición que contiene la ventana de información (una vista normal con cada bit de interactividad y capacidades de dibujo). Luego comienzo, Handlerque sincroniza la posición de la ventana de información con la posición del punto en el mapa cada 16 ms. Suena loco, pero en realidad funciona.

Video de demostración: https://www.youtube.com/watch?v=bT9RpH4p9mU (tenga en cuenta que el rendimiento disminuye debido a que el emulador y la grabación de video se ejecutan simultáneamente).

Código de la demostración: https://github.com/deville/info-window-demo

Un artículo que proporciona detalles (en ruso): http://habrahabr.ru/post/213415/

Andrii Chernenko
fuente
Esto ralentizará / retrasará el desplazamiento del mapa
Shane Ekanayake
2

Para aquellos que no pudieron obtener choose007'srespuesta en funcionamiento

Si clickListenerno funciona correctamente en todo momento en la chose007'ssolución, intente implementar en View.onTouchListenerlugar de clickListener. Manejar evento táctil usando cualquiera de las acciones ACTION_UPo ACTION_DOWN. Por alguna razón, los mapas infoWindowcausan un comportamiento extraño cuando se envía a clickListeners.

infoWindow.findViewById(R.id.my_view).setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
          int action = MotionEventCompat.getActionMasked(event);
          switch (action){
                case MotionEvent.ACTION_UP:
                    Log.d(TAG,"a view in info window clicked" );
                    break;
                }
                return true;
          }

Editar: así es como lo hice paso a paso

Primero infle su propia ventana de información (variable global) en algún lugar de su actividad / fragmento. La mía está dentro del fragmento. También asegúrese de que la vista raíz en el diseño de su ventana de información es lineal (por alguna razón, la distribución relativa ocupaba todo el ancho de la pantalla en la ventana de información)

infoWindow = (ViewGroup) getActivity().getLayoutInflater().inflate(R.layout.info_window, null);
/* Other global variables used in below code*/
private HashMap<Marker,YourData> mMarkerYourDataHashMap = new HashMap<>();
private GoogleMap mMap;
private MapWrapperLayout mapWrapperLayout;

Luego, en onMapReady callback de la API de Android de Google Maps (siga esto si no sabe qué es onMapReady Maps> Documentation - Getting Started )

   @Override
    public void onMapReady(GoogleMap googleMap) {
       /*mMap is global GoogleMap variable in activity/fragment*/
        mMap = googleMap;
       /*Some function to set map UI settings*/ 
        setYourMapSettings();

MapWrapperLayoutinicialización http://stackoverflow.com/questions/14123243/google-maps-android-api-v2- interactive-infowindow-like-in-original-android-go / 15040761 # 15040761 39 - altura predeterminada del marcador 20 - desplazamiento entre el borde inferior de InfoWindow predeterminado y su borde inferior de contenido * /

        mapWrapperLayout.init(mMap, Utils.getPixelsFromDp(mContext, 39 + 20));

        /*handle marker clicks separately - not necessary*/
       mMap.setOnMarkerClickListener(this);

       mMap.setInfoWindowAdapter(new GoogleMap.InfoWindowAdapter() {
                @Override
                public View getInfoWindow(Marker marker) {
                    return null;
                }

            @Override
            public View getInfoContents(Marker marker) {
                YourData data = mMarkerYourDataHashMap.get(marker);
                setInfoWindow(marker,data);
                mapWrapperLayout.setMarkerWithInfoWindow(marker, infoWindow);
                return infoWindow;
            }
        });
    }

Método SetInfoWindow

private void setInfoWindow (final Marker marker, YourData data)
            throws NullPointerException{
        if (data.getVehicleNumber()!=null) {
            ((TextView) infoWindow.findViewById(R.id.VehicelNo))
                    .setText(data.getDeviceId().toString());
        }
        if (data.getSpeed()!=null) {
            ((TextView) infoWindow.findViewById(R.id.txtSpeed))
                    .setText(data.getSpeed());
        }

        //handle dispatched touch event for view click
        infoWindow.findViewById(R.id.any_view).setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                int action = MotionEventCompat.getActionMasked(event);
                switch (action) {
                    case MotionEvent.ACTION_UP:
                        Log.d(TAG,"any_view clicked" );
                        break;
                }
                return true;
            }
        });

Manejar el marcador de clic por separado

    @Override
    public boolean onMarkerClick(Marker marker) {
        Log.d(TAG,"on Marker Click called");
        marker.showInfoWindow();
        CameraPosition cameraPosition = new CameraPosition.Builder()
                .target(marker.getPosition())      // Sets the center of the map to Mountain View
                .zoom(10)
                .build();
        mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition),1000,null);
        return true;
    }
Jangid Manish
fuente
¿me puede mostrar su código? Pude registrar eventos táctiles con éxito pero no hacer clic en eventos, así que decidí implementarlo. Además, debe implementar la solución de choose007 correctamente.
Manish Jangid
Compartí mi implementación completa. Avíseme si tiene alguna pregunta
Manish Jangid
0

Solo una especulación, no tengo suficiente experiencia para probarlo ...) -:

Como GoogleMap es un fragmento, debería ser posible capturar el marcador en el evento Click y mostrar una vista de fragmento personalizada . Un fragmento del mapa seguirá siendo visible en el fondo. ¿Alguien lo intentó? ¿Alguna razón por la que no podría funcionar?

La desventaja es que el fragmento de mapa se congelaría en el fondo, hasta que un fragmento de información personalizado le devuelva el control.

Wooff
fuente
55
No creo que sea interesante publicar una especulación como respuesta. Tal vez debería ser un comentario sobre la pregunta original. Solo mis pensamientos.
Johan Karlsson
1
Esta biblioteca github.com/Appolica/InteractiveInfoWindowAndroid hace algo similar
Deyan Genovski el
-2

He creado un proyecto de estudio de Android de muestra para esta pregunta.

capturas de pantalla de salida: -

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

Descargar el código fuente completo del proyecto Haga clic aquí

Tenga en cuenta: debe agregar su clave API en Androidmanifest.xml

Premkumar Manipillai
fuente
55
Es mejor compartir su código a través de Github / gist en lugar del archivo zip. Entonces podemos verificar directamente allí.
Noundla Sandeep
encontré un pequeño error, cuando presionas algún botón, se hace clic en todos los botones.
Morozov
3
@Noundla y otros: no te molestes en descargar su código comprimido. Es una copia exacta de la respuesta aceptada para esta pregunta.
Ganesh Mohan
1
@ ganesh2shiv tiene razón, el código comprimido es inútil. No debe adjuntarse copia del código pegado
Onkar Nene
-7

Es realmente simple

googleMap.setInfoWindowAdapter(new InfoWindowAdapter() {

            // Use default InfoWindow frame
            @Override
            public View getInfoWindow(Marker marker) {              
                return null;
            }           

            // Defines the contents of the InfoWindow
            @Override
            public View getInfoContents(Marker marker) {

                // Getting view from the layout file info_window_layout
                View v = getLayoutInflater().inflate(R.layout.info_window_layout, null);

                // Getting reference to the TextView to set title
                TextView note = (TextView) v.findViewById(R.id.note);

                note.setText(marker.getTitle() );

                // Returning the view containing InfoWindow contents
                return v;

            }

        });

Simplemente agregue el código anterior en su clase donde está usando GoogleMap. R.layout.info_window_layout es nuestro diseño personalizado que muestra la vista en lugar de la ventana de información. Acabo de agregar la vista de texto aquí. Puede agregar una vista adicional aquí para que se parezca al complemento de muestra. Mi info_window_layout fue

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

        <TextView 
        android:id="@+id/note"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

Espero que sea de ayuda. Podemos encontrar un ejemplo de funcionamiento de la ventana de información personalizada en http://wptrafficanalyzer.in/blog/customizing-infowindow-contents-in-google-map-android-api-v2-using-infowindowadapter/#comment-39731

EDITADO: Este código muestra cómo podemos agregar una vista personalizada en infoWindow. Este código no manejó los clics en los elementos de Vista personalizada. Por lo tanto, está cerca de responder, pero no es exactamente la respuesta, por eso no se acepta como respuesta.

Zeeshan Mirza
fuente
¿Anula getInfoContents y getInfoWindow donde crea los marcadores? Por ejemplo, si estoy agregando diferentes tipos de marcadores y utilizando diferentes métodos, ¿anularía dentro del cuerpo de cada uno de esos métodos donde creo los diferentes marcadores?
Rarw
Sí, anulo getInfoContents y getInfoWindow.
Zeeshan Mirza
Solo probé esto y funciona como inflar cualquier otro diseño xml - buen trabajo
Rarw
31
Parece que no leíste mi pregunta. Sé cómo crear una vista personalizada dentro de la ventana de información. Lo que no sé es cómo hacer un botón personalizado dentro y escuchar sus eventos de clic.
user1943012
55
Esta solución no responde a la pregunta original
biddulph.r