Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

knowing when map has stopped scrolling (like "moveend" in javascript API)

I need to detect when a MapView has been scrolled or zoomed, like the "moveend" event in the javascript API. I'd like to wait until the view has stopped moving, so I can then detect if I need to query my server for items withing the viewing rectangle, and if so send out a request. (actually I send a request for a slightly larger area than the viewing rectangle)

Obviously, I'd rather not send out a request for data if the view is still moving. But even worse is that I don't know that I need to send another request, leaving areas of the map missing markers.

Currently I am subclassing MapView and handling the onTouchEvent as follows:

 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;
    }

Problem is, I don't know if the view has stopped, since scrolling tends to have inertia and can keep going past the "ACTION_UP" event.

Is there some event I can tap into that will alert me when a mapview is done moving (or zooming)? If not, has anyone written logic to detect this? In theory I could make a guess by looking at all the actions, and set something to come along bit later and check it...but...that seems messy and a PITA. But if someone has already written it.... :)

like image 859
rob Avatar asked Dec 04 '10 03:12

rob


2 Answers

This is the method I am using at the moment, I have used this and tested it, works well. Just make sure you make your draw() method efficient. (Avoid GC in it).

//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);
  }

}

This is your OnMoveOverlay class

//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();
            }
}

Just implement your own listener eventListener.mapMovingFinishedEvent(); and fire the map moving bool by another method like above and your sorted.

The idea is when the map is moving the pixel projection to the coords will be changing, once they are the same, you have finished moving.

I have updated this with newer more complete code, there was an issue with it double drawing.

We don't do anything on the shadow pass as we would just double calculate per draw pass which is a waste.

Feel Free to ask any questions :)

Thanks, Chris

like image 123
Chris.Jenkins Avatar answered Nov 06 '22 15:11

Chris.Jenkins


I had the same problem and "solved" it in a similar way, but I think less complicated: As overriding computeScroll() didn't work for me, I overrode onTouchEvent, too. Then I used a Handler, that invokes a method call after 50ms, if the map center changed, the same happens again, if the map center didn't change, the listener is called. The method I invoke in onTouchEvent looks like this:

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);
    }
}

But I'm waiting for a real solution for this, too ...

like image 43
Tobbe Avatar answered Nov 06 '22 15:11

Tobbe