Зная, когда карта перестала прокручиваться (например, "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. Но если кто-то уже написал это...:)

Ответ 1

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

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

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

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

Ответ 2

Это метод, который я использую в данный момент, я использовал его и протестировал, хорошо работает. Просто убедитесь, что ваш метод 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(); и запустите перемещение карты с помощью другого метода, например, выше и отсортированного.

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

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

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

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

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

Ответ 3

У меня была такая же проблема и "решена" она аналогичным образом, но я считаю менее сложной: Поскольку переопределение 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);
    }
}

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

Ответ 4

Я решил это, используя поток, и, похоже, он работает неплохо. Он не только обнаруживает изменения центра, но и изменяет масштаб. Ну, обнаружение выполняется после масштабирования и прокрутки. Если вам нужно обнаружить изменения масштаба при перемещении вверх по первому пальцу, вы можете немного изменить свой код, чтобы обнаружить разные указатели. Но я не нуждался в этом, поэтому не включил его и оставил для вас домашнюю работу: 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
    }
}

Ответ 5

В последней версии 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();
    }
});