如何使用适用于 Android V2 的 Google Maps 处理地图移动端?
Posted
技术标签:
【中文标题】如何使用适用于 Android V2 的 Google Maps 处理地图移动端?【英文标题】:How can I handle map move end using Google Maps for Android V2? 【发布时间】:2012-11-22 00:46:45 【问题描述】:我想在地图中心更改后立即对地址进行地理编码。
如何使用新的 android 版 Google 地图 V2 处理地图移动端? (我说的是用户用手指拖动地图的情况)
【问题讨论】:
onTouchEvent 方法对你没有帮助? 谷歌还没有这个内置的!疯了.. 【参考方案1】:查看新的地图 API。
@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();
developers.google.com sample
【讨论】:
完美解决方案!【参考方案2】:这是确定拖动开始和拖动结束事件的可能解决方法:
您必须扩展 SupportMapFragment 或 MapFragment。在 onCreateView 中,您必须将 MapView 包装在自定义的 FrameLayout 中(在下面的示例中,它是“TouchableWrapper”类),您可以在其中拦截触摸事件并识别地图是否被点击。如果您的“onCameraChange”被调用,只需检查地图视图是否被按下(在下面的示例中,这是变量“mMapIsTouched”)。
示例代码:
更新1:
在 getView() 中返回原始创建的视图 使用 dispatchTouchEvent() 代替 onInterceptTouchEvent()自定义框架布局:
private class TouchableWrapper extends FrameLayout
@Override
public boolean dispatchTouchEvent(MotionEvent ev)
switch (ev.getAction())
case MotionEvent.ACTION_DOWN:
mMapIsTouched = true;
break;
case MotionEvent.ACTION_UP:
mMapIsTouched = false;
break;
return super.dispatchTouchEvent(ev);
在您自定义的 MapFragment 中:
@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;
在您的相机更改回调方法中:
private final OnCameraChangeListener mOnCameraChangeListener =
new OnCameraChangeListener()
@Override
public void onCameraChange(CameraPosition cameraPosition)
if (!mMapIsTouched)
refreshClustering(false);
;
【讨论】:
嘿@Alexey Zakharov,请查看 Google 地图问题跟踪器中的主题:code.google.com/p/gmaps-api-issues/issues/… 确实如此。在您确定 ACTION_UP 事件后,不保证会调用 onCameraChanged()。无论如何,我也希望谷歌能尽快解决这个问题! @AZ13 我收到错误“MyMapFragment 类型的方法 getActivity() 未定义”。如何声明 MyMapFragment? @AZ13 我检查了您的实现,并且必须承认该事件仍未在每次地图移动时触发。查看demo project I prepared using your code。 很好,但你实际上不需要实现自定义的框架布局,只需在框架布局上做setOnTouchListener(new View.OnTouchListener() ...
并在其中添加事件处理代码。【参考方案3】:
过时请改用新的地图 API。查看 punksta 的回答。
在使用了上面AZ13的解决方案后,遇到了cmets中提到的问题,我创建了以下解决方案,可以更可靠地解决问题。但是,由于我在 onRelease 事件之后使用计时器来确定地图是否仍在动画中,因此此解决方案存在轻微延迟。
代码可以通过这个链接在 Github 上找到:https://github.com/MadsFrandsen/MapStateListener
我的解决方案可以通过以下方式从活动中使用:
new MapStateListener(mMap, mMapFragment, this)
@Override
public void onMapTouched()
// Map touched
@Override
public void onMapReleased()
// Map released
@Override
public void onMapUnsettled()
// Map unsettled
@Override
public void onMapSettled()
// Map settled
;
【讨论】:
对我来说似乎工作正常,这是对@Az13 解决方案的一个很好的增强:) @Mads Frandsen,可以分享您如何将您的库与 Marker setOnMarkerClickListener 结合起来?我尝试使用您的库,当按下任何标记时,代码将始终执行 onMapSettled 功能 @simeh 是的,我认为任何接触都会算作不安。您可能会在将其设置为 unsettled 之前检查是否有任何移动 - 这将避免 onMapSettled 调用,当它只是一个点击。如果到那时您还没有解决问题,我也许可以在本周末或下周末检查我的(旧)代码。 @Mads Frandsen,我设法通过将此库与另一个检测单点触摸的代码 sn-p 相结合来解决这个问题。感谢您的回复 很好,但是如果你可以将它上传到 Maven 或类似 jitpack.io 上会更好,这样我们就可以在 gradle 上使用它。【参考方案4】:我会尝试onCameraChangeListener。每次摄像机移动完成时都会调用监听器。侦听器还将为您提供新位置。在我的测试中,监听器在拖动过程中经常被调用,也许有更好的解决方案。
【讨论】:
我找到了那个方法,但正如你所说,它经常触发。我只需要在用户停止拖动地图并放下手指时进行地理编码。所以看来我也需要自己处理触摸。但是新地图 api 中没有 onTouch 处理程序。 @AlexeyZakharov 是对的,它被触发太频繁导致动画卡住 AZ13 的答案非常好。【参考方案5】:最简单的方法是使用 setOnCameraIdleListener 方法来处理地图片段上触摸侦听器的移动结束状态。 请看下面的例子:
mMap.setOnCameraMoveStartedListener(new GoogleMap.OnCameraMoveStartedListener()
@Override
public void onCameraMoveStarted(int i)
mapPin.startAnimation(animZoomOut);
);
mMap.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener()
@Override
public void onCameraIdle()
mapPin.startAnimation(animZoomIn);
);
【讨论】:
【参考方案6】:从 play-services-maps 9.4.0 开始,您可以简单地使用 GoogleMap.OnCameraMoveStartedListener
、GoogleMap.OnCameraMoveListener
和 GoogleMap.OnCameraIdleListener
。
如果出于某种原因,您想使用现在已弃用的旧 API,您可以使用 onCameraChangeListener
。但你必须注意两件事:
onCameraChange()
可能会在您拖动地图时被调用多次或仅调用一次(在拖动停止时)。
onCameraChange()
的最后一次调用中的摄像头位置可能与最终摄像头位置略有不同。
以下代码将这两个问题都考虑在内:
private static final int MESSAGE_ID_SAVE_CAMERA_POSITION = 1;
private static final int MESSAGE_ID_READ_CAMERA_POSITION = 2;
private CameraPosition lastCameraPosition;
private Handler handler;
private GoogleMap googleMap;
public void onMapReady(GoogleMap theGoogleMap)
googleMap = theGoogleMap;
handler = new Handler()
public void handleMessage(Message msg)
if (msg.what == MESSAGE_ID_SAVE_CAMERA_POSITION)
lastCameraPosition = googleMap.getCameraPosition();
else if (msg.what == MESSAGE_ID_READ_CAMERA_POSITION)
if (lastCameraPosition.equals(googleMap.getCameraPosition()))
Log.d(LOG, "Camera position stable");
;
googleMap.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener()
@Override
public void onCameraChange(CameraPosition cameraPosition)
handler.removeMessages(MESSAGE_ID_SAVE_CAMERA_POSITION);
handler.removeMessages(MESSAGE_ID_READ_CAMERA_POSITION);
handler.sendEmptyMessageDelayed(MESSAGE_ID_SAVE_CAMERA_POSITION, 300);
handler.sendEmptyMessageDelayed(MESSAGE_ID_READ_CAMERA_POSITION, 600);
);
【讨论】:
【参考方案7】:On camera idle is what you should use now
googleMap.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener()
@Override
public void onCameraIdle()
//Called when camera movement has ended, there are no pending animations and the user has stopped interacting with the map.
);
【讨论】:
【参考方案8】:只要用户拖动地图,我就必须将我的标记设置为居中。我已经使用 Stas Shakirov 回答
实现了它MapDragListenerFragment.class
public class MapDragListenerFragment extends Fragment implements OnMapReadyCallback, GoogleMap.OnMapLoadedCallback
private Context mContext;
private SupportMapFragment supportMapFragment;
private Marker centerMarker;
private LatLng mapCenterLatLng;
private TextView tvLocationName;
private Button btnFinalizeDestination;
private GoogleMap mGoogleMap;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
return inflater.inflate(R.layout.fragment_map_drag_listener, container, false);
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState)
super.onViewCreated(view, savedInstanceState);
mContext = getActivity();
tvLocationName = (TextView) view.findViewById(R.id.tv_location_name);
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState)
super.onActivityCreated(savedInstanceState);
FragmentManager fm = getActivity().getSupportFragmentManager();//getChildFragmentManager();//
supportMapFragment = (SupportMapFragment) fm.findFragmentById(R.id.map_container);
if (supportMapFragment == null)
//// FIXME: 2/13/2017 crashes at casting to TouchableMapFragment
supportMapFragment = SupportMapFragment.newInstance();
fm.beginTransaction().replace(R.id.map_container, supportMapFragment).commit();
supportMapFragment.getMapAsync(this);
@Override
public void onMapReady(GoogleMap googleMap)
if (googleMap != null)
mGoogleMap = googleMap;
centerMarker = mGoogleMap.addMarker(new MarkerOptions().position(mGoogleMap.getCameraPosition().target)
.title("Center of Map")
.icon(BitmapDescriptorFactory.fromResource(R.drawable.end_green)));
mGoogleMap.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener()
@Override
public void onCameraIdle()
mapCenterLatLng = mGoogleMap.getCameraPosition().target;
animateMarker(centerMarker,mapCenterLatLng,false);
Toast.makeText(mContext, "The camera has stopped moving.",
Toast.LENGTH_SHORT).show();
String address = getCompleteAddressString(mapCenterLatLng.longitude,mapCenterLatLng.longitude);
tvLocationName.setText(address);
);
mGoogleMap.setOnCameraMoveStartedListener(new GoogleMap.OnCameraMoveStartedListener()
@Override
public void onCameraMoveStarted(int reason)
if (reason == GoogleMap.OnCameraMoveStartedListener.REASON_GESTURE)
///tvLocationName.setText("Lat " + mapCenterLatLng.latitude + " Long :" + mapCenterLatLng.longitude);
Toast.makeText(mContext, "The user gestured on the map.",
Toast.LENGTH_SHORT).show();
else if (reason == GoogleMap.OnCameraMoveStartedListener
.REASON_API_ANIMATION)
Toast.makeText(mContext, "The user tapped something on the map.",
Toast.LENGTH_SHORT).show();
else if (reason == GoogleMap.OnCameraMoveStartedListener
.REASON_DEVELOPER_ANIMATION)
Toast.makeText(mContext, "The app moved the camera.",
Toast.LENGTH_SHORT).show();
);
mGoogleMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener()
@Override
public void onCameraMove()
Toast.makeText(mContext, "The camera is moving.",
Toast.LENGTH_SHORT).show();
);
mGoogleMap.setOnCameraMoveCanceledListener(new GoogleMap.OnCameraMoveCanceledListener()
@Override
public void onCameraMoveCanceled()
Toast.makeText(mContext, "Camera movement canceled.",
Toast.LENGTH_SHORT).show();
);
mapCenterLatLng = mGoogleMap.getCameraPosition().target;// it should be done on MapLoaded.
if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION) !=
PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_COARSE_LOCATION) !=
PackageManager.PERMISSION_GRANTED)
return;
mGoogleMap.setMyLocationEnabled(true);
mGoogleMap.animateCamera(CameraUpdateFactory.zoomTo(15));
mGoogleMap.setOnMapLoadedCallback(this);
mGoogleMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener()
@Override
public void onCameraMove()
);
public void animateMarker(final Marker marker, final LatLng toPosition,
final boolean hideMarker)
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
Projection proj = mGoogleMap.getProjection();
Point startPoint = proj.toScreenLocation(marker.getPosition());
final LatLng startLatLng = proj.fromScreenLocation(startPoint);
final long duration = 500;
final Interpolator interpolator = new LinearInterpolator();
handler.post(new Runnable()
@Override
public void run()
long elapsed = SystemClock.uptimeMillis() - start;
float t = interpolator.getInterpolation((float) elapsed
/ duration);
double lng = t * toPosition.longitude + (1 - t)
* startLatLng.longitude;
double lat = t * toPosition.latitude + (1 - t)
* startLatLng.latitude;
marker.setPosition(new LatLng(lat, lng));
if (t < 1.0)
// Post again 16ms later.
handler.postDelayed(this, 16);
else
if (hideMarker)
marker.setVisible(false);
else
marker.setVisible(true);
);
在哪里 fragment_map_drag_listener.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_
android:orientation="vertical">
<RelativeLayout
android:layout_
android:layout_
android:layout_weight="1">
<fragment
android:id="@+id/map_container"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_
android:layout_ />
<ImageView
android:id="@+id/iv_center_overlay"
android:layout_
android:layout_
android:visibility="gone"
android:layout_centerInParent="true"
android:src="@drawable/start_blue" />
</RelativeLayout>
<TextView
android:id="@+id/tv_location_name"
android:layout_
android:layout_
android:padding="4dp"
android:text="Location Name" />
</LinearLayout>
在哪里 MapDragListenerActivity
public class MapDragListenerActivity extends AppCompatActivity
private Context mContext;
private static final String TAG = MapDragListenerFragment.class.getSimpleName();
private MapDragListenerFragment mapDragListenerFragment;
private Button selectPlaceBtn;
public static final int PLACE_AUTOCOMPLETE_REQUEST_CODE = 1219;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map_drag_listener);
mContext = MapDragListenerActivity.this;
mapDragListenerFragment = new MapDragListenerFragment();
getSupportFragmentManager().beginTransaction()
.replace(R.id.frame_container,//where frame_container is a FrameLayout
mapDragListenerFragment,
MapyFragment.class.getSimpleName()).commit();
selectPlaceBtn = (Button) findViewById(R.id.btn_select_place);
selectPlaceBtn.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
try
Intent intent = new PlaceAutocomplete.IntentBuilder(
PlaceAutocomplete.MODE_FULLSCREEN).build(MapDragListenerActivity.this);
startActivityForResult(intent, PLACE_AUTOCOMPLETE_REQUEST_CODE);
catch (GooglePlayServicesRepairableException e)
e.printStackTrace();
catch (GooglePlayServicesNotAvailableException e)
e.printStackTrace();
);
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == PLACE_AUTOCOMPLETE_REQUEST_CODE)
if (resultCode == RESULT_OK)
Place place = PlaceAutocomplete.getPlace(mContext, data);
if(mapDragListenerFragment != null && mapDragListenerFragment.isVisible())
mapDragListenerFragment.updateMarkerAtPosition(
place.getLatLng() ,place.getName().toString());
Log.i(TAG, "Place:" + place.toString());
else if (resultCode == PlaceAutocomplete.RESULT_ERROR)
Status status = PlaceAutocomplete.getStatus(mContext, data);
Log.i(TAG, status.getStatusMessage());
else if (requestCode == RESULT_CANCELED)
activity_map_drag_listener.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_
android:orientation="vertical">
<Button
android:id="@+id/btn_select_place"
android:layout_
android:layout_
android:text="Select Place" />
<FrameLayout
android:id="@+id/frame_container"
android:layout_
android:layout_ />
</LinearLayout>
【讨论】:
【参考方案9】:@Override
public boolean onTouchEvent(MotionEvent event, MapView mapView)
if(event.getAction() == MotionEvent.ACTION_MOVE)
return true;
return false;
【讨论】:
这个问题被标记为低质量,因为如果它的长度和内容。您能否补充说明这是如何解决问题的,并可能引用所使用的功能?【参考方案10】:我认为地图中的onclick事件是:map.setOnMapClick... 但是 event drag 是: map.onCameraChangeListener 因为我在这两个函数中都调用了 log.e 并且它显示为 onClick 视图和 onDrag 视图。所以只是为你使用它们。
【讨论】:
【参考方案11】:在 Xamarin Android 中使用处理程序内部类的增强解决方案,基于 Tobus 答案:
public void OnMapReady(GoogleMap googleMap)
_googleMap = googleMap;
if (_googleMap != null)
_cameraPositionHandler = new CameraPositionlHandler(_googleMap);
_googleMap.CameraChange += OnCameraChanged;
void OnCameraChanged (object sender, GoogleMap.CameraChangeEventArgs e)
_cameraPositionHandler.RemoveMessages(MESSAGE_ID_SAVE_CAMERA_POSITION);
_cameraPositionHandler.RemoveMessages(MESSAGE_ID_READ_CAMERA_POSITION);
_cameraPositionHandler.SendEmptyMessageDelayed(MESSAGE_ID_SAVE_CAMERA_POSITION, 300);
_cameraPositionHandler.SendEmptyMessageDelayed(MESSAGE_ID_READ_CAMERA_POSITION, 600);
具有以下内部类:
private class CameraPositionlHandler : Handler
private CameraPosition _lastCameraPosition;
private GoogleMap _googleMap;
public CameraPositionlHandler (GoogleMap googleMap)
_googleMap = googleMap;
public override void HandleMessage(Message msg)
if (_googleMap != null)
if (msg.What == MESSAGE_ID_SAVE_CAMERA_POSITION)
_lastCameraPosition = _googleMap.CameraPosition;
else if (msg.What == MESSAGE_ID_READ_CAMERA_POSITION)
if (_lastCameraPosition.Equals(_googleMap.CameraPosition))
Console.WriteLine("Camera position stable");
//do what you want
【讨论】:
以上是关于如何使用适用于 Android V2 的 Google Maps 处理地图移动端?的主要内容,如果未能解决你的问题,请参考以下文章
适用于 ios OAuth2、API V2 的 LinkedIn 登录。如何打开已安装的 LinkedIn 应用程序
Google Maps Android api v2 和当前位置
如何在 Docusaurus v2 中嵌入全局 React 组件?
使用地理编码器和 android Google Maps API v2 获取纬度和经度