我希望做一些类似于优步处理捏缩放事件的方式。无论您在屏幕上捏什么,它都会使地图居中并放大中心位置。有没有办法在地图片段上没有某种叠加的情况下做到这一点?或者我应该只禁用地图的事件,在地图片段上创建叠加层,并处理叠加层中的所有缩放/其他事件?
答案 0 :(得分:21)
我花了大约3天的时间在谷歌搜索后建立了完整的解决方案。我的回答是从https://stackoverflow.com/a/32734436/3693334编辑的。
public class CustomMapView extends MapView {
private int fingers = 0;
private GoogleMap googleMap;
private long lastZoomTime = 0;
private float lastSpan = -1;
private Handler handler = new Handler();
private ScaleGestureDetector scaleGestureDetector;
private GestureDetector gestureDetector;
public CustomMapView(Context context) {
super(context);
}
public CustomMapView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomMapView(Context context, AttributeSet attrs, int style) {
super(context, attrs, style);
}
public CustomMapView(Context context, GoogleMapOptions options) {
super(context, options);
}
public void init(GoogleMap map) {
scaleGestureDetector = new ScaleGestureDetector(getContext(), new ScaleGestureDetector.OnScaleGestureListener() {
@Override
public boolean onScale(ScaleGestureDetector detector) {
if (lastSpan == -1) {
lastSpan = detector.getCurrentSpan();
} else if (detector.getEventTime() - lastZoomTime >= 50) {
lastZoomTime = detector.getEventTime();
googleMap.animateCamera(CameraUpdateFactory.zoomBy(getZoomValue(detector.getCurrentSpan(), lastSpan)), 50, null);
lastSpan = detector.getCurrentSpan();
}
return false;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
lastSpan = -1;
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
lastSpan = -1;
}
});
gestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
disableScrolling();
googleMap.animateCamera(CameraUpdateFactory.zoomIn(), 400, null);
return true;
}
});
googleMap = map;
}
private float getZoomValue(float currentSpan, float lastSpan) {
double value = (Math.log(currentSpan / lastSpan) / Math.log(1.55d));
return (float) value;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
gestureDetector.onTouchEvent(ev);
switch (ev.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_POINTER_DOWN:
fingers = fingers + 1;
break;
case MotionEvent.ACTION_POINTER_UP:
fingers = fingers - 1;
break;
case MotionEvent.ACTION_UP:
fingers = 0;
break;
case MotionEvent.ACTION_DOWN:
fingers = 1;
break;
}
if (fingers > 1) {
disableScrolling();
} else if (fingers < 1) {
enableScrolling();
}
if (fingers > 1) {
return scaleGestureDetector.onTouchEvent(ev);
} else {
return super.dispatchTouchEvent(ev);
}
}
private void enableScrolling() {
if (googleMap != null && !googleMap.getUiSettings().isScrollGesturesEnabled()) {
handler.postDelayed(new Runnable() {
@Override
public void run() {
googleMap.getUiSettings().setAllGesturesEnabled(true);
}
}, 50);
}
}
private void disableScrolling() {
handler.removeCallbacksAndMessages(null);
if (googleMap != null && googleMap.getUiSettings().isScrollGesturesEnabled()) {
googleMap.getUiSettings().setAllGesturesEnabled(false);
}
}
}
并自定义MapFragment
public class CustomMapFragment extends Fragment {
CustomMapView view;
Bundle bundle;
GoogleMap map;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bundle = savedInstanceState;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_map, container, false);
view = (CustomMapView) v.findViewById(R.id.mapView);
view.onCreate(bundle);
view.onResume();
map = view.getMap();
view.init(map);
MapsInitializer.initialize(getActivity());
return v;
}
public GoogleMap getMap() {
return map;
}
@Override
public void onResume() {
super.onResume();
view.onResume();
}
@Override
public void onPause() {
super.onPause();
view.onPause();
}
@Override
public void onDestroy() {
super.onDestroy();
view.onDestroy();
}
@Override
public void onLowMemory() {
super.onLowMemory();
view.onLowMemory();
}
}
最后,在您的活动中:
....
<fragment
android:id="@+id/map"
class="yourpackage.CustomMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
...
我已经在Android 4.1(API 16)和后者上进行了测试,它运行良好且流畅。 (关于API&lt; 16,我没有任何设备可以测试)。
答案 1 :(得分:6)
这是MechEthan想到的代码。
首先,您必须检测叠加视图上的双击。
public class TouchableWrapper extends FrameLayout {
private final GestureDetector.SimpleOnGestureListener mGestureListener
= new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onDoubleTap(MotionEvent e) {
//Notify the event bus (I am using Otto eventbus of course) that you have just received a double-tap event on the map, inside the event bus event listener
EventBus_Singleton.getInstance().post(new EventBus_Poster("double_tapped_map"));
return true;
}
};
public TouchableWrapper(Context context) {
super(context);
mGestureDetector = new GestureDetectorCompat(context, mGestureListener);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
mGestureDetector.onTouchEvent(ev);
return super.onInterceptTouchEvent(ev);
}
}
无论您何时抓住mapView,请将mapView包装在上面创建的TouchableWrapper中。这就是我这样做的原因,因为我有一个问题需要将mapFragment添加到另一个片段中,所以我需要一个自定义的SupportMapFragment来执行此操作
public class CustomMap_Fragment extends SupportMapFragment {
TouchableWrapper mTouchView;
public CustomMap_Fragment() {
super();
}
public static CustomMap_Fragment newInstance() {
return new CustomMap_Fragment();
}
@Override
public View onCreateView(LayoutInflater arg0, ViewGroup arg1, Bundle arg2) {
View mapView = super.onCreateView(arg0, arg1, arg2);
Fragment fragment = getParentFragment();
if (fragment != null && fragment instanceof OnMapReadyListener) {
((OnMapReadyListener) fragment).onMapReady();
}
mTouchView = new TouchableWrapper(getActivity());
mTouchView.addView(mapView);
return mTouchView;
}
public static interface OnMapReadyListener {
void onMapReady();
}
}
在我的Map_Fragment中(最终将位于支持导航抽屉和片段事务以切换视图的活动中的FrameLayout内)
mMapFragment = CustomMap_Fragment.newInstance();
getChildFragmentManager().beginTransaction().replace(R.id.map_container, mMapFragment).commit();
现在终于在我刚拿到地图的同一个片段中,EventBus接收器在收到&#34; double_tapped_map&#34;
时会执行以下操作@Subscribe public void eventBus_ListenerMethod(AnswerAvailableEvent event) {
//Construct a CameraUpdate object that will zoom into the exact middle of the map, with a zoom of currentCameraZoom + 1 unit
zoomInUpdate = CameraUpdateFactory.zoomIn();
//Run that with a speed of 400 ms.
map.animateCamera(zoomInUpdate, 400, null);
}
注意:为了完美实现这一点,您需要在地图上停用zoomGestures(意味着您执行myMap.getUiSettings().setZoomGesturesEnabled(false);
。如果您不这样做,您将可以双击很快在地图上你会看到它会从中心缩放,因为双击的实现与我在第一个答案中的实现完全一样,即它们减去当前时间之前的点击时间,所以在那个窗口你可以滑入第三个点击它不会触发事件总线事件,谷歌地图将捕捉它;所以禁用缩放手势。
然而,你会看到捏合/熄灭将不再起作用,你也必须处理捏,我也已经完成但需要再花1个小时而且我没有时间去做那个但是,当我这样做时,我会更新答案。
提示:优步已禁用地图上的旋转手势。 map.getUiSettings().setRotateGesturesEnabled(false);
答案 2 :(得分:2)
就个人而言,我只会在地图上禁用缩放手势,检测叠加上的捏合,然后将其他所有内容传递到地图。
google-maps v2 API没有任何明确的自定义缩放处理功能。虽然我确信你可以注入一些东西,但是叠加方法可以使你免受google-maps更改的影响,并且可以让你在需要时更轻松地支持其他地图提供者。
(只是为了完整性:您还可以处理相机后更改事件并重新居中,但这将是一种愚蠢,糟糕的用户体验。)
答案 3 :(得分:1)
您可以使用LatLngBounds限制地图从您想要的位置移动。 (您可以将绑定的东北角和西南角设置为相同的点)。
请查看以下链接。
https://developers.google.com/maps/documentation/android-api/views
答案 4 :(得分:1)
我也有相同的要求。我必须了解如何在android中处理事件以解决此问题,因为我们必须拦截触摸事件以进行缩放并将滚动事件传递给地图。 为此,我们需要在Google Map View上自定义View。我们的自定义视图拦截触摸事件,并通过不给基础地图提供处理机会的机会来决定是否处理后续事件,或者只是让基础地图独自处理所有事件。
现在的代码时间- 这里我们需要两件事-一个自定义片段,一个自定义视图。
自定义片段
public class CustomMapFragment extends SupportMapFragment implements OnMapReadyCallback {
public View mapView = null;
public WrapperView wrapperView = null;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
mapView = super.onCreateView(inflater, parent, savedInstanceState);
wrapperView = new WrapperView(getActivity());
wrapperView.addView(mapView);
SupportMapFragment mapFragment = (SupportMapFragment) getActivity().getSupportFragmentManager().findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
return wrapperView;
}
@Override
public View getView() {
return mapView;
}
@Override
public void onMapReady(GoogleMap googleMap) {
wrapperView.setGoogleMap(googleMap);
}
自定义视图
public class WrapperView extends FrameLayout {
private GoogleMap googleMap;
Activity activity = null;
ScaleGestureDetector scaleGestureDetector;
public WrapperView(Activity activity) {
super(activity);
this.activity=activity;
scaleGestureDetector = new ScaleGestureDetector(activity ,new MyOnScaleGestureListener());
}
public void setGoogleMap(GoogleMap map){
googleMap = map;
}
private boolean isZoomInProgress(MotionEvent event){
if(event.getPointerCount()>1){
return true;
}
return false;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event){
return isZoomInProgress(event);
}
@Override
public boolean onTouchEvent(MotionEvent event){
return scaleGestureDetector.onTouchEvent(event);
}
public class MyOnScaleGestureListener extends
ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
float previousSpan = detector.getPreviousSpan();
float currentSpan = detector.getCurrentSpan();
float targetSpan;
if(previousSpan>currentSpan){
targetSpan = previousSpan-currentSpan;
}else{
targetSpan = currentSpan-previousSpan;
}
float scaleFactor = detector.getScaleFactor();
if (scaleFactor > 1) {
if(googleMap.getCameraPosition().zoom!=googleMap.getMaxZoomLevel()) {
for(int j=0;j<(targetSpan*2);j++){
googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(googleMap.getCameraPosition().target, googleMap.getCameraPosition().zoom + 0.002f));
}
}
} else {
if (googleMap.getCameraPosition().zoom != googleMap.getMinZoomLevel()) {
for(int j=0;j<(targetSpan*2);j++){
googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(googleMap.getCameraPosition().target, googleMap.getCameraPosition().zoom - 0.002f));
}
}
}
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {}
}
在您的视图中使用新的自定义片段,如下所示-
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/map"
android:name="yourpackage.CustomMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" />
答案 5 :(得分:0)
对于您的setLatLngBoundsForCameraTarget()
对象,可以使用GoogleMap
方法轻松实现。在Android版Google Maps SDK的“相机和视图”页面上:
private GoogleMap mMap;
// Create a LatLngBounds that includes the city of Adelaide in Australia.
private LatLngBounds ADELAIDE = new LatLngBounds(
new LatLng(-35.0, 138.58), new LatLng(-34.9, 138.61));
// Constrain the camera target to the Adelaide bounds.
mMap.setLatLngBoundsForCameraTarget(ADELAIDE);`
请相信我,如果您将LatLngBounds
放置在标记的确切坐标上,则可以使相机保持在标记的中心。容易!