Google Maps Android API v2-대화 형 InfoWindow (원래 Android Google지도와 유사)


191

InfoWindow새로운 Google Maps API v2에서 마커를 클릭 한 후 맞춤 제작을 시도하고 있습니다. Google의 원래지도 응용 프로그램 에서처럼 보이기를 원합니다. 이처럼 :

이미지 예

내가 ImageButton안에 있을 때 , 그것의 작동하지 않습니다-전체 InfoWindow뿐만 아니라뿐만 아니라 선택 ImageButton됩니다. View자체는 없지만 스냅 샷이기 때문에 개별 항목을 구별 할 수 없기 때문에 읽었습니다 .

편집 : 에서 문서 (덕분에 디스코 S2 ) :

정보창의 이전 섹션에서 언급했듯이 정보창은 라이브 뷰가 아니라 뷰가 이미지에 맵으로 렌더링됩니다. 결과적으로 뷰에서 설정 한 리스너는 무시되고 뷰의 여러 부분에서 클릭 이벤트를 구분할 수 없습니다. 버튼, 확인란 또는 텍스트 입력과 같은 대화 형 구성 요소를 사용자 정의 정보 창에 배치하지 않는 것이 좋습니다.

그러나 구글이 그것을 사용한다면 그것을 만들 수있는 방법이 있어야한다. 누구든지 어떤 아이디어가 있습니까?


"작동하지 않습니다"는 증상에 대한 유용한 설명이 아닙니다. 다음은 이미지가 포함 된 사용자 정의 정보 창을 보여주는 샘플 프로젝트입니다. github.com/commonsguy/cw-omnibus/tree/master/MapsV2/Popups
CommonsWare

8
@CommonsWare 나는 그 이유를 썼습니다. 원본 문서에서 참고 : 그려진 정보창은 라이브 뷰가 아닙니다. 뷰는 이미지로 렌더링됩니다 (을 사용하여 View.draw(Canvas)). 이는 뷰에 대한 후속 변경 사항이 맵의 정보 창에 반영되지 않음을 의미합니다. 정보창을 나중에 업데이트하려면 또한 정보창은 터치 또는 제스처 이벤트와 같은 일반보기에 일반적으로 나타나는 상호 작용을 고려하지 않습니다. 그러나 아래 섹션에 설명 된대로 전체 정보 창에서 일반 클릭 이벤트를들을 수 있습니다. ... 귀하의 예에서는 textview입니다
user1943012

안녕하세요, 전체 화면 모드에서 내 위치 버튼이 작업 표시 줄 아래에 표시되도록하려면 어떻게합니까? 감사!
tkblackbelt

45
나는이 질문을 닫아야한다고 생각하지 않습니다. 내가 직면하고있는 문제에 대한 합법적 인 질문입니다.
marienke '10

12
나는 이것을 너무 자주 볼 수 있으며, 대답과 함께 가장 유용한 질문이 많이 닫혔습니다. 질문이 항상 올바른 질문의 정확한 요구 사항에 맞지는 않을 수도 있지만 문제가 잘 설명되어 있고 대답이 잘되어 있으면 질문을 종결 할 이유가 없습니다. 사람들은 Stackoverflow를 사용하여 지식을 교환하고 "주 제외"가 좋은 생각처럼 보이지 않기 때문에 적극적이고 생산적인 질문 스레드를 닫습니다 ..
John

답변:


333

나는이 문제에 대한 해결책을 운없이 직접 찾고 있었기 때문에 여기서 공유하고 싶은 것을 내 자신에게 굴려야했습니다. (나의 나쁜 영어를 용서해주십시오) (영어로 다른 체코 ​​사람에게 대답하는 것은 약간 미친 일입니다 :-))

내가 시도한 첫 번째 일은 좋은 것을 사용하는 것이 었습니다 PopupWindow. 매우 쉽습니다. 하나만 듣고 마커 위에 OnMarkerClickListener커스텀을 표시 PopupWindow하면됩니다. StackOverflow의 다른 사람들 이이 솔루션을 제안했으며 실제로 언뜻보기에 꽤 좋아 보입니다. 그러나이 솔루션의 문제는지도를 움직이기 시작할 때 나타납니다. PopupWindow어떤 onTouch 이벤트를 들으면서 어떻게 든 자신 을 움직여야 하지만 IMHO를 사용하면 특히 느린 장치에서 충분히 잘 보이게 할 수 없습니다. 간단한 방법을 사용하면 한 지점에서 다른 지점으로 "점프"합니다. 또한 애니메이션을 사용하여 점프를 다듬을 수도 있지만이 방법 PopupWindow은 항상 내가 싫어하는 맵에 있어야하는 "한 걸음"입니다.

이 시점에서 나는 다른 해결책에 대해 생각하고있었습니다. 실제로 실제로 많은 자유가 필요하지 않다는 것을 깨달았습니다. 애니메이션 진행률 표시 줄과 같은 모든 가능성으로 사용자 정의보기를 표시 할 수 있습니다. Google 엔지니어조차도 Google지도 앱 에서이 방법을 사용하지 않는 이유가 있다고 생각합니다. InfoWindow의 버튼 하나만 누르면 눌린 상태가 표시되고 클릭 할 때 일부 동작이 트리거됩니다. 그래서 두 부분으로 나뉘는 다른 솔루션을 생각해 냈습니다.

첫 번째 부분 :
첫 번째 부분은 버튼 클릭을 잡아서 동작을 트리거 할 수 있도록하는 것입니다. 내 생각은 다음과 같습니다.

  1. InfoWindowAdapter에서 작성된 사용자 정의 infoWindow에 대한 참조를 유지하십시오.
  2. MapFragment(또는 MapView)를 사용자 정의 ViewGroup 안에 래핑하십시오 ( 광산은 MapWrapperLayout이라고 함)
  3. MapWrapperLayout의 dispatchTouchEvent를 재정의 하고 (InfoWindow가 현재 표시된 경우) 먼저 MotionEvents를 이전에 생성 된 InfoWindow로 라우팅합니다. InfoEvent 등에서 클릭 가능한 영역을 클릭하지 않았기 때문에 MotionEvents를 사용하지 않는 경우 이벤트는 MapWrapperLayout의 수퍼 클래스로 내려 가서 결국 맵으로 전달됩니다.

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

이 모든 것이 InfoView 내부의 뷰를 다시 "라이브"하게합니다. OnClickListener가 트리거링을 시작합니다.

두 번째 부분 : 나머지 문제는 분명히 InfoWindow의 UI 변경 사항을 화면에서 볼 수 없다는 것입니다. 그렇게하려면 Marker.showInfoWindow를 수동으로 호출해야합니다. 이제 버튼의 레이블을 다른 것으로 변경하는 것과 같이 InfoWindow에서 일부 영구적 변경을 수행하면 충분합니다.

그러나 버튼을 누르는 상태 또는 그 성질을 나타내는 것은 더 복잡합니다. 첫 번째 문제는 (적어도) InfoWindow가 정상적인 버튼을 눌렀을 때 상태를 표시 할 수 없다는 것입니다. 버튼을 오랫동안 누른 경우에도 화면에서 누르지 않은 상태로 유지되었습니다. 나는 이것이지도 프레임 워크 자체에서 처리되는 것으로 믿고 아마도 정보 창에 과도 상태를 표시하지 않을 것입니다. 그러나 나는 틀릴 수 있었다. 나는 이것을 찾으려고하지 않았다.

내가 한 일은 또 다른 불쾌한 해킹입니다 OnTouchListener. 버튼에 버튼을 부착 하고 버튼을 누르거나 놓을 때 수동으로 배경을 수동으로 전환했습니다. 하나는 정상 상태의 버튼과 다른 하나는 눌린 상태입니다. 이것은 그리 좋지는 않지만 작동합니다 :). 이제 화면에서 버튼이 정상 상태에서 눌린 상태로 전환되는 것을 볼 수있었습니다.

버튼을 너무 빨리 클릭하면 눌려진 상태가 표시되지 않습니다-클릭 자체가 시작되어 "작동"하더라도 여전히 정상적인 상태로 유지됩니다. 적어도 이것이 내 Galaxy Nexus에 표시되는 방식입니다. 마지막으로 한 번 누르면 버튼이 눌린 상태에서 약간 지연된 것입니다. 이것은 또한 추악한 일이며, 오래되고 느린 장치에서 어떻게 작동하는지 잘 모르겠지만지도 프레임 워크 자체조차도 이와 같은 일을한다고 생각합니다. 전체 InfoWindow를 클릭하면 누른 상태로 약간 더 길게 누른 다음 일반 버튼이 작동합니다 (적어도 내 전화에서는). 그리고 이것이 원래 Google지도 앱에서도 실제로 작동하는 방식입니다.

어쨌든, 버튼 상태 변경과 내가 언급 한 다른 모든 것을 처리하는 사용자 정의 클래스를 작성했습니다. 그래서 코드는 다음과 같습니다.

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

내가 사용한 사용자 정의 InfoWindow 레이아웃 파일은 다음과 같습니다.

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

테스트 활동 레이아웃 파일 ( MapFragment내부에 있음 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>

그리고 마지막으로 테스트 활동의 소스 코드로이 모든 것을 하나로 묶습니다.

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

그게 다야. 지금까지 Galaxy Galaxy (4.2.1) 및 Nexus 7 (4.2.1)에서만이 테스트를 수행 했으므로 기회가있을 때 진저 브레드 전화에서 시도해 봅니다. 지금까지 내가 발견 한 한계는 화면의 버튼이있는 곳에서지도를 드래그 할 수없고지도를 움직일 수 없다는 것입니다. 어쨌든 극복 할 수는 있지만 지금은 그와 함께 살 수 있습니다.

나는 이것이 추악한 해킹이라는 것을 알고 있지만 더 좋은 것을 찾지 못했습니다.이 디자인 패턴이 너무 나빠서 맵 v1 프레임 워크 (btw. 나는 정말로 피하고 싶습니다. 조각 등이 포함 된 새 앱의 경우). Google이 개발자에게 InfoWindows에 버튼을 가질 수있는 공식적인 방법을 제공하지 않는 이유를 이해하지 못합니다. 그것은 일반적인 디자인 패턴이며,이 패턴은 공식 Google Maps 앱에서도 사용됩니다 :). InfoWindows에서 뷰를 "실시간"으로 만들 수없는 이유를 이해합니다.지도를 이동하고 스크롤 할 때 성능이 저하 될 수 있습니다. 그러나 뷰를 사용하지 않고이 효과를 얻는 방법이 있어야합니다.


6
흥미로운 해결책. 나는 이것을 시도하고 싶다. 성능은 어땠습니까?
Patrick

10
교체 할 필요 view.setBackground(bgDrawablePressed);와 함께 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { view.setBackgroundDrawable(bgDrawablePressed); }else{ view.setBackground(bgDrawablePressed); }, 안드로이드 진저 브레드에서 작업에 참고 : 또 하나 개의 발생이 view.setBackground코드는.
Balaji

4
KK_07k11A0585 : InfoWindowAdapter 구현이 잘못된 것 같습니다. 대체 할 메소드는 getInfoWindow () 및 getInfoContents ()입니다. 프레임 워크는 먼저 getInfoWindow () 메소드를 호출하고 null 만 리턴하면 getInfoContents ()를 호출합니다. getInfoWindow에서 일부보기를 반환하면 프레임 워크는 기본 정보 창 프레임 안에 배치합니다. 따라서 getInfoWindow에서 null을 반환하고 getInfoContent에서 View를 반환하면 괜찮습니다.
chose007007

18
관심있는 사람은 내 솔루션 인 CG Transit (Google Play에서 찾을 수 있음) 에서이 솔루션을 사용하고 있습니다. 두 달 후 수만 명의 사용자가 사용했으며 아직 아무도 불평하지 않았습니다.
chose007007

2
표준 정보창을 사용하지 않는 경우 하단 오프셋을 어떻게 결정합니까?
Rarw

12

이 질문은 이미 오래되었지만 여전히 ...

우리는 원하는 것을 달성하기 위해 회사에서 sipmle 라이브러리를 만들었습니다-전망과 모든 것을 갖춘 대화 형 정보 창. github에서 확인할 수 있습니다 .

나는 그것이 도움이되기를 바랍니다 :)


클러스터와 함께 사용할 수 있습니까?
gcolucci

9

여기에 문제가 있습니다. AbsoluteLayoutInfo Window (모든 상호 작용 및 그리기 기능이있는 일반보기)가 포함 된 오버레이를 만듭니다 . 그런 다음 Handler정보 창의 위치를 ​​16ms마다지도상의 위치와 동기화하기 시작 합니다. 미친 듯이 들리지만 실제로 작동합니다.

데모 비디오 : https://www.youtube.com/watch?v=bT9RpH4p9mU (에뮬레이터와 비디오 녹화가 동시에 실행되므로 성능이 저하된다는 점을 고려하십시오).

데모 코드 : https://github.com/deville/info-window-demo

세부 정보를 제공하는 기사 (러시아어) : http://habrahabr.ru/post/213415/


이지도 스크롤 지상 (Lag) / 느려집니다
셰인 Ekanayake

2

choose007's응답을 얻지 못한 사람들을 위해

경우 clickListener에 항상 제대로 작동하지 않는 chose007's솔루션을 구현하는 시도 View.onTouchListener대신에 clickListener. 액션의 사용 핸들 터치 이벤트 ACTION_UP또는 ACTION_DOWN. 어떤 이유로 맵 infoWindow을에 전달할 때 이상한 동작이 발생합니다 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;
          }

편집 : 이것은 단계별로 수행 한 방법입니다

먼저 활동 / 조각의 어딘가에 자신의 정보창 (전역 변수)을 부풀립니다. 광산은 조각 안에 있습니다. 또한 정보창 레이아웃의 루트보기가 linearlayout인지 확인하십시오 (어떤 이유로 relativelayout은 정보창에서 화면의 전체 너비를 취했습니다)

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;

그런 다음 Google의 onMapReady 콜백에서 Android API를 매핑합니다 (onMapReady 가지도 > 문서-시작하기 인지 모르는 경우 이것을 따르십시오 )

   @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초기화 http://stackoverflow.com/questions/14123243/google-maps-android-api-v2- interactive-infowindow-like-in-original-android-go / 15040761 # 15040761 39-기본 마커 높이 20- 기본 InfoWindow 아래쪽 가장자리와 내용 아래쪽 가장자리 * /

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

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

마커 클릭을 개별적으로 처리

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

코드를 보여줄 수 있습니까? 터치 이벤트는 성공적으로 기록 할 수 있었지만 클릭 이벤트는 기록 할 수 없었으므로이 구현을 진행했습니다. 또한 choose007의 솔루션을 올바르게 구현해야합니다.
Manish Jangid

나는 완전한 구현을 공유했다. 문의 사항이 있으시면 알려주세요
Manish Jangid

0

단지 추측, 나는 그것을 시도하기에 충분한 경험이 없습니다 ...)-:

GoogleMap은 단편이므로 마커 onClick 이벤트를 포착하고 사용자 정의 단편 보기를 표시 할 수 있어야합니다 . 지도 조각은 여전히 ​​배경에 표시됩니다. 아무도 그것을 시도 했습니까? 그것이 작동하지 않는 이유는 무엇입니까?

단점은 사용자 정의 정보 단편이 제어를 리턴 할 때까지 맵 단편이 backgroud에서 정지된다는 것입니다.


5
나는 추측으로 답을 올리는 것이 흥미롭지 않다고 생각합니다. 아마도 원래 질문에 대한 의견이어야합니다. 그냥 내 생각
Johan Karlsson

1
이 라이브러리 github.com/Appolica/InteractiveInfoWindowAndroid는 비슷한 않습니다
데얀 Genovski

-2

이 질문에 대한 샘플 안드로이드 스튜디오 프로젝트를 만들었습니다.

출력 스크린 샷 :-

여기에 이미지 설명을 입력하십시오

여기에 이미지 설명을 입력하십시오

여기에 이미지 설명을 입력하십시오

전체 프로젝트 소스 코드 다운로드 여기를 클릭 하십시오

참고 : Androidmanifest.xml에 API 키를 추가해야합니다


5
zip 파일 대신 Github / gist를 통해 코드를 공유하는 것이 좋습니다. 직접 확인할 수 있습니다.
Noundla Sandeep

약간의 버그를 발견했습니다 .u 버튼을 누르면 모든 버튼이 클릭됩니다.
Morozov

3
@ Noundla 및 기타 : 압축 된 코드를 다운로드하지 않아도됩니다. 이 질문에 허용되는 답변의 정확한 사본입니다.
Ganesh Mohan

1
@ ganesh2shiv가 맞습니다. 압축 코드는 쓸모가 없습니다. 붙여 넣기 코드에 첨부해서는 안됩니다
Onkar Nene

-7

정말 간단합니다.

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;

            }

        });

GoogleMap을 사용하는 클래스에 위의 코드를 추가하십시오. R.layout.info_window_layout은 infowindow 대신 제공되는보기를 보여주는 사용자 지정 레이아웃입니다. 방금 텍스트 뷰를 여기에 추가했습니다. 여기에 추가 뷰를 추가하여 샘플 스냅처럼 보이게 할 수 있습니다. 내 info_window_layout은

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

도움이 되길 바랍니다. http://wptrafficanalyzer.in/blog/customizing-infowindow-contents-in-google-map-android-api-v2-using-infowindowadapter/#comment-39731 에서 맞춤형 정보창의 실제 예를 찾을 수 있습니다 .

편집 :이 코드는 infoWindow에 커스텀 뷰를 추가하는 방법을 보여줍니다. 이 코드는 사용자 정의보기 항목에 대한 클릭을 처리하지 않았습니다. 따라서 답변에 가깝지만 정확한 답변이 아니기 때문에 답변으로 받아 들여지지 않습니다.


마커를 작성하는 getInfoContents 및 getInfoWindow를 대체합니까? 예를 들어, 다른 종류의 마커를 추가하고 다른 방법을 사용하는 경우 다른 마커를 생성하는 각 메소드의 본문 내에서 재정의합니까?
Rarw

예, getInfoContents 및 getInfoWindow를 재정의합니다.
Zeeshan Mirza

그냥 이것을 시도하고 그것은 다른 XML 레이아웃을 팽창시키는 것과 같이 잘 작동합니다-좋은 일
Rarw

31
내 질문을 읽지 않은 것 같습니다. infowindow 내에서 사용자 정의보기를 만드는 방법을 알고 있습니다. 내가 모르는 것은 내부에 사용자 정의 버튼을 만들고 클릭 이벤트를 듣는 방법입니다.
user1943012

5
이 솔루션은 원래 질문에 대답하지 않습니다
biddulph.r
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.