2012-12-23 111 views
59

我無法找到關於如何攔截新版Google地圖API v2上的地圖觸摸的示例。谷歌地圖Android API v2 - 在地圖上檢測觸摸

我需要知道用戶何時觸摸地圖才能停止線程(圍繞我當前位置的地圖中心)。

+3

如果有人回答您的問題,請將您的問題標記爲已回答。此外,你明確地說'在地圖上點擊',所以不需要在ape或CommonsWare中捕捉不能讀懂你的想法。 – Maarten

+1

我甚至可以將它標記爲已回答,但我寫了「地圖觸摸」,而不是「單擊」地圖。評論中的@ape提示瞭解決我的問題的另一個線程(http:// stackoverflow。com/questions/13722869/how-to-handle-ontouch-event-for-map-in-google-map-api-v2),但我無法使用它,因爲我在評論中寫道。我無法在鏈接上的線程上獲得解決方案。我應該打開另一個問題嗎? – Gaucho

+0

你的答案應該是一個答案,而不是編輯成問題。你已經很難遵循。如果你自己的答案是最能幫助你的答案,那麼你甚至可以接受它來向其他人展示。 –

回答

85

@ape這裏就如何截取地圖點擊寫一個答案,但我需要攔截觸摸,然後他建議在它的答案,How to handle onTouch event for map in Google Map API v2?的評論下面的鏈接。

該解決方案似乎是一種可能的解決方法,但建議的代碼不完整。出於這個原因,我重寫並測試了它,現在它可以工作。

這是工作代碼:

我創建的類MySupportMapFragment.java

import com.google.android.gms.maps.SupportMapFragment; 
import android.os.Bundle; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 

public class MySupportMapFragment extends SupportMapFragment { 
    public View mOriginalContentView; 
    public TouchableWrapper mTouchView; 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { 
     mOriginalContentView = super.onCreateView(inflater, parent, savedInstanceState); 
     mTouchView = new TouchableWrapper(getActivity()); 
     mTouchView.addView(mOriginalContentView); 
     return mTouchView; 
    } 

    @Override 
    public View getView() { 
     return mOriginalContentView; 
    } 
} 

我甚至創建的類TouchableWrapper.java:

import android.content.Context; 
import android.view.MotionEvent; 
import android.widget.FrameLayout; 

public class TouchableWrapper extends FrameLayout { 

    public TouchableWrapper(Context context) { 
     super(context); 
    } 

    @Override 
    public boolean dispatchTouchEvent(MotionEvent event) { 

     switch (event.getAction()) { 

      case MotionEvent.ACTION_DOWN: 
        MainActivity.mMapIsTouched = true; 
        break; 

      case MotionEvent.ACTION_UP: 
        MainActivity.mMapIsTouched = false; 
        break; 
     } 
     return super.dispatchTouchEvent(event); 
    } 
} 

在我宣佈佈局它這樣:

<fragment xmlns:android="http://schemas.android.com/apk/res/android" 
      android:id="@+id/mapFragment" 
      android:layout_width="match_parent" 
      android:layout_height="wrap_content" 
      android:layout_alignParentBottom="true" 
      android:layout_below="@+id/buttonBar" 
      class="com.myFactory.myApp.MySupportMapFragment" 
/> 

就在我只寫了主要業務測試以下:

public class MainActivity extends FragmentActivity { 
    public static boolean mMapIsTouched = false; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 
    } 
} 
+0

優秀的完成你所包含的鏈接 – aez

+1

投了票,因爲這不是用戶需要的,但這就是他所要求的。這就是我所需要的:) –

+0

@Gaucho,我不得不改進一點該解決方案,主要是通過使用自定義監聽器而不是使用公共靜態變量。查看下面的解決方案。 –

5

https://developers.google.com/maps/documentation/android/reference/com/google/android/gms/maps/GoogleMap.OnMapClickListener

請參閱此鏈接。實施接口並填寫onMapClick()方法或您需要的任何一種,並將onMapClickListener設置爲正確的實施方式。

public class YourActivity extends Activity implements OnMapClickListener { 
    @Override 
    protected void onCreate(Bundle icicle) { 
     super.onCreate(icicle); 
     ... 
     my_map.setOnMapClickListener(this)   
     ... 
    } 

    public void onMapClick (LatLng point) { 
     // Do Something 
    } 
} 
+0

非常感謝ndsmyter提供的答案。 onMapClick在地圖上點擊時截獲,但當您在地圖上移動手指時,它不起作用。我不僅需要攔截地圖點擊,還要攔截地圖平移。你知道該怎麼辦? – Gaucho

+0

檢查文檔..這不是你的點擊事件 – adarsh

+1

地圖觸摸不是「地圖點擊」,所以這個問題沒有回答。我需要攔截由於用戶在地圖上的觸摸而導致的地圖移動,並且我無法找到攔截此操作的工作方式。我認爲我無法使用setOnCameraChangeListener,因爲我仍然使用animateCamera方法更新代碼中的相機位置,然後我只需要一個偵聽器在地圖平移過程中攔截地圖上的觸摸。 – Gaucho

1

對於Mono愛好者:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using Android.App; 
using Android.Content; 
using Android.OS; 
using Android.Runtime; 
using Android.Util; 
using Android.Views; 
using Android.Widget; 
using Android.Gms.Maps; 

namespace apcurium.MK.Booking.Mobile.Client.Controls 
{ 
    public class TouchableMap : SupportMapFragment 
    { 
     public View mOriginalContentView; 

     public TouchableWrapper Surface; 

     public override View OnCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) 
     { 
      mOriginalContentView = base.OnCreateView(inflater, parent, savedInstanceState); 
      Surface = new TouchableWrapper(Activity); 
      Surface.AddView(mOriginalContentView); 
      return Surface; 
     } 

     public override View View 
     { 
      get 
      { 
       return mOriginalContentView; 
      } 
     } 
    } 

    public class TouchableWrapper: FrameLayout { 

     public event EventHandler<MotionEvent> Touched; 

     public TouchableWrapper(Context context) : 
     base(context) 
     { 
     } 

     public TouchableWrapper(Context context, IAttributeSet attrs) : 
     base(context, attrs) 
     { 
     } 

     public TouchableWrapper(Context context, IAttributeSet attrs, int defStyle) : 
     base(context, attrs, defStyle) 
     { 
     } 

     public override bool DispatchTouchEvent(MotionEvent e) 
     { 
      if (this.Touched != null) 
      { 
       this.Touched(this, e); 
      } 

      return base.DispatchTouchEvent(e); 
     } 
    } 
} 
38

這裏有一個簡單的解決方案根據用戶的選擇來獲得位置(點擊地圖選項)

googleMap.setOnMapClickListener(new OnMapClickListener() { 

      @Override 
      public void onMapClick(LatLng arg0) { 
       // TODO Auto-generated method stub 
       Log.d("arg0", arg0.latitude + "-" + arg0.longitude); 
      } 
     }); 
+0

當您在地圖上平滑地觸摸時,此過程可以正常工作,但當您更加難以觸及地圖時,它會開始縮放,由於這個原因,不會調用MapClick方法。 –

+0

如果單擊標記,則根本不會被調用。 – zyamys

+1

@ Md.SajedulKarim可以使用googleMap.getUiSettings()來禁用所有的手勢。setAllGesturesEnabled(false);然後在重新啓用手勢後再聽那個水龍頭。 – Array

6

我在佈局中的MapFragment頂部創建了一個空的FrameLayout。然後我在這個視圖上設置一個onTouchListener,所以我知道地圖何時被觸摸,但返回false,以便觸摸傳遞到地圖上。

<FrameLayout 
    android:id="@+id/map_touch_layer" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" /> 

mapTouchLayer.setOnTouchListener(new View.OnTouchListener() { 
     @Override 
     public boolean onTouch(View v, MotionEvent event) { 
      Utils.logDebug(TAG, "Map touched!"); 
      timeLastTouched = System.currentTimeMillis(); 
      return false; // Pass on the touch to the map or shadow layer. 
     } 
    }); 
+0

這根本行不通。在ACTION_DOWN上返回false將導致ACTION_UP被中斷,並且不會爲其調用onTouch。 –

1
// Initializing 
    markerPoints = new ArrayList<LatLng>(); 

    // Getting reference to SupportMapFragment of the activity_main 
    SupportMapFragment sfm = (SupportMapFragment)getSupportFragmentManager().findFragmentById(R.id.map); 

    // Getting Map for the SupportMapFragment 
    map = sfm.getMap(); 

    // Enable MyLocation Button in the Map 
    map.setMyLocationEnabled(true); 

    // Setting onclick event listener for the map 
    map.setOnMapClickListener(new OnMapClickListener() { 

     @Override 
     public void onMapClick(LatLng point) { 

      // Already two locations 
      if(markerPoints.size()>1){ 
       markerPoints.clear(); 
       map.clear(); 
      } 

      // Adding new item to the ArrayList 
      markerPoints.add(point); 

      // Creating MarkerOptions 
      MarkerOptions options = new MarkerOptions(); 

      // Setting the position of the marker 
      options.position(point); 


      if(markerPoints.size()==1){ 
       options.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN)); 
      }else if(markerPoints.size()==2){ 
       options.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED)); 
      } 

      // Add new marker to the Google Map Android API V2 
      map.addMarker(options); 

      // Checks, whether start and end locations are captured 
      if(markerPoints.size() >= 2){ 
       LatLng origin = markerPoints.get(0); 
       LatLng dest = markerPoints.get(1); 

      //Do what ever you want with origin and dest 
      } 
     } 
    }); 
0

@Gaucho MySupportMapFragment顯然將通過一些其它fargment或活性(其中可能有更多的視圖元素比地圖片段)一起使用。那麼如何將這個事件派發到下一個將被使用的片段。我們是否需要重新編寫一個界面來做到這一點?

2

高科有很大的答案,看到許多upvotes我想可能有一些需要另一種實現方式:

我需要它使用監聽這樣我就可以在觸摸反應,並沒有不斷檢查它。

我把所有能像這樣使用一個類:

mapFragment.setNonConsumingTouchListener(new TouchSupportMapFragment.NonConsumingTouchListener() { 
    @Override 
    public void onTouch(MotionEvent motionEvent) { 
     switch (motionEvent.getActionMasked()) { 
      case MotionEvent.ACTION_DOWN: 
       // map is touched 
       break; 
      case MotionEvent.ACTION_UP: 
       // map touch ended 
       break; 
      default: 
       break; 
      // use more cases if needed, for example MotionEvent.ACTION_MOVE 
     } 
    } 
}); 

其中mapfragment必須類型TouchSupportMapFragment,並在佈局XML需要該行:

<fragment class="de.bjornson.maps.TouchSupportMapFragment" 
... 

這裏是這個類:

package de.bjornson.maps; 

import android.content.Context; 
import android.os.Bundle; 
import android.view.LayoutInflater; 
import android.view.MotionEvent; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.FrameLayout; 

import com.google.android.gms.maps.SupportMapFragment; 

public class TouchSupportMapFragment extends SupportMapFragment { 
    public View mOriginalContentView; 
    public TouchableWrapper mTouchView; 
    private NonConsumingTouchListener mListener; 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { 
     mOriginalContentView = super.onCreateView(inflater, parent, savedInstanceState); 
     mTouchView = new TouchableWrapper(getActivity()); 
     mTouchView.addView(mOriginalContentView); 
     return mTouchView; 
    } 

    @Override 
    public View getView() { 
     return mOriginalContentView; 
    } 

    public void setNonConsumingTouchListener(NonConsumingTouchListener listener) { 
     mListener = listener; 
    } 

    public interface NonConsumingTouchListener { 
     boolean onTouch(MotionEvent motionEvent); 
    } 

    public class TouchableWrapper extends FrameLayout { 

     public TouchableWrapper(Context context) { 
      super(context); 
     } 

     @Override 
     public boolean dispatchTouchEvent(MotionEvent event) { 
      if (mListener != null) { 
       mListener.onTouch(event); 
      } 
      return super.dispatchTouchEvent(event); 
     } 
    } 
} 
14

此功能和更多現在支持:)

這是開發人員注意事項(問題4636):

在2016年8月發佈引入了一組新的相機變化的聽衆中的攝像機運動的開始,持續和結束事件。您還可以瞭解相機爲何在移動,無論是由用戶手勢,內置API動畫還是由開發人員控制的移動。有關詳細信息,請參閱指南相機改變事件: https://developers.google.com/maps/documentation/android-api/events#camera-change-events

另外,請參見發行說明: https://developers.google.com/maps/documentation/android-api/releases#august_1_2016

這裏是從文檔頁面的代碼片段

public class MyCameraActivity extends FragmentActivity implements 
     OnCameraMoveStartedListener, 
     OnCameraMoveListener, 
     OnCameraMoveCanceledListener, 
     OnCameraIdleListener, 
     OnMapReadyCallback { 

    private GoogleMap mMap; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_my_camera); 

     SupportMapFragment mapFragment = 
      (SupportMapFragment) getSupportFragmentManager() 
        .findFragmentById(R.id.map); 
     mapFragment.getMapAsync(this); 
    } 

    @Override 
    public void onMapReady(GoogleMap map) { 
     mMap = map; 

     mMap.setOnCameraIdleListener(this); 
     mMap.setOnCameraMoveStartedListener(this); 
     mMap.setOnCameraMoveListener(this); 
     mMap.setOnCameraMoveCanceledListener(this); 

     // Show Sydney on the map. 
     mMap.moveCamera(CameraUpdateFactory 
       .newLatLngZoom(new LatLng(-33.87365, 151.20689), 10)); 
    } 

    @Override 
    public void onCameraMoveStarted(int reason) { 

     if (reason == OnCameraMoveStartedListener.REASON_GESTURE) { 
      Toast.makeText(this, "The user gestured on the map.", 
          Toast.LENGTH_SHORT).show(); 
     } else if (reason == OnCameraMoveStartedListener 
           .REASON_API_ANIMATION) { 
      Toast.makeText(this, "The user tapped something on the map.", 
          Toast.LENGTH_SHORT).show(); 
     } else if (reason == OnCameraMoveStartedListener 
           .REASON_DEVELOPER_ANIMATION) { 
      Toast.makeText(this, "The app moved the camera.", 
          Toast.LENGTH_SHORT).show(); 
     } 
    } 

    @Override 
    public void onCameraMove() { 
     Toast.makeText(this, "The camera is moving.", 
         Toast.LENGTH_SHORT).show(); 
    } 

    @Override 
    public void onCameraMoveCanceled() { 
     Toast.makeText(this, "Camera movement canceled.", 
         Toast.LENGTH_SHORT).show(); 
    } 

    @Override 
    public void onCameraIdle() { 
     Toast.makeText(this, "The camera has stopped moving.", 
         Toast.LENGTH_SHORT).show(); 
    } 
} 
0

我有一個更簡單的解決方案與TouchableWrapper不同,它適用於play-services-maps:10.0.1的最新版本。此解決方案僅使用地圖事件並且不使用自定義視圖。不使用已棄用的功能,並可能支持多個版本。

首先,你需要(asumes即不是由動畫都會觸發攝像機移動是由用戶觸發該代碼)

GoogleMap googleMap; 
boolean movedByApi = false; 
存儲如果地圖是由動畫或通過用戶輸入移動的標誌變量

你fragament或活動必須實現GoogleMap.OnMapReadyCallbackGoogleMap.CancelableCallback

public class ActivityMap extends Activity implements OnMapReadyCallback, GoogleMap.CancelableCallback{ 
    ... 
} 

,這迫使你實現方法onMapReadyonFinishonCancel。而在onMapReady GoogleMap對象必須設置一個事件偵聽相機移動

@Override 
public void onMapReady(GoogleMap mMap) { 
    //instantiate the map 
    googleMap = mMap; 

    [...] // <- set up your map 

    googleMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() { 
     @Override 
     public void onCameraMove() { 
      if (movedByApi) { 
       Toast.makeText(ActivityMap.this, "Moved by animation", Toast.LENGTH_SHORT).show(); 

       [...] // <-- do something whe you want to handle api camera movement 
      } else { 
       Toast.makeText(ActivityMap.this, "Moved by user", Toast.LENGTH_SHORT).show(); 

       [...] // <-- do something whe you want to handle user camera movement 
      } 
     } 
    }); 
} 
@Override 
public void onFinish() { 
    //is called when the animation is finished 
    movedByApi = false; 
} 
@Override 
public void onCancel() { 
    //is called when the animation is canceled (the user drags the map or the api changes to a ne position) 
    movedByApi = false; 
} 

最後它的尤爲明顯,如果你創建移動您移動地圖的地圖

public void moveMapPosition(CameraUpdate cu, boolean animated){ 
    //activate the flag notifying that the map is being moved by the api 
    movedByApi = true; 
    //if its not animated, just do instant move 
    if (!animated) { 
     googleMap.moveCamera(cu); 
     //after the instant move, clear the flag 
     movedByApi = false; 
    } 
    else 
     //if its animated, animate the camera 
     googleMap.animateCamera(cu, this); 
} 

或只是每次的通用功能,在動畫之前激活標誌

movedByApi = true; 
googleMap.animateCamera(cu, this); 

我希望這有助於!

+0

如果用戶在動畫中觸摸地圖,這不起作用。 –