Google Maps Android API v2 - InfoWindow interattiva (come nella google maps originale android)


191

Sto cercando di personalizzare con InfoWindowun clic su un marcatore con la nuova API di Google Maps v2. Voglio che appaia nell'applicazione mappe originale di Google. Come questo:

Immagine di esempio

Quando ho ImageButtondentro, non funziona - l'intero InfoWindowè tagliato e non solo il ImageButton. Ho letto che è perché non c'è uno Viewstesso ma è un'istantanea, quindi i singoli elementi non possono essere distinti l'uno dall'altro.

EDIT: nella documentazione (grazie a Disco S2 ):

Come menzionato nella sezione precedente sulle finestre informative, una finestra informativa non è una vista live, ma la vista viene visualizzata come immagine sulla mappa. Di conseguenza, tutti i listener impostati nella vista vengono ignorati e non è possibile distinguere tra eventi di clic in varie parti della vista. Si consiglia di non posizionare componenti interattivi, come pulsanti, caselle di controllo o input di testo, all'interno della finestra delle informazioni personalizzate.

Ma se Google lo utilizza, ci deve essere un modo per farlo. Qualcuno ha qualche idea?


"non funziona" non è una descrizione particolarmente utile dei sintomi. Ecco un esempio di progetto che mostra di avere una finestra
informativa

8
@CommonsWare Ho scritto questo. Dalla documentazione originale Nota: la finestra informativa disegnata non è una vista live. La vista viene visualizzata come immagine (usando View.draw(Canvas)) ... Ciò significa che eventuali successive modifiche alla vista non verranno riflesse dalla finestra informativa sulla mappa. Per aggiornare la finestra informativa in un secondo momento Inoltre, la finestra informativa non rispetterà alcuna interattività tipica di una vista normale come eventi touch o gesture. Tuttavia, è possibile ascoltare un evento di clic generico sull'intera finestra informativa, come descritto nella sezione seguente. ... nel tuo esempio è solo la visualizzazione di testo
user1943012

Ciao, come hai fatto ad apparire il pulsante della mia posizione sotto la barra delle azioni in modalità schermo intero? Grazie!
tkblackbelt,

45
Non penso che questa domanda dovrebbe essere chiusa. È una domanda legittima a un problema che sto affrontando.
marienke,

12
Riesco a vederlo così spesso, molte delle domande più utili con risposta sono state chiuse. Una domanda potrebbe non sempre soddisfare i requisiti esatti di una domanda corretta, ma se abbiamo un PROBLEMA ben spiegato e una RISPOSTA ben spiegata, non c'è motivo di chiudere la domanda. Le persone usano Stackoverflow per scambiare conoscenze e chiudere un thread di domande che è così attivamente e così produttivo come "off-topic" non mi sembra una buona idea ..
John,

Risposte:


333

Stavo cercando una soluzione a questo problema da solo, senza fortuna, quindi ho dovuto rotolare il mio che vorrei condividere qui con te. (Per favore, scusa il mio cattivo inglese) (È un po 'folle rispondere a un altro ragazzo ceco in inglese :-))

La prima cosa che ho provato è stato usare un buon vecchio PopupWindow. È abbastanza facile: bisogna solo ascoltare OnMarkerClickListenere quindi mostrare un'abitudine PopupWindowsopra il marker. Alcuni altri ragazzi qui su StackOverflow hanno suggerito questa soluzione e in realtà sembra abbastanza buona a prima vista. Ma il problema con questa soluzione si presenta quando inizi a spostare la mappa. Devi spostare PopupWindowte stesso in qualche modo ciò che è possibile (ascoltando alcuni eventi onTouch) ma IMHO non riesci a farlo sembrare abbastanza buono, specialmente su alcuni dispositivi lenti. Se lo fai nel modo più semplice, "salta" da un punto all'altro. Potresti anche usare alcune animazioni per lucidare quei salti, ma in questo modo PopupWindowsarà sempre "un passo indietro" dove dovrebbe essere sulla mappa che non mi piace.

A questo punto, stavo pensando a qualche altra soluzione. Mi sono reso conto che in realtà non ho davvero bisogno di tanta libertà - per mostrare le mie viste personalizzate con tutte le possibilità che ne derivano (come barre di avanzamento animate ecc.). Penso che ci sia una buona ragione per cui anche gli ingegneri di Google non lo fanno in questo modo nell'app di Google Maps. Tutto ciò di cui ho bisogno è un pulsante o due nell'InfoWindow che mostrerà uno stato premuto e farà scattare alcune azioni quando si fa clic. Quindi ho trovato un'altra soluzione che si divide in due parti:

Prima parte:
la prima parte deve essere in grado di catturare i clic sui pulsanti per attivare alcune azioni. La mia idea è la seguente:

  1. Conservare un riferimento alla finestra informativa personalizzata creata in InfoWindowAdapter.
  2. Avvolgi il MapFragment(o MapView) all'interno di un ViewGroup personalizzato (il mio si chiama MapWrapperLayout)
  3. Sostituisci MapWrapperLayoutdispatchTouchEvent e (se è attualmente visualizzata la finestra Info) indirizza prima i MotionEvents alla finestra Info creata in precedenza. Se non consuma i MotionEvents (come perché non hai fatto clic su alcuna area cliccabile all'interno di InfoWindow ecc.), Allora (e solo allora) lascia che gli eventi passino alla superclasse di MapWrapperLayout in modo che alla fine venga consegnato alla mappa.

Ecco il codice sorgente di 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);
    }
}

Tutto ciò renderà nuovamente attive le viste all'interno di InfoView: gli OnClickListeners inizieranno a attivarsi ecc.

Seconda parte: il problema rimanente è che, ovviamente, non è possibile visualizzare sullo schermo eventuali modifiche dell'interfaccia utente della finestra Info. Per fare ciò devi chiamare manualmente Marker.showInfoWindow. Ora, se esegui alcune modifiche permanenti nella tua InfoWindow (come cambiare l'etichetta del tuo pulsante in qualcos'altro), questo è abbastanza buono.

Ma mostrare uno stato premuto un pulsante o qualcosa del genere è più complicato. Il primo problema è che (almeno) non sono riuscito a fare in modo che l'InfoWindow mostrasse il normale stato premuto del pulsante. Anche se ho premuto a lungo il pulsante, è rimasto semplicemente non premuto sullo schermo. Credo che questo sia qualcosa gestito dalla struttura della mappa stessa che probabilmente si assicura di non mostrare alcun stato transitorio nelle finestre informative. Ma potrei sbagliarmi, non ho provato a scoprirlo.

Quello che ho fatto è un altro brutto trucco - ho attaccato un OnTouchListenerpulsante e ho cambiato manualmente il suo sfondo quando il pulsante è stato premuto o rilasciato su due drawable personalizzati - uno con un pulsante in uno stato normale e l'altro in uno stato premuto. Questo non è molto bello, ma funziona :). Ora sono stato in grado di vedere il pulsante passare dallo stato normale a quello premuto sullo schermo.

C'è ancora un ultimo glitch - se fai clic sul pulsante troppo velocemente, non mostra lo stato premuto - rimane solo nel suo stato normale (anche se il clic stesso viene attivato, quindi il pulsante "funziona"). Almeno questo è come si presenta sul mio Galaxy Nexus. Quindi l'ultima cosa che ho fatto è che ho ritardato un po 'il pulsante nel suo stato premuto. Anche questo è abbastanza brutto e non sono sicuro di come funzionerebbe su alcuni dispositivi più vecchi e lenti, ma sospetto che anche il framework della mappa stesso faccia qualcosa del genere. Puoi provarlo tu stesso - quando fai clic sull'intera InfoWindow, rimane in uno stato premuto un po 'più a lungo, quindi i normali pulsanti lo fanno (di nuovo - almeno sul mio telefono). Ed è così che funziona anche sull'app originale di Google Maps.

Comunque, mi sono scritto una classe personalizzata che gestisce i cambiamenti di stato dei pulsanti e tutte le altre cose che ho citato, quindi ecco il codice:

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);
}

Ecco un file di layout InfoWindow personalizzato che ho usato:

<?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>

File di layout dell'attività di test ( MapFragmentall'interno 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>

E infine il codice sorgente di un'attività di test, che incolla tutto questo insieme:

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);
    }
}

Questo è tutto. Finora ho provato questo solo sul mio Galaxy Nexus (4.2.1) e Nexus 7 (anche 4.2.1), lo proverò su alcuni telefoni Gingerbread quando ne avrò la possibilità. Una limitazione che ho scoperto finora è che non è possibile trascinare la mappa da dove si trova il pulsante sullo schermo e spostare la mappa. Probabilmente potrebbe essere superato in qualche modo, ma per ora, posso conviverci.

So che si tratta di un brutto trucco ma non ho trovato niente di meglio e ho così tanto bisogno di questo modello di progettazione che questo sarebbe davvero un motivo per tornare al framework map v1 (quale tra l'altro. Mi piacerebbe davvero evitare per una nuova app con frammenti ecc.). Non capisco perché Google non offra agli sviluppatori un modo ufficiale per avere un pulsante su InfoWindows. È un modello di progettazione così comune, inoltre questo modello viene utilizzato anche nell'app ufficiale di Google Maps :). Capisco i motivi per cui non possono semplicemente rendere "live" le tue viste in InfoWindows - questo probabilmente ucciderebbe le prestazioni quando si sposta e si scorre la mappa. Ma dovrebbe esserci un modo per ottenere questo effetto senza usare le viste.


6
soluzione interessante. Voglio provare questo Com'è andata la performance?
Patrick,

10
è necessario sostituirlo view.setBackground(bgDrawablePressed);con if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { view.setBackgroundDrawable(bgDrawablePressed); }else{ view.setBackground(bgDrawablePressed); }per funzionare in Android Gingerbread, Nota: c'è un'altra occorrenza di view.setBackgroundnel codice.
Balaji,

4
KK_07k11A0585: Sembra che l'implementazione di InfoWindowAdapter sia probabilmente errata. Esistono due metodi per sovrascrivere: getInfoWindow () e getInfoContents (). Il framework prima chiama il metodo getInfoWindow () e (solo) se restituisce null, chiama getInfoContents (). Se si restituisce una vista da getInfoWindow, il framework la posizionerà all'interno della cornice della finestra delle informazioni predefinita, cosa che nel tuo caso non si desidera. Quindi basta restituire null da getInfoWindow e restituire View da getInfoContent e quindi dovrebbe andare bene.
Ho scelto 007 il

18
Se qualcuno è interessato, sto usando questa soluzione nella mia app - CG Transit (la puoi trovare su Google Play). Dopo due mesi è stato utilizzato da diverse decine di migliaia di utenti e nessuno si è ancora lamentato.
Ho scelto 007 il

2
come si determina l'offset inferiore se non si utilizza una finestra standard?
Raro

12

Vedo che questa domanda è già vecchia ma ancora ...

Abbiamo creato una libreria di sipmle presso la nostra azienda per ottenere ciò che si desidera: una finestra informativa interattiva con viste e tutto. Puoi verificarlo su github .

Spero possa essere d'aiuto :)


È possibile usarlo con i cluster?
gcolucci,

9

Ecco la mia opinione sul problema. Creo una AbsoluteLayoutsovrapposizione che contiene la finestra informativa (una vista regolare con ogni bit di interattività e capacità di disegno). Quindi inizio Handlerche sincronizza la posizione della finestra informativa con la posizione del punto sulla mappa ogni 16 ms. Sembra pazzo, ma funziona davvero.

Video dimostrativo: https://www.youtube.com/watch?v=bT9RpH4p9mU (tenere presente che le prestazioni sono ridotte a causa dell'emulatore e della registrazione video in esecuzione contemporaneamente).

Codice della demo: https://github.com/deville/info-window-demo

Un articolo che fornisce dettagli (in russo): http://habrahabr.ru/post/213415/


Questo rallenterà / rallenterà lo scorrimento della mappa
Shane Ekanayake,

2

Per coloro che non sono stati in grado di choose007'srispondere e funzionare

Se clickListenernon funziona sempre correttamente nella chose007'ssoluzione, prova a implementare View.onTouchListenerinvece di clickListener. Gestisci l'evento touch usando una delle azioni ACTION_UPo ACTION_DOWN. Per qualche ragione, le mappe infoWindowcausano alcuni comportamenti strani durante la spedizione 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;
          }

Modifica: ecco come l'ho fatto passo dopo passo

Innanzitutto gonfia la tua infowindow (variabile globale) da qualche parte nella tua attività / frammento. Il mio è all'interno di un frammento. Assicurati anche che la vista di root nel layout della tua finestra sia linearlayout (per qualche ragione relativelayout stava prendendo l'intera larghezza dello schermo in infowindow)

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;

Quindi in onMapReady callback di google maps android api (segui questo se non sai cosa onMapReady è Maps> Documentazione - Per iniziare )

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

MapWrapperLayoutinizializzazione http://stackoverflow.com/questions/14123243/google-maps-android-api-v2- interactive-infowindow-like-in-original-android-go / 15040761 # 15040761 39 - altezza predefinita del marker 20 - offset tra il bordo inferiore predefinito di InfoWindow e contenuto bordo inferiore * /

        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;
            }
        });
    }

Metodo 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;
            }
        });

Gestire il clic del marker separatamente

    @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;
    }

puoi mostrarmi il tuo codice? Sono stato in grado di registrare correttamente gli eventi di tocco ma non di fare clic sugli eventi, quindi ho optato per questa implementazione. Inoltre, è necessario implementare correttamente la soluzione di Choose007.
Manish Jangid,

Ho condiviso la mia completa implementazione. Fammi sapere se hai domande
Manish Jangid

0

Solo una speculazione, non ho abbastanza esperienza per provarlo ...) -:

Poiché GoogleMap è un frammento, dovrebbe essere possibile catturare un marcatore nell'evento Click e mostrare una vista personalizzata del frammento . Un frammento di mappa sarà ancora visibile sullo sfondo. Qualcuno l'ha provato? Qualche motivo per cui non potrebbe funzionare?

Lo svantaggio è che il frammento di mappa verrebbe congelato su backgroud, fino a quando un frammento di informazioni personalizzato non lo restituirà.


5
Non credo sia interessante pubblicare una speculazione come risposta. Forse dovrebbe essere un commento sulla domanda originale. Solo i miei pensieri.
Johan Karlsson,

1
Questa libreria github.com/Appolica/InteractiveInfoWindowAndroid fa qualcosa di simile
Deyan Genovski

-2

Ho creato un progetto Android Studio di esempio per questa domanda.

schermate di output: -

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

Scarica il codice sorgente completo del progetto Fai clic qui

Nota: devi aggiungere la tua chiave API in Androidmanifest.xml


5
È meglio condividere il codice tramite Github / gist invece del file zip. Quindi possiamo controllare direttamente lì.
Noundla Sandeep,

un piccolo bug che ho riscontrato quando si preme un pulsante, tutti i pulsanti vengono cliccati.
Morozov,

3
@Noundla e altri: non preoccuparti di scaricare il suo codice zippato. È una copia esatta della risposta accettata per questa domanda.
Ganesh Mohan,

1
@ ganesh2shiv ha ragione, il codice zippato è inutile. Non dovresti essere allegato copia codice incollato
Onkar Nene

-7

È davvero semplice

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;

            }

        });

Aggiungi il codice sopra nella tua classe in cui stai utilizzando GoogleMap. R.layout.info_window_layout è il nostro layout personalizzato che mostra la vista che verrà al posto di infowindow. Ho appena aggiunto la visualizzazione di testo qui. Puoi aggiungere una vista aggiuntiva qui per renderla simile allo snap di esempio. Il mio info_window_layout era

<?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>

Spero che sarà di aiuto. Possiamo trovare un esempio funzionante di finestra personalizzata su http://wptrafficanalyzer.in/blog/customizing-infowindow-contents-in-google-map-android-api-v2-using-infowindowadapter/#comment-39731

MODIFICATO: questo codice mostra come possiamo aggiungere una visualizzazione personalizzata su infoWindow. Questo codice non ha gestito i clic sugli elementi della vista personalizzata. Quindi è vicino alla risposta ma non è esattamente la risposta, ecco perché non è accettata come risposta.


Sostituisci getInfoContents e getInfoWindow dove crei i marker? Ad esempio, se aggiungo diversi tipi di marcatori e utilizzo metodi diversi, dovrei semplicemente sostituire il corpo di ciascuno di quei metodi in cui creo i diversi marcatori?
Rarw,

Sì, eseguo l'override di getInfoContents e getInfoWindow.
Zeeshan Mirza,

Ho appena provato questo e funziona semplicemente come gonfiare qualsiasi altro layout XML - buon lavoro
Rarw

31
Sembra che tu non abbia letto la mia domanda. So come creare una vista personalizzata all'interno della finestra. Che non so è come creare un pulsante personalizzato all'interno e ascoltare i suoi eventi di clic.
user1943012

5
Questa soluzione non risponde alla domanda originale
biddulph.r
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.