Google Maps Android API v2-interaktywne InfoWindow (jak w oryginalnych android google maps)

Próbuję zrobić niestandardowe InfoWindow po kliknięciu na znacznik z nowym Google Maps API v2. Chcę, aby wyglądało to jak w oryginalnej aplikacji map przez Google. Tak:

Przykładowy Obrazek

Kiedy mam ImageButton w środku, nie działa - cały InfoWindow jest przecięty, a nie tylko ImageButton. Czytałam, że to dlatego, że nie ma View samej w sobie, ale jest to migawka, więc poszczególnych elementów nie da się odróżnić od siebie.

EDIT: W dokumentacja (Dzięki Disco S2):

Jak wspomniano w poprzedniej sekcji Okna info, okno info nie jest aktywnym widokiem, raczej widok jest renderowany jako obraz na Mapa. W rezultacie wszystkie detektory ustawione w widoku nie są brane pod uwagę i nie można rozróżnić zdarzeń kliknięć w różnych częściach widok. Zaleca się nie umieszczać elementów interaktywnych - takich jako przyciski, pola wyboru lub wejścia tekstowe-w ramach informacji niestandardowych okno.

Ale jeśli Google go używa, musi być jakiś sposób, aby to zrobić. Czy ktoś ma jakiś pomysł?

Author: albert c braun, 2013-01-02

7 answers

Sam szukałem rozwiązania tego problemu bez powodzenia, więc musiałem rzucić własne, którymi chciałbym się z wami podzielić. (Proszę wybaczyć mój zły angielski) (to trochę szalone, aby odpowiedzieć innym Czechem po angielsku : -))

Pierwszą rzeczą, którą próbowałem, było użycie starego, dobrego PopupWindow. To dość proste - wystarczy tylko odsłuchać OnMarkerClickListener, a następnie pokazać Niestandardowy PopupWindow nad markerem. Jacyś inni goście na StackOverflow zasugerowali To rozwiązanie i wygląda całkiem dobry na pierwszy rzut oka. Ale problem z tym rozwiązaniem pojawia się, gdy zaczniesz przesuwać mapę. Trzeba jakoś samemu przesunąć PopupWindow, co jest możliwe (słuchając niektórych zdarzeń onTouch) , ale IMHO nie da się tego zrobić, szczególnie na niektórych wolnych urządzeniach. Jeśli zrobisz to w prosty sposób, "przeskakuje" z jednego miejsca na drugie. Możesz również użyć animacji do polerowania tych skoków, ale w ten sposób PopupWindow zawsze będzie "krok w tył", gdzie powinien być na mapie co mi się po prostu nie podoba.

W tym momencie myślałem o innym rozwiązaniu. Zdałem sobie sprawę, że tak naprawdę nie potrzebuję tyle swobody - aby pokazać moje niestandardowe widoki ze wszystkimi możliwościami, które się z tym wiążą(jak animowane paski postępu itp.). Myślę, że jest dobry powód, dla którego nawet inżynierowie google nie robią tego w ten sposób w aplikacji Google Maps. Wszystko, czego potrzebuję, to przycisk lub dwa na InfoWindow, który pokaże wciśnięty stan i wywoła pewne działania po kliknięciu. Więc Ja wymyślił inne rozwiązanie, które dzieli się na dwie części:

Pierwsza część:
Pierwsza część polega na tym, aby móc złapać kliknięcia na przyciskach, aby wywołać jakąś akcję. Mój pomysł jest następujący:

  1. Zachowaj odniesienie do niestandardowego infoWindow utworzonego w InfoWindowAdapter.
  2. W tym celu należy utworzyć własną grupę widoków (moja nazywa się MapWrapperLayout) MapFragment (lub MapView).]}
  3. Override MapWrapperLayout's dispatchTouchEvent and (if the InfoWindow is aktualnie pokazane) pierwsza trasa MotionEvents do wcześniej utworzonego InfoWindow. Jeśli nie zużywa MotionEvents (np. dlatego, że nie kliknąłeś żadnego klikalnego obszaru wewnątrz InfoWindow itp.) wtedy (i tylko wtedy) niech wydarzenia zejdą do superklasy MapWrapperLayout, więc w końcu zostaną dostarczone na mapę.

Oto kod źródłowy 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);
    }
}

Wszystko to sprawi, że widoki wewnątrz InfoView" na żywo " ponownie-OnClickListeners zacznie wyzwalać itp.

Druga część: Pozostały problem polega na tym, że oczywiście nie widać żadnych zmian interfejsu użytkownika w InfoWindow na ekranie. Aby to zrobić, musisz ręcznie wywołać Marker.showInfoWindow. Teraz, jeśli wykonasz jakąś stałą zmianę w InfoWindow (np. zmienisz Etykietę przycisku na coś innego), jest to wystarczająco dobre.

Ale pokazanie stanu wciśniętego przycisku lub czegoś takiego jest bardziej skomplikowane. Pierwszy problem polega na tym, że (w najmniej) nie byłem w stanie zrobić InfoWindow pokazać normalny przycisk wciśnięty stan. Nawet jeśli nacisnąłem przycisk przez długi czas, po prostu pozostał bez naciśnięcia na ekranie. Uważam, że jest to coś, co jest obsługiwane przez sam framework map, który prawdopodobnie upewnia się, że nie pokazuje żadnego stanu przejściowego w oknach informacyjnych. Ale mogę się mylić, nie próbowałem się tego dowiedzieć.

To co zrobiłem to kolejny paskudny hack-podłączyłem OnTouchListener do przycisku i ręcznie podmieniłem to tło, gdy przycisk został naciśnięty lub zwolniony do dwóch niestandardowych rysunków - jeden z Przyciskiem w stanie normalnym, a drugi w stanie wciśniętym. Nie jest to zbyt miłe, ale działa :). Teraz udało mi się zobaczyć przycisk przełączający się między stanem normalnym a naciśniętym na ekranie.

Jest jeszcze jedna ostatnia usterka - jeśli klikniesz przycisk zbyt szybko, nie pokazuje wciśniętego stanu-po prostu pozostaje w normalnym stanie (chociaż samo kliknięcie jest wywoływane, więc przycisk "działa"). Przynajmniej to jest jak to się pojawia w moim Galaxy Nexus. Więc ostatnią rzeczą, jaką zrobiłem, jest to, że opóźniłem przycisk w stanie, w którym jest wciśnięty. Jest to również dość brzydkie i nie jestem pewien, jak to działa na niektórych starszych, powolnych urządzeniach, ale podejrzewam, że nawet sam framework map robi coś takiego. Możesz spróbować sam - po kliknięciu całego Infowindowa pozostaje w stanie wciśniętym trochę dłużej, wtedy robią to normalne przyciski (znowu - przynajmniej na moim telefonie). I tak to działa nawet na oryginalna aplikacja Google Maps.

W każdym razie, napisałem sobie niestandardową klasę, która obsługuje zmiany stanu przycisków i wszystkie inne rzeczy, o których wspomniałem, więc oto kod: {]}

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

Oto niestandardowy plik układu InfoWindow, którego użyłem:

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

Test activity layout file (MapFragment being inside the 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>

I na koniec kod źródłowy działania testowego, który skleja to wszystko razem:

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

To jest to. Do tej pory testowałem to tylko na moim Galaxy Nexus (4.2.1) i Nexus 7 (również 4.2.1), wypróbuję go na jakimś piernikowym telefonie, kiedy będę miał okazję. Ograniczenie, które znalazłem do tej pory jest to, że nie można przeciągnąć mapę z miejsca, gdzie znajduje się przycisk na ekranie i przenieść mapę wokół. Pewnie da się to jakoś przezwyciężyć, ale na razie mogę z tym żyć.

Wiem, że to brzydki hack, ale po prostu nie znalazłem nic lepszego i potrzebuję tego wzorca projektowego tak bardzo, że naprawdę byłby to powód, aby wrócić do map v1 framework (który btw. Naprawdę bardzo chciałbym uniknąć dla nowej aplikacji z fragmentami itp.). Po prostu nie rozumiem, dlaczego Google nie oferuje deweloperom jakiegoś oficjalnego sposobu na posiadanie przycisku na InfoWindows. To taki powszechny wzorzec projektowy, ponadto ten wzorzec jest używany nawet w oficjalnej aplikacji Google Maps :). Rozumiem powody, dla których nie mogą po prostu sprawić, że Twoje widoki będą "na żywo" w InfoWindows - prawdopodobnie zabiłoby to wydajność podczas przesuwania i przewijania mapy. Ale powinien być jakiś sposób, jak osiągnąć ten efekt bez użycia widoków.

 316
Author: chose007,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-29 11:42:36

Widzę, że to pytanie jest już stare, ale jednak...

Stworzyliśmy bibliotekę sipmle w naszej firmie, aby osiągnąć to, co jest pożądane-interaktywne okno informacyjne z widokami i wszystkim. Możesz to sprawdzić na GitHubie.

Mam nadzieję, że to pomoże:)

 11
Author: definera,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-28 11:44:35

Oto moje podejście do problemu. Tworzę AbsoluteLayout nakładkę, która zawiera okno Info (zwykły widok z każdym bitem interaktywności i możliwości rysowania). Następnie uruchamiam Handler która synchronizuje pozycję okna informacyjnego z pozycją punktu na mapie co 16 ms. brzmi szalenie, ale faktycznie działa.

Demo video: https://www.youtube.com/watch?v=bT9RpH4p9mU (weź pod uwagę, że wydajność jest zmniejszona z powodu uruchomionego emulatora i nagrywania wideo jednocześnie).

Kod demo: https://github.com/deville/info-window-demo

Artykuł zawierający szczegóły (w języku rosyjskim): http://habrahabr.ru/post/213415/

 8
Author: Andrii Chernenko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-13 20:08:18

Dla tych, którzy nie mogli uzyskać choose007's odpowiedzi i działa

Jeśli clickListener nie działa prawidłowo przez cały czas w chose007's Rozwiązanie, Spróbuj zaimplementować View.onTouchListener zamiast clickListener. Obsługa zdarzenia dotykowego za pomocą dowolnej akcji ACTION_UP lub ACTION_DOWN. Z jakiegoś powodu mapy infoWindow powodują dziwne zachowanie podczas wysyłania do 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;
          }

Edit: tak zrobiłem krok po kroku

Najpierw nadmuchaj swój własny infowindow (zmienna globalna) gdzieś w swoim aktywność / fragment. Mój jest w środku. Upewnij się również, że główny widok w układzie infowindow jest linearlayout (z jakiegoś powodu relativelayout zajmował pełną szerokość ekranu w 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;

Następnie w onmapready callback z google maps Android api (postępuj zgodnie z tym, jeśli nie wiesz, co onMapReady jest Mapy > dokumentacja - pierwsze kroki )

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

MapWrapperLayout inicjalizacja http://stackoverflow.com/questions/14123243/google-maps-android-api-v2- interactive-infowindow-like-in-original-android-go / 15040761#15040761 39-domyślna wysokość znacznika 20-przesunięcie między domyślną dolną krawędzią InfoWindow a dolną krawędzią zawartości */

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

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

Uchwyt markera kliknij osobno

    @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;
    }
 2
Author: Manish Jangid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-16 12:05:15

Tylko spekulacje, mam za mało doświadczenia, aby spróbować... )-:

Ponieważ GoogleMap jest fragmentem, powinno być możliwe przechwycenie znacznika onClick event i pokazanie własnego widoku fragment . Fragment mapy będzie nadal widoczny w tle. Czy ktoś próbował? Jakiś powód, dla którego to nie może działać?

Wadą jest to, że fragment mapy zostanie zamrożony na tle, dopóki Niestandardowy fragment informacji nie zwróci do niego kontroli.

 0
Author: Wooff,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-24 08:29:31

Zbudowałem przykładowy projekt android studio dla tego pytania.

Wyjściowe zrzuty ekranu:-

Tutaj wpisz opis obrazka

Tutaj wpisz opis obrazka

Tutaj wpisz opis obrazka

Pobierz Pełny kod źródłowy projektu Kliknij proszę.

Uwaga: musisz dodać swój klucz API w Androidmanifest.xml

 -2
Author: Premkumar Manipillai,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-30 11:57:25

To naprawdę proste.

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;

            }

        });

Po prostu dodaj powyższy kod w swojej klasie, gdzie używasz GoogleMap. R. layout.info_window_layout jest naszym niestandardowym układem, który pokazuje widok, który pojawi się w miejsce infowindow. Właśnie dodałem tu textview. Możesz dodać dodatkowy widok tutaj, aby wyglądał jak przystawka próbki. Moje info_window_layout było

<?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>
Mam nadzieję, że to pomoże. Możemy znaleźć działający przykład niestandardowego infowindow na http://wptrafficanalyzer.in/blog/customizing-infowindow-contents-in-google-map-android-api-v2-using-infowindowadapter/#comment-39731

Edytowany: ten kod pokazuje jak możemy dodać własny widok na infoWindow. Ten kod nie obsługiwał kliknięć na niestandardowych elementach widoku. Więc jest blisko odpowiedzi, ale nie dokładnie odpowiedź, dlatego nie jest akceptowane jako odpowiedź.

 -5
Author: Zeeshan Mirza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-29 06:06:50