Google Maps Android API v2 – 在地图上检测触摸
我无法find如何拦截新版Google Maps API v2上的地图触控的示例。
我需要知道用户何时触摸地图以停止线程(围绕我当前位置的地图的中心)。
@ape在这里写了一个关于如何拦截地图点击的答案,但是我需要拦截点击,然后他在其答案的评论中提示下面的链接, 如何在Google Map API v2中处理地图的onTouch事件? 。
该解决scheme似乎是一个可能的解决方法,但build议的代码是不完整的。 出于这个原因,我重写和testing,现在它的工作。
这是工作代码:
我创build了类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; } }
我甚至创build了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" />
只是在主要的活动testing我只写了以下内容:
public class MainActivity extends FragmentActivity { public static boolean mMapIsTouched = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }
这里是一个简单的解决scheme来获取基于用户select的位置(点击地图上的选项)
googleMap.setOnMapClickListener(new OnMapClickListener() { @Override public void onMapClick(LatLng arg0) { // TODO Auto-generated method stub Log.d("arg0", arg0.latitude + "-" + arg0.longitude); } });
此function和更多现在支持:)
这是开发者注意事项(问题4636):
2016年8月发布了一系列用于相机运动开始,正在进行和结束事件的新型相机更换监听器。 您还可以看到摄像机为何在移动,无论是由用户手势,内置的APIanimation还是由开发人员控制的移动。 有关详细信息,请参阅相机更改事件指南: 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(); } }
我在布局的MapFragment顶部创build了一个空的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. } });
看到这个链接。 实现接口,并填写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 } }
高乔有一个很好的答案,看到我认为可能有一些需要另一个实现的许多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需要是TouchSupportMapFragmenttypes的,在layout xml中需要这一行:
<fragment class="de.bjornson.maps.TouchSupportMapFragment" ...
这是class级:
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); } } }
// 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 } } });
对于单声道爱好者:
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); } } }
@Gaucho MySupportMapFragment显然会被其他一些fargment或活动(可能有更多的视图元素比地图片段)使用。 那么如何将这个事件派发到下一个将被使用的片段。 我们是否需要重新编写一个界面来做到这一点?
我有一个更加简单的解决scheme,与TouchableWrapper
不同,它可以和最新版本的play-services-maps:10.0.1
。 此解决scheme仅使用地图事件,不使用自定义视图。 不使用已弃用的function,可能会支持多个版本。
首先,您需要一个标志variables,用于存储地图是由animation还是由用户input进行移动(此代码假设每个摄像机移动都不由animation触发)
GoogleMap googleMap; boolean movedByApi = false;
您的零碎或活动必须执行GoogleMap.OnMapReadyCallback
, GoogleMap.CancelableCallback
public class ActivityMap extends Activity implements OnMapReadyCallback, GoogleMap.CancelableCallback{ ... }
这迫使你在onMapReady
, onFinish
, onMapReady
上实现这些方法。 而onMapReady中的googleMap对象必须设置一个eventlistener来进行相机移动
@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; }
最后,如果你创build一个移动地图的通用函数的话
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); }
或者只是每次移动地图时,在animation之前激活标志
movedByApi = true; googleMap.animateCamera(cu, this);
我希望这有帮助!