API Android do Google Maps v2 - InfoWindow interativo (como no original google google maps)

191

Estou tentando personalizar InfoWindowdepois de clicar em um marcador com a nova API do Google Maps v2. Quero que pareça no aplicativo de mapas original do Google. Como isso:

Imagem de exemplo

Quando estou ImageButtonlá dentro, não está funcionando - o todo InfoWindowé cortado e não apenas o ImageButton. Eu li que é porque não existe um em Viewsi, mas é um instantâneo; portanto, itens individuais não podem ser distinguidos um do outro.

EDIT: Na documentação (graças ao Disco S2 ):

Conforme mencionado na seção anterior, nas janelas de informações, uma janela de informações não é uma Visualização ao vivo, mas a visualização é renderizada como uma imagem no mapa. Como resultado, todos os ouvintes configurados na exibição são desconsiderados e você não pode distinguir entre os eventos de clique em várias partes da exibição. É recomendável não colocar componentes interativos - como botões, caixas de seleção ou entradas de texto - em sua janela de informações personalizadas.

Mas se o Google usá-lo, deve haver alguma maneira de fazê-lo. Alguém tem alguma ideia?

user1943012
fonte
"não está funcionando" não é uma descrição especialmente útil dos seus sintomas. Aqui é um projeto de exemplo que mostra ter uma janela de informações personalizada com uma imagem: github.com/commonsguy/cw-omnibus/tree/master/MapsV2/Popups
CommonsWare
8
@CommonsWare eu escrevi a razão disso. Da documentação original Nota: A janela de informações desenhada não é uma visualização ao vivo. A visualização é renderizada como uma imagem (usando View.draw(Canvas)) ... Isso significa que quaisquer alterações subsequentes na visualização não serão refletidas pela janela de informações no mapa. Para atualizar a janela de informações posteriormente Além disso, a janela de informações não respeitará a interatividade típica de uma exibição normal, como eventos de toque ou gestos. No entanto, você pode ouvir um evento de clique genérico em toda a janela de informações, conforme descrito na seção abaixo. ... no seu exemplo é apenas textview
user1943012 2/13/13
Olá, como você conseguiu que o botão minha localização apareça abaixo da barra de ação no modo de tela cheia? Obrigado!
tkblackbelt
45
Não acho que essa questão deva ser encerrada. É uma pergunta legítima para um problema que também estou enfrentando.
Marienke #
12
Vejo isso com tanta frequência que muitas das perguntas mais úteis com resposta foram encerradas. Uma pergunta nem sempre se encaixa nos requisitos exatos de uma pergunta adequada, mas se tivermos um PROBLEMA bem explicado e uma RESPOSTA bem explicada, não haverá motivo para encerrá-la. As pessoas usam o Stackoverflow para trocar conhecimento e fechar um tópico de perguntas que é tão ativo e produtivo como "off-topic" não me parece uma boa idéia. #
John

Respostas:

333

Eu estava procurando uma solução para esse problema, sem sorte, então tive que criar minha própria, que gostaria de compartilhar aqui com você. (Por favor, desculpe meu inglês ruim) (É um pouco louco responder a outro cara tcheco em inglês :-))

A primeira coisa que tentei foi usar uma boa e velha PopupWindow . É bem fácil - basta ouvir OnMarkerClickListenere mostrar um costume PopupWindowacima do marcador. Alguns outros caras aqui no StackOverflow sugeriram esta solução e ela realmente parece muito boa à primeira vista. Mas o problema com esta solução aparece quando você começa a mover o mapa. Você precisa mudar de PopupWindowalguma maneira o que é possível (ouvindo alguns eventos onTouch), mas IMHO não pode fazer com que pareça bom o suficiente, especialmente em alguns dispositivos lentos. Se você fizer isso da maneira simples, "pula" de um ponto para outro. Você também pode usar algumas animações para polir esses saltos, mas dessa forma PopupWindow, sempre estará "um passo atrás" onde deveria estar no mapa, do qual eu simplesmente não gosto.

Neste ponto, eu estava pensando em alguma outra solução. Percebi que realmente não preciso de tanta liberdade - para mostrar minhas visualizações personalizadas com todas as possibilidades que as acompanham (como barras de progresso animadas etc.). Eu acho que há uma boa razão pela qual até os engenheiros do Google não fazem dessa maneira no aplicativo Google Maps. Tudo o que preciso é de um ou dois botões no InfoWindow que mostrem um estado pressionado e acionem algumas ações quando clicados. Então, eu vim com outra solução que se divide em duas partes:

Primeira parte:
a primeira parte é conseguir capturar os cliques nos botões para acionar alguma ação. Minha ideia é a seguinte:

  1. Mantenha uma referência ao infoWindow personalizado criado no InfoWindowAdapter.
  2. Coloque o MapFragment(ou MapView) dentro de um ViewGroup personalizado (o meu é chamado MapWrapperLayout)
  3. Substitua o MapWrapperLayout dispatchTouchEvent e (se o InfoWindow estiver sendo exibido no momento) primeiro direcione os MotionEvents para o InfoWindow criado anteriormente. Se ele não consumir os MotionEvents (como porque você não clicou em nenhuma área clicável dentro do InfoWindow etc.), então (e somente então) deixou os eventos descerem para a superclasse do MapWrapperLayout, para que eventualmente sejam entregues no mapa.

Aqui está o código fonte do 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);
    }
}

Tudo isso tornará as visualizações dentro do InfoView "ativas" novamente - os OnClickListeners começarão a ser acionados etc.

Segunda parte: O problema restante é que, obviamente, você não pode ver nenhuma alteração na interface do seu InfoWindow na tela. Para fazer isso, você deve chamar manualmente Marker.showInfoWindow. Agora, se você realizar alguma alteração permanente no InfoWindow (como alterar o rótulo do botão para outra coisa), isso é bom o suficiente.

Mas mostrar um estado pressionado por botão ou algo dessa natureza é mais complicado. O primeiro problema é que (pelo menos) não consegui fazer com que o InfoWindow mostrasse o estado pressionado do botão normal. Mesmo se eu pressionasse o botão por um longo tempo, ele permaneceria pressionado na tela. Eu acredito que isso é algo tratado pela própria estrutura do mapa, que provavelmente garante que não mostre nenhum estado transitório nas janelas de informações. Mas eu posso estar errado, não tentei descobrir isso.

O que fiz foi outro truque desagradável - anexei um OnTouchListener no botão e troquei manualmente o plano de fundo quando o botão foi pressionado ou liberado para dois drawables personalizados - um com um botão no estado normal e o outro no estado pressionado. Isso não é muito bom, mas funciona :). Agora pude ver o botão alternando entre os estados normal e pressionado na tela.

Ainda existe uma última falha - se você clicar no botão rápido demais, ele não mostra o estado pressionado - ele permanece no estado normal (embora o clique em si seja acionado, o botão "funcionará"). Pelo menos é assim que aparece no meu Galaxy Nexus. Então a última coisa que fiz foi atrasar um pouco o botão no estado pressionado. Isso também é bastante feio e não tenho certeza de como funcionaria em alguns dispositivos mais antigos e lentos, mas suspeito que mesmo a estrutura do mapa faça algo assim. Você pode tentar você mesmo - quando você clicar em todo o InfoWindow, ele permanecerá pressionado por mais algum tempo e os botões normais (novamente - pelo menos no meu telefone). E é assim que funciona mesmo no aplicativo original do Google Maps.

Enfim, escrevi para mim mesma uma classe personalizada que lida com as alterações de estado dos botões e todas as outras coisas que mencionei, então aqui está o 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);
}

Aqui está um arquivo de layout personalizado do InfoWindow que eu usei:

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

Arquivo de layout de atividade de teste ( MapFragmentestando dentro do 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, finalmente, o código fonte de uma atividade de teste, que cola tudo isso:

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

É isso aí. Até agora, só testei isso no meu Galaxy Nexus (4.2.1) e Nexus 7 (também 4.2.1). Vou testá-lo em um telefone Gingerbread quando tiver chance. Uma limitação que encontrei até agora é que você não pode arrastar o mapa de onde está o botão na tela e movê-lo. Provavelmente poderia ser superado de alguma forma, mas por enquanto eu posso viver com isso.

Sei que esse é um truque feio, mas não encontrei nada melhor e preciso tanto desse padrão de design que seria realmente um motivo para voltar ao quadro v1 do mapa (que, aliás. Eu realmente gostaria de evitar para um novo aplicativo com fragmentos etc.). Só não entendo por que o Google não oferece aos desenvolvedores uma maneira oficial de ter um botão no InfoWindows. É um padrão de design tão comum, além disso, esse padrão é usado mesmo no aplicativo oficial do Google Maps :). Entendo os motivos pelos quais eles não podem simplesmente tornar suas visualizações "vivas" no InfoWindows - isso provavelmente prejudicaria o desempenho ao mover e rolar o mapa. Mas deve haver alguma maneira de obter esse efeito sem usar visualizações.

escolheu007
fonte
6
solução interessante. Eu quero tentar isso. Como foi o desempenho?
26413 Patrick
10
precisa substituir view.setBackground(bgDrawablePressed);por if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { view.setBackgroundDrawable(bgDrawablePressed); }else{ view.setBackground(bgDrawablePressed); }para funcionar no Android Gingerbread, Observação: há mais uma ocorrência view.setBackgroundno código.
Balaji
4
KK_07k11A0585: Parece que sua implementação do InfoWindowAdapter provavelmente está errada. Existem dois métodos para substituir: getInfoWindow () e getInfoContents (). A estrutura primeiro chama o método getInfoWindow () e (somente) se retornar nulo, chama o getInfoContents (). Se você retornar algum View do getInfoWindow, a estrutura o colocará dentro do quadro da janela de informações padrão - o que, no seu caso, você não deseja. Portanto, basta retornar null a partir de getInfoWindow e retornar a você View from getInfoContent, e então tudo ficará bem.
chose007
18
Se alguém estiver interessado, estou usando esta solução em meu próprio aplicativo - CG Transit (você pode encontrá-lo no Google Play). Depois de dois meses, ele foi usado por várias dezenas de milhares de usuários e ninguém se queixou ainda.
chose007
2
como você determina o deslocamento inferior se não está usando uma janela de infow padrão?
Rarw
12

Vejo que essa pergunta já é antiga, mas ainda ...

Criamos uma biblioteca simples em nossa empresa para alcançar o que é desejado - uma janela de informações interativa com visualizações e tudo mais. Você pode conferir no github .

Espero que ajude :)

dephinera
fonte
É possível usá-lo com clusters?
gcolucci
9

Aqui está a minha opinião sobre o problema. Crio uma AbsoluteLayoutsobreposição que contém a Janela de informações (uma exibição regular com todos os recursos de interatividade e desenho). Então começo, Handlerque sincroniza a posição da janela de informações com a posição do ponto no mapa a cada 16 ms. Parece loucura, mas realmente funciona.

Vídeo de demonstração: https://www.youtube.com/watch?v=bT9RpH4p9mU (leve em consideração que o desempenho diminuiu devido ao emulador e gravação de vídeo sendo executados simultaneamente).

Código da demonstração: https://github.com/deville/info-window-demo

Um artigo que fornece detalhes (em russo): http://habrahabr.ru/post/213415/

Andrii Chernenko
fonte
Isto irá desacelerar / lag percorrendo o mapa
Shane Ekanayake
2

Para aqueles que não conseguiram obter choose007'sresposta em funcionamento

Se clickListenernão estiver funcionando corretamente o tempo todo na chose007'ssolução, tente implementar em View.onTouchListenervez de clickListener. Manipule o evento de toque usando qualquer uma das ações ACTION_UPou ACTION_DOWN. Por alguma razão, os mapas infoWindowcausam algum comportamento estranho ao serem enviados para 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: Isto é como eu fiz isso passo a passo

Primeiro, inflar sua própria janela de entrada (variável global) em algum lugar da sua atividade / fragmento. O meu está dentro de um fragmento. Verifique também se a visualização raiz no layout da janela de entrada é layout linear (por algum motivo, o relativelayout estava ocupando toda a largura da tela na janela de entrada)

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;

Em seguida, no retorno de chamada onMapReady da API do Google Maps para Android (siga isto se você não sabe o que é onMapReady Mapas> Documentação - Introdução )

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

MapWrapperLayoutinicialização http://stackoverflow.com/questions/14123243/google-maps-android-api-v2- infowindow-interativo-como-no-original-android-go / 15040761 # 15040761 # 15040761 39 - altura padrão do marcador 20 - deslocamento entre o borda inferior padrão do InfoWindow e seu conteúdo borda inferior * /

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

Manipular o clique do marcador separadamente

    @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;
    }
Manish Jangid
fonte
você pode me mostrar seu código? Consegui registrar os eventos de toque com êxito, mas não os eventos de clique, então fui para esta implementação. Além disso, você precisa implementar a solução do choose007 corretamente.
Manish Jangid
Eu compartilhei minha implementação completa. Deixe-me saber se você tem alguma dúvida
Manish Jangid
0

Apenas uma especulação, não tenho experiência suficiente para experimentá-lo ...) -:

Como o GoogleMap é um fragmento, deve ser possível capturar o marcador no evento Click e mostrar a exibição personalizada do fragmento . Um fragmento de mapa ainda estará visível em segundo plano. Alguém tentou? Alguma razão para que não funcionasse?

A desvantagem é que o fragmento do mapa seria congelado no backgroud, até que um fragmento de informação personalizado retornasse o controle a ele.

Wooff
fonte
5
Não acho interessante postar uma especulação como resposta. Talvez deva ser um comentário sobre a pergunta original. Apenas meus pensamentos.
Johan Karlsson
1
Esta biblioteca github.com/Appolica/InteractiveInfoWindowAndroid faz algo semelhante
Deyan Genovski
-2

Eu construí um projeto de estúdio android de amostra para esta pergunta.

capturas de tela de saída: -

insira a descrição da imagem aqui

insira a descrição da imagem aqui

insira a descrição da imagem aqui

Faça o download do código fonte completo do projeto Clique aqui

Observe: você precisa adicionar sua chave de API no Androidmanifest.xml

Premkumar Manipillai
fonte
5
É melhor compartilhar seu código através do Github / gist em vez do arquivo zip. Para que possamos verificar diretamente lá.
Noundla Sandeep
um pequeno bug que encontrei, quando você pressiona algum botão, todos os botões são clicados.
Morozov 20/02
3
@ Noundla e outros: não se preocupe em baixar o código compactado. É uma pasta de cópia exata da resposta aceita para esta pergunta.
Ganesh Mohan
1
@ ganesh2shiv está certo, o código compactado é inútil. Você não deve ser anexado código colado cópia
Onkar Nene
-7

É realmente simples.

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;

            }

        });

Basta adicionar o código acima na sua classe onde você está usando o GoogleMap. R.layout.info_window_layout é o nosso layout personalizado que mostra a visualização que será substituída pela janela de infowindow. Acabei de adicionar a visualização de texto aqui. Você pode adicionar uma visualização adicional aqui para torná-la como a amostra. Meu 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>

Eu espero que isso ajude. Podemos encontrar um exemplo prático de janela de infow personalizada em http://wptrafficanalyzer.in/blog/customizing-infowindow-contents-in-google-map-android-api-v2-using-infowindowadapter/#comment-39731

EDITADO: Este código mostra como podemos adicionar uma exibição personalizada no infoWindow. Este código não tratou dos cliques nos itens da Visualização Personalizada. Portanto, está perto de responder, mas não é exatamente a resposta, por isso não é aceito como resposta.

Zeeshan Mirza
fonte
Você substitui getInfoContents e getInfoWindow onde cria os marcadores? Por exemplo, se eu estiver adicionando tipos diferentes de marcadores e usando métodos diferentes, eu simplesmente substituirei no corpo de cada um desses métodos em que crio os marcadores diferentes?
Rarw 28/02
Sim, substituo getInfoContents e getInfoWindow.
Zeeshan Mirza
Apenas tentei isso e ele funciona jus como inflar qualquer outro layout xml - bom trabalho
Rarw
31
Parece que você não leu minha pergunta. Eu sei como criar uma exibição personalizada dentro da infowindow. O que eu não sei é como criar um botão personalizado dentro e ouvir seus eventos de clique.
user1943012
5
Esta solução não responder à pergunta original
biddulph.r