【问题标题】:How can I handle map move end using Google Maps for Android V2?如何使用适用于 Android V2 的 Google Maps 处理地图移动端?
【发布时间】:2012-11-22 00:46:45
【问题描述】:

我想在地图中心更改后立即对地址进行地理编码。

如何使用新的 Android 版 Google 地图 V2 处理地图移动端? (我说的是用户用手指拖动地图的情况)

【问题讨论】:

  • onTouchEvent 方法对你没有帮助?
  • 谷歌还没有这个内置的!疯了..

标签: android google-maps android-mapview google-maps-android-api-2


【解决方案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.OnCameraMoveStartedListenerGoogleMap.OnCameraMoveListenerGoogleMap.OnCameraIdleListener

    如果出于某种原因,您想使用现在已弃用的旧 API,您可以使用 onCameraChangeListener。但你必须注意两件事:

    1. onCameraChange() 可能会在您拖动地图时被调用多次或仅调用一次(在拖动停止时)。
    2. 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_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
        
            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1">
        
                <fragment
                    android:id="@+id/map_container"
                    android:name="com.google.android.gms.maps.SupportMapFragment"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent" />
        
                <ImageView
                    android:id="@+id/iv_center_overlay"
                    android:layout_width="25dp"
                    android:layout_height="25dp"
                    android:visibility="gone"
                    android:layout_centerInParent="true"
                    android:src="@drawable/start_blue" />
            </RelativeLayout>
        
        
            <TextView
                android:id="@+id/tv_location_name"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                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_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
        
            <Button
                android:id="@+id/btn_select_place"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Select Place" />
        
            <FrameLayout
                android:id="@+id/frame_container"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
        
        </LinearLayout>
        

        【讨论】:

          【解决方案9】:
          @Override
          public boolean onTouchEvent(MotionEvent event, MapView mapView){
          
              if(event.getAction() == MotionEvent.ACTION_MOVE)
                  return true;
          
              return false;
          }
          

          【讨论】:

          • 这个问题被标记为低质量,因为如果它的长度和内容。您能否补充说明这是如何解决问题的,并可能引用所使用的功能?
          【解决方案10】:

          我认为地图中的onclick事件是:ma​​p.setOnMapClick... 但是 event drag 是: ma​​p.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
                                }
                            }
                        }
                    }
                }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2023-03-17
              • 2023-03-17
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多