Зная, когда карта перестала прокручиваться (например, "moveend" в javascript API)

Мне нужно обнаружить, когда MapView был прокручен или увеличен, например, событие moveend в javascript API. Я хотел бы подождать, пока представление не перестанет двигаться, поэтому я могу определить, нужно ли мне запрашивать мой сервер для элементов, имеющих прямоугольник просмотра, и если да, отправьте запрос. (на самом деле я отправляю запрос на немного большую область, чем прямоугольник просмотра)

Очевидно, что я предпочитаю не отправлять запрос на данные, если представление все еще перемещается. Но еще хуже то, что я не знаю, что мне нужно отправить другой запрос, оставив в местах карты отсутствующие маркеры.

В настоящее время я подклассифицирую MapView и обрабатываю onTouchEvent следующим образом:

public boolean onTouchEvent(android.view.MotionEvent ev) {
 super.onTouchEvent (ev);
 if (ev.getAction() == MotionEvent.ACTION_UP) {
 GeoPoint center = getMapCenter();
 int latSpan = getLatitudeSpan(), lngSpan = getLongitudeSpan();
 /* (check if it has moved enough to need a new set of data) */ 
 }
 return true;
 }

Проблема в том, что я не знаю, остановилось ли представление, поскольку прокрутка имеет тенденцию иметь инерцию и может продолжать проходить событие "ACTION_UP".

Есть ли какое-то событие, которое я могу задействовать, которое предупредит меня, когда будет выполнено перемещение карты (или масштабирование)? Если нет, кто-нибудь написал логику, чтобы обнаружить это? В теории я мог бы догадаться, посмотрев на все действия, и поставил что-то немного позже и проверил это... но... это кажется беспорядочным и PITA. Но если кто-то уже написал это...:)

5 ответов

У меня действительно нет удовлетворительного решения этой проблемы, но я могу сказать, что я сделал, чтобы частично решить эту проблему.

  • Я подклассифицировал MapView и переопределяет метод computeScroll(), который получает текущую центральную точку карты и сравнивает ее с последней известной центральной точкой (сохраненной как поле volatile в подклассе). Если центральная точка изменилась, она вызывает событие для слушателя карты (для этого я определил интерфейс пользовательского прослушивателя).
  • Слушатель - это действие, которое создает подкласс AsyncTask и выполняет его. Эта задача приостанавливается на 100 мс в методе doInBackGround() перед выполнением выборки данных сервера.
  • Когда активность прослушивателя получает второе событие переноса карты (которое он будет делать из-за эффекта шага движения карты), он проверяет состояние только что выполненного AsyncTask. Если эта задача все еще запущена, она будет cancel(). Затем он создает новую задачу и выполняет это.

Общий эффект заключается в том, что, когда слушатели получают шквал событий с перемещением по карте в несколько миллисекунд друг от друга, единственная, которая фактически запускает задачу для выполнения выборки сервера, является последней в последовательности. Недостатком является то, что он вводит небольшую задержку между движением карты и происходит выборка сервера.

Я не доволен этим, это уродливо, но это смягчает проблему. Я хотел бы видеть лучшее решение этого.


Это метод, который я использую в данный момент, я использовал его и протестировал, хорошо работает. Просто убедитесь, что ваш метод draw() эффективен. (Избегайте использования GC).

//In map activity
class MyMapActivity extends MapActivity {
 protected void onCreate(Bundle savedState){
 setContent(R.layout.activity_map);
 super.onCreate(savedSate);
 OnMapMoveListener mapListener = new OnMapMoveListener(){
 public void mapMovingFinishedEvent(){
 Log.d("MapActivity", "Hey look! I stopped scrolling!");
 }
 }
 // Create overlay
 OnMoveOverlay mOnMoveOverlay = new OnMoveOverlay(mapListener);
 // Add overlay to view.
 MapView mapView = (MapView)findViewById(R.id.map_view);
 // Make sure you add as the last overlay so its on the top. 
 // Otherwise other overlays could steal the touchEvent;
 mapView.getOverlays().add(mOnMoveOverlay);
 }
}

Это ваш класс OnMoveOverlay

//OnMoveOverlay
class OnMoveOverlay extends Overlay
{
 private static GeoPoint lastLatLon = new GeoPoint(0, 0);
 private static GeoPoint currLatLon;
 // Event listener to listen for map finished moving events
 private OnMapMoveListener eventListener = null;
 protected boolean isMapMoving = false;
 public OnMoveOverlay(OnMapMoveListener eventLis){
 //Set event listener
 eventListener = eventLis;
 }
 @Override 
 public boolean onTouchEvent(android.view.MotionEvent ev)
 {
 super.onTouchEvent(ev);
 if (ev.getAction() == MotionEvent.ACTION_UP)
 {
 // Added to example to make more complete
 isMapMoving = true;
 }
 //Fix: changed to false as it would handle the touch event and not pass back.
 return false;
 }
 @Override 
 public void draw(Canvas canvas, MapView mapView, boolean shadow)
 {
 if (!shadow)
 {
 if (isMapMoving)
 {
 currLatLon = mapView.getProjection().fromPixels(0, 0);
 if (currLatLon.equals(lastLatLon))
 {
 isMapMoving = false;
 eventListener.mapMovingFinishedEvent();
 }
 else
 {
 lastLatLon = currLatLon;
 }
 }
 }
 }
 public interface OnMapMoveListener{
 public void mapMovingFinishedEvent();
 }
}

Просто выполните свой собственный прослушиватель eventListener.mapMovingFinishedEvent(); и запустите перемещение карты с помощью другого метода, например, выше и отсортированного.

Идея заключается в том, что карта перемещает проекцию пикселя в координаты, будет меняться, как только они будут такими же, вы закончили движение.

Я обновил это с более новым более полным кодом, возникла проблема с двойным рисунком.

Мы ничего не делаем на теневом проходе, так как мы просто удвоим расчет за ничью, которая является пустой тратой.

Не стесняйтесь задавать любые вопросы:)

Спасибо, Крис


У меня была такая же проблема и "решена" она аналогичным образом, но я считаю менее сложной: Поскольку переопределение computeScroll() не сработало для меня, я тоже переопределил onTouchEvent. Затем я использовал обработчик, который вызывает вызов метода через 50 мс, если центр карты изменился, то снова повторится, если центр карты не изменился, вызывается слушатель. Метод, который я вызываю в onTouchEvent, выглядит следующим образом:

private void refreshMapPosition() {
 GeoPoint currentMapCenter = getMapCenter();
 if (oldMapCenter==null || !oldMapCenter.equals(currentMapCenter)) {
 oldMapCenter = currentMapCenter;
 handler.postDelayed(new Runnable() {
 @Override
 public void run() {
 refreshMapPosition();
 }
 }, 50);
 }
 else {
 if (onScrollEndListener!=null)
 onScrollEndListener.onScrollEnd(currentMapCenter);
 }
}

Но я жду настоящего решения для этого тоже...


Я решил это, используя поток, и, похоже, он работает неплохо. Он не только обнаруживает изменения центра, но и изменяет масштаб. Ну, обнаружение выполняется после масштабирования и прокрутки. Если вам нужно обнаружить изменения масштаба при перемещении вверх по первому пальцу, вы можете немного изменить свой код, чтобы обнаружить разные указатели. Но я не нуждался в этом, поэтому не включил его и оставил для вас домашнюю работу: D

public class CustomMapView extends MapView {
 private GeoPoint pressGP;
 private GeoPoint lastGP;
 private int pressZoom;
 private int lastZoom;
 public boolean onTouchEvent( MotionEvent event ) {
 switch( event.getAction() ) {
 case MotionEvent.ACTION_DOWN:
 pressGP = getMapCenter();
 pressZoom = getZoomLevel();
 break;
 case MotionEvent.ACTION_UP:
 lastGP = getMapCenter();
 pressZoom = getZoomLevel();
 if( !pressGP.equals( lastGP ) ) {
 Thread thread = new Thread() {
 public void run() {
 while( true ) {
 try {
 Thread.sleep( 100 );
 } catch (InterruptedException e) {}
 GeoPoint gp = getMapCenter();
 int zl = getZoomLevel();
 if( gp.equals( lastGP ) && zl == lastZoom)
 break;
 lastGP = gp;
 lastZoom = zl;
 }
 onMapStop( lastGP );
 }
 };
 thread.start();
 }
 break;
 }
 return super.onTouchEvent( event );
 }
 public void onMapStop( GeoPoint point , int zoom ){
 // PUT YOUR CODE HERE
 }
}


В последней версии API карт Google (V2) для этого есть слушатель, т.е. GoogleMap.OnCameraChangeListener.

mGoogleMap.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener()
{
 @Override
 public void onCameraChange(CameraPosition cameraPosition)
 {
 Toast.makeText(mActivity, "Longitude : "+cameraPosition.target.longitude
 +", Latitude : "+cameraPosition.target.latitude, Toast.LENGTH_SHORT).show();
 }
});

licensed under cc by-sa 3.0 with attribution.