API Google Maps Android API v2 - InfoWindow tương tác (như trong bản đồ google gốc của Android)


191

Tôi đang cố gắng thực hiện tùy chỉnh InfoWindowsau khi nhấp vào điểm đánh dấu bằng Google Maps API v2 mới. Tôi muốn nó trông giống như trong ứng dụng bản đồ gốc của Google. Như thế này:

Hình ảnh ví dụ

Khi tôi có ImageButtonbên trong, nó không hoạt động - toàn bộ InfoWindowbị trượt và không chỉ ImageButton. Tôi đọc được rằng đó là vì không cóView chính nó nhưng nó là ảnh chụp nhanh, vì vậy các mục riêng lẻ không thể phân biệt được với nhau.

EDIT: Trong tài liệu (nhờ vào Disco S2 ):

Như đã đề cập trong phần trước trên các cửa sổ thông tin, cửa sổ thông tin không phải là Chế độ xem trực tiếp, thay vào đó, chế độ xem được hiển thị dưới dạng hình ảnh trên bản đồ. Do đó, bất kỳ trình nghe nào bạn đặt trên chế độ xem đều bị bỏ qua và bạn không thể phân biệt giữa các sự kiện nhấp chuột trên các phần khác nhau của chế độ xem. Bạn nên không đặt các thành phần tương tác - chẳng hạn như các nút, hộp kiểm hoặc nhập văn bản - trong cửa sổ thông tin tùy chỉnh của bạn.

Nhưng nếu Google sử dụng nó, phải có một số cách để tạo ra nó. Có ai có ý kiến ​​gì không?


"Nó không hoạt động" không phải là một mô tả đặc biệt hữu ích về các triệu chứng của bạn. Đây là một dự án mẫu cho thấy có một cửa sổ thông tin tùy chỉnh với một hình ảnh: github.com/commonsguy/cw-omnibus/tree/master/MapsV2/Popups
CommonsWare

8
@CommonsWare tôi đã viết lý do đó. Từ tài liệu gốc Lưu ý: Cửa sổ thông tin được vẽ không phải là chế độ xem trực tiếp. Chế độ xem được hiển thị dưới dạng hình ảnh (sử dụng View.draw(Canvas)) ... Điều này có nghĩa là mọi thay đổi tiếp theo đối với chế độ xem sẽ không được phản ánh bởi cửa sổ thông tin trên bản đồ. Để cập nhật cửa sổ thông tin sau này Ngoài ra, cửa sổ thông tin sẽ không tôn trọng bất kỳ tương tác nào điển hình cho chế độ xem bình thường, chẳng hạn như các sự kiện chạm hoặc cử chỉ. Tuy nhiên, bạn có thể nghe một sự kiện nhấp chuột chung trên toàn bộ cửa sổ thông tin như được mô tả trong phần bên dưới. ... trong ví dụ của bạn chỉ là textview
user1943012

Xin chào, làm thế nào bạn có được nút vị trí của tôi xuất hiện bên dưới thanh hành động khi ở chế độ toàn màn hình? Cảm ơn!
tkblackbelt

45
Tôi không nghĩ rằng câu hỏi này nên được đóng lại. Đó là một câu hỏi chính đáng cho một vấn đề tôi cũng gặp phải.
marienke

12
Tôi có thể thấy điều này rất thường xuyên, nhiều câu hỏi hữu ích nhất với câu trả lời đã bị đóng. Một câu hỏi có thể không phải lúc nào cũng phù hợp với các yêu cầu chính xác của một câu hỏi thích hợp nhưng nếu chúng ta có một VẤN ĐỀ được giải thích rõ và một TRẢ LỜI được giải thích tốt thì không có lý do gì để đóng câu hỏi. Mọi người sử dụng Stackoverflow để trao đổi kiến ​​thức và đóng một chuỗi câu hỏi rất tích cực và hiệu quả như "ngoài chủ đề" dường như không phải là một ý tưởng hay đối với tôi ..
John

Câu trả lời:


333

Tôi đã tự mình tìm kiếm một giải pháp cho vấn đề này mà không gặp may mắn, vì vậy tôi đã phải tự mình chia sẻ điều mà tôi muốn chia sẻ ở đây với bạn. (Xin thứ lỗi tiếng Anh không tốt của tôi) (Thật hơi điên khi trả lời một anh chàng người Séc khác bằng tiếng Anh :-))

Điều đầu tiên tôi đã cố gắng là sử dụng một cái cũ tốt PopupWindow. Điều này khá dễ dàng - người ta chỉ cần lắng nghe OnMarkerClickListenervà sau đó hiển thị một tùy chỉnh PopupWindowphía trên điểm đánh dấu. Một số người khác ở đây trên StackOverflow đã đề xuất giải pháp này và nó thực sự trông khá tốt ngay từ cái nhìn đầu tiên. Nhưng vấn đề với giải pháp này xuất hiện khi bạn bắt đầu di chuyển bản đồ xung quanh. Bạn phải PopupWindowtự mình di chuyển bằng cách nào đó có thể (bằng cách nghe một số sự kiện onTouch) nhưng IMHO bạn không thể làm cho nó trông đủ tốt, đặc biệt là trên một số thiết bị chậm. Nếu bạn làm theo cách đơn giản, nó sẽ "nhảy" từ điểm này sang điểm khác. Bạn cũng có thể sử dụng một số hình ảnh động để đánh bóng những bước nhảy đó, nhưng theo cách này PopupWindowsẽ luôn là "một bước phía sau" nơi nó sẽ xuất hiện trên bản đồ mà tôi không thích.

Tại thời điểm này, tôi đã suy nghĩ về một số giải pháp khác. Tôi nhận ra rằng tôi thực sự không cần nhiều sự tự do đó - để hiển thị các chế độ xem tùy chỉnh của tôi với tất cả các khả năng đi kèm với nó (như các thanh tiến trình hoạt hình, v.v.). Tôi nghĩ có một lý do chính đáng tại sao ngay cả các kỹ sư google cũng không làm theo cách này trong ứng dụng Google Maps. Tất cả những gì tôi cần là một hoặc hai nút trên InfoWindow sẽ hiển thị trạng thái nhấn và kích hoạt một số hành động khi nhấp vào. Vì vậy, tôi đã đưa ra một giải pháp khác được chia thành hai phần:

Phần đầu tiên:
Phần đầu tiên là có thể bắt các nhấp chuột vào các nút để kích hoạt một số hành động. Ý tưởng của tôi là như sau:

  1. Giữ một tham chiếu đến thông tin tùy chỉnhWindow được tạo trong InfoWindowAd CHƯƠNG.
  2. Gói MapFragment(hoặc MapView) bên trong một Viewgroup tùy chỉnh (của tôi được gọi là MapWrapperLayout)
  3. Ghi đè công văn MapWrapperLayout'touchEvent và (nếu InfoWindow hiện được hiển thị) trước tiên định tuyến MotionEvents đến InfoWindow được tạo trước đó. Nếu nó không tiêu thụ MotionEvents (như vì bạn đã không nhấp vào bất kỳ khu vực có thể nhấp nào trong InfoWindow, v.v.) thì (và chỉ sau đó) hãy để các sự kiện đi xuống siêu lớp của MapWrapperLayout để cuối cùng nó sẽ được gửi đến bản đồ.

Đây là mã nguồn của 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);
    }
}

Tất cả điều này sẽ làm cho các chế độ xem bên trong InfoView "trực tiếp" trở lại - OnClickListener sẽ bắt đầu kích hoạt, v.v.

Phần thứ hai: Vấn đề còn lại là, rõ ràng, bạn không thể thấy bất kỳ thay đổi giao diện người dùng nào của InfoWindow trên màn hình. Để làm điều đó, bạn phải gọi Marker.showInfoWindow theo cách thủ công. Bây giờ, nếu bạn thực hiện một số thay đổi vĩnh viễn trong InfoWindow của bạn (như thay đổi nhãn của nút của bạn thành một thứ khác), điều này là đủ tốt.

Nhưng hiển thị trạng thái nhấn nút hoặc một cái gì đó có tính chất đó phức tạp hơn. Vấn đề đầu tiên là, (ít nhất) tôi không thể làm cho InfoWindow hiển thị trạng thái nhấn nút bình thường. Ngay cả khi tôi nhấn nút trong một thời gian dài, nó vẫn không được nhấn trên màn hình. Tôi tin rằng đây là thứ được xử lý bởi chính khung bản đồ có thể đảm bảo không hiển thị bất kỳ trạng thái thoáng qua nào trong các cửa sổ thông tin. Nhưng tôi có thể sai, tôi đã không cố gắng tìm ra điều này.

Những gì tôi đã làm là một hack khó chịu khác - Tôi đã gắn một OnTouchListenernút và tự chuyển nền của nó khi nút được nhấn hoặc nhả ra hai ngăn kéo tùy chỉnh - một nút có trạng thái bình thường và nút còn lại ở trạng thái nhấn. Điều này không phải là rất tốt đẹp, nhưng nó hoạt động :). Bây giờ tôi đã có thể thấy nút chuyển đổi giữa trạng thái bình thường sang trạng thái ấn trên màn hình.

Vẫn còn một trục trặc cuối cùng - nếu bạn nhấp vào nút quá nhanh, nó sẽ không hiển thị trạng thái nhấn - nó vẫn duy trì ở trạng thái bình thường (mặc dù bản thân nhấp chuột được kích hoạt để nút "hoạt động"). Ít nhất đây là cách nó hiển thị trên Galaxy Nexus của tôi. Vì vậy, điều cuối cùng tôi đã làm là tôi trì hoãn nút ở trạng thái nhấn một chút. Điều này cũng khá xấu và tôi không chắc nó sẽ hoạt động như thế nào trên một số thiết bị cũ, chậm nhưng tôi nghi ngờ rằng ngay cả khung bản đồ cũng làm điều tương tự. Bạn có thể tự mình thử - khi bạn nhấp vào toàn bộ InfoWindow, nó vẫn ở trạng thái nhấn lâu hơn một chút, sau đó các nút bình thường sẽ làm (một lần nữa - ít nhất là trên điện thoại của tôi). Và đây thực sự là cách nó hoạt động ngay cả trên ứng dụng Google Maps gốc.

Dù sao, tôi đã viết cho mình một lớp tùy chỉnh xử lý các thay đổi trạng thái của nút và tất cả những thứ khác tôi đã đề cập, vì vậy đây là mã:

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

Dưới đây là tệp bố cục InfoWindow tùy chỉnh mà tôi đã sử dụng:

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

Kiểm tra tập tin bố trí hoạt động ( MapFragmentnằm bên trong 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>

Và cuối cùng là mã nguồn của một hoạt động thử nghiệm, kết hợp tất cả những điều này lại với nhau:

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

Đó là nó. Cho đến nay tôi chỉ thử nghiệm điều này trên Galaxy Nexus (4.2.1) và Nexus 7 (cũng là 4.2.1), tôi sẽ thử nó trên một số điện thoại Gingerbread khi tôi có cơ hội. Một hạn chế tôi tìm thấy cho đến nay là bạn không thể kéo bản đồ từ đâu là nút của bạn trên màn hình và di chuyển bản đồ xung quanh. Nó có thể được khắc phục bằng cách nào đó nhưng bây giờ, tôi có thể sống với điều đó.

Tôi biết đây là một hack xấu xí nhưng tôi không tìm thấy gì tốt hơn và tôi cần mẫu thiết kế này tệ đến mức đây thực sự là một lý do để quay trở lại khung bản đồ v1 (mà btw. Tôi thực sự muốn tránh cho một ứng dụng mới với các đoạn, v.v.). Tôi chỉ không hiểu tại sao Google không cung cấp cho nhà phát triển một số cách chính thức để có nút trên InfoWindows. Đây là một mẫu thiết kế phổ biến như vậy, hơn nữa mẫu này được sử dụng ngay cả trong ứng dụng Google Maps chính thức :). Tôi hiểu lý do tại sao họ không thể khiến chế độ xem của bạn "sống" trong InfoWindows - điều này có thể sẽ giết chết hiệu suất khi di chuyển và cuộn bản đồ xung quanh. Nhưng nên có một số cách để đạt được hiệu ứng này mà không cần sử dụng lượt xem.


6
giải pháp thú vị. Tôi muốn thử cái này. Buổi biểu diễn thế nào?
Patrick

10
cần phải thay thế view.setBackground(bgDrawablePressed);với if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { view.setBackgroundDrawable(bgDrawablePressed); }else{ view.setBackground(bgDrawablePressed); }công việc trong Android Gingerbread, Lưu ý: có một lần xuất hiện nhiều hơn của view.setBackgroundtrong mã.
Balaji

4
KK_07k11A0585: Có vẻ như việc triển khai InfoWindowAdOG của bạn có thể sai. Có hai phương thức để ghi đè: getInfoWindow () và getInfoContents (). Khung công tác trước tiên gọi phương thức getInfoWindow () và (chỉ) nếu nó trả về null, nó gọi hàm getInfoContents (). Nếu bạn trả về một số Chế độ xem từ getInfoWindow, khung sẽ đặt nó bên trong khung cửa sổ thông tin mặc định - trong trường hợp bạn không muốn. Vì vậy, chỉ cần trả về null từ getInfoWindow và trả về cho bạn Xem từ getInfoContent và nó sẽ ổn sau đó.
chọn007

18
Nếu có ai quan tâm, tôi đang sử dụng giải pháp này trong ứng dụng của riêng tôi - CG Transit (bạn có thể tìm thấy nó trên Google Play). Sau hai tháng, nó đã được sử dụng bởi hàng chục ngàn người dùng và chưa có ai phàn nàn về nó.
chọn007

2
Làm thế nào để bạn xác định phần bù dưới cùng nếu bạn không sử dụng một infowindow tiêu chuẩn?
Rarw

12

Tôi thấy rằng câu hỏi này đã cũ nhưng vẫn ...

Chúng tôi đã tạo một thư viện sipmle tại công ty của chúng tôi để đạt được những gì mong muốn - Một cửa sổ thông tin tương tác với các khung nhìn và mọi thứ. Bạn có thể kiểm tra nó trên github .

Tôi hy vọng nó sẽ giúp :)


Có thể sử dụng nó với cụm?
gcolucci

9

Đây là vấn đề của tôi. Tôi tạo AbsoluteLayoutlớp phủ có chứa Cửa sổ thông tin (chế độ xem thông thường với mỗi bit khả năng tương tác và vẽ). Sau đó tôi bắt đầuHandler đồng bộ hóa vị trí của cửa sổ thông tin với vị trí điểm trên bản đồ cứ sau 16 ms. Nghe có vẻ điên rồ, nhưng thực sự hoạt động.

Video giới thiệu: https://www.youtube.com/watch?v=bT9RpH4p9mU (tính đến hiệu suất bị giảm do trình giả lập và quay video chạy đồng thời).

Mã của bản demo: https://github.com/deville/info-window-demo

Một bài viết cung cấp chi tiết (bằng tiếng Nga): http://habrahabr.ru/post/213415/


Điều này sẽ làm chậm / lag cuộn bản đồ
Shane Ekanayake

2

Đối với những người không thể nhận được choose007'scâu trả lời và chạy

Nếu clickListenerkhông hoạt động đúng mọi lúc trong chose007'sgiải pháp, hãy thử thực hiện View.onTouchListenerthay vì clickListener. Xử lý sự kiện chạm bằng cách sử dụng bất kỳ hành động ACTION_UPhoặc ACTION_DOWN. Vì một số lý do, bản đồ infoWindowgây ra một số hành vi kỳ lạ khi gửi đến 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;
          }

Chỉnh sửa: Đây là cách tôi từng bước thực hiện

Đầu tiên thổi phồng infowindow của bạn (biến toàn cục) ở đâu đó trong hoạt động / đoạn của bạn. Của tôi là trong mảnh vỡ. Ngoài ra, hãy đảm bảo rằng chế độ xem gốc trong bố cục infowindow của bạn là linearlayout (vì một số lý do, relat khóayout chiếm toàn bộ chiều rộng màn hình trong 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;

Sau đó, trong cuộc gọi lại onMapReady của google maps android api (hãy làm theo điều này nếu bạn không biết onMapReady là Bản đồ> Tài liệu - Bắt đầu )

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

MapWrapperLayoutkhởi tạo http://stackoverflow.com/questions/14123243/google-maps-android-api-v2- tương tác-infowindow-like-in-original-android-go / 15040761 # 15040761 39 - chiều cao điểm đánh dấu mặc định 20 - bù giữa mặc định InfoWindow cạnh dưới và đó là cạnh dưới nội dung * /

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

Phương thức 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;
            }
        });

Xử lý đánh dấu bấm riêng

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

bạn có thể cho tôi xem mã của bạn? Tôi đã có thể đăng nhập các sự kiện chạm thành công nhưng không nhấp vào các sự kiện vì vậy tôi đã thực hiện việc này. Ngoài ra, bạn cần triển khai giải pháp select007 đúng cách.
Manish Jangid

Tôi đã chia sẻ thực hiện đầy đủ của tôi. Hãy cho tôi biết nếu bạn có bất kỳ câu hỏi nào
Manish Jangid

0

Chỉ là suy đoán, tôi chưa có đủ kinh nghiệm để thử nó ...) -:

Vì GoogleMap là một đoạn, nên có thể bắt điểm đánh dấu trên sự kiện và hiển thị chế độ xem đoạn tùy chỉnh . Một mảnh bản đồ sẽ vẫn được nhìn thấy trên nền. Có ai đã thử nó? Bất kỳ lý do tại sao nó không thể làm việc?

Nhược điểm là đoạn bản đồ sẽ bị đóng băng trên backgroud, cho đến khi một đoạn thông tin tùy chỉnh trả lại cho nó.


5
Tôi không thể nghĩ rằng thật thú vị khi đăng một suy đoán như một câu trả lời. Có lẽ nó nên là một nhận xét về câu hỏi ban đầu. Chỉ là suy nghĩ của tôi.
Johan Karlsson

1
Thư viện này github.com/Appolica/InteractiveInfoWindowAndroid thực hiện một điều tương tự
Deyan Genovski

-2

Tôi đã xây dựng một dự án studio android mẫu cho câu hỏi này.

ảnh chụp màn hình đầu ra: -

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

Tải về mã nguồn dự án đầy đủ Bấm vào đây

Xin lưu ý: bạn phải thêm khóa API của mình trong Androidmanifest.xml


5
Tốt hơn là chia sẻ mã của bạn thông qua Github / gist thay vì tệp zip. Vì vậy, chúng tôi có thể trực tiếp kiểm tra ở đó.
Noundla Sandeep

Một lỗi nhỏ tôi tìm thấy, khi bạn nhấn vào nút nào đó, tất cả các nút được nhấp.
Morozov

3
@Noundla và những người khác: đừng bận tâm tải xuống mã zip của anh ấy. Đây là một bản sao chính xác của câu trả lời được chấp nhận cho câu hỏi này.
Ganesh Mohan

1
@ ganesh2shiv đúng, mã nén là vô dụng. Bạn không nên đính kèm bản sao đã dán mã
Onkar Nene

-7

Nó thực sự đơn giản.

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;

            }

        });

Chỉ cần thêm mã ở trên trong lớp của bạn, nơi bạn đang sử dụng GoogleMap. R.layout.info_window_layout là bố cục tùy chỉnh của chúng tôi đang hiển thị chế độ xem sẽ thay thế cho infowindow. Tôi chỉ cần thêm các textview ở đây. Bạn có thể thêm chế độ xem bổ sung tại đây để làm cho nó giống như ảnh chụp mẫu. Thông tin của tôi_window_layout là

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

Tôi mong nó sẽ có ích. Chúng tôi có thể tìm thấy một ví dụ hoạt động của infowindow tùy chỉnh tại http://wptrafficanalyzer.in/blog/customizing-infowindow-contents-in-google-map-android-api-v2-USE-infowindowadad/#comment-39431

EDITED: Mã này cho thấy cách chúng ta có thể thêm chế độ xem tùy chỉnh trên infoWindow. Mã này đã không xử lý các nhấp chuột vào các mục Xem tùy chỉnh. Vì vậy, nó gần để trả lời nhưng không chính xác câu trả lời đó là lý do tại sao Nó không được chấp nhận là câu trả lời.


Bạn có ghi đè getInfoContents và getInfoWindow nơi bạn tạo các điểm đánh dấu không? Ví dụ: nếu tôi thêm các loại dấu khác nhau và sử dụng các phương thức khác nhau, tôi có thể ghi đè trong phần thân của từng phương thức mà tôi tạo các dấu khác nhau không?
Rarw

Có, tôi ghi đè getInfoContents và getInfoWindow.
Zeeshan Mirza

Chỉ cần thử điều này và nó hoạt động tốt như thổi phồng bất kỳ bố cục xml nào khác - công việc tốt
Rarw

31
Có vẻ như bạn đã không đọc câu hỏi của tôi. Tôi biết cách tạo chế độ xem tùy chỉnh bên trong infowindow. Mà tôi không biết là làm thế nào để tạo nút tùy chỉnh bên trong và lắng nghe các sự kiện nhấp chuột của nó.
dùng1943012

5
Giải pháp này không trả lời câu hỏi ban đầu
biddulph.r
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.