知道地图何时停止滚动(如javascript API中的“moveend”)

时间:2010-12-04 03:17:33

标签: android google-maps

我需要检测MapView的滚动或缩放时间,例如javascript API中的“moveend”事件。我想等到视图停止移动,然后我可以检测是否需要向服务器查询带有查看矩形的项目,如果是,则发出请求。 (实际上我发送了一个比观察矩形略大的区域的请求)

显然,如果视图仍在移动,我宁愿不发送数据请求。但更糟糕的是,我不知道我需要发送另一个请求,使地图区域缺少标记。

目前我正在继承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”事件。

是否有一些我可以使用的事件会在mapview完成移动(或缩放)时提醒我?如果没有,有没有人写过逻辑来检测这个?从理论上讲,我可以通过查看所有动作进行猜测,并稍后设置一些东西并检查它......但是......这看起来很麻烦而且是PITA。但如果有人已经写了......:)

5 个答案:

答案 0 :(得分:13)

这是我目前正在使用的方法,我已经使用过它并对其进行了测试,效果很好。 只需确保您的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();,然后通过上面的其他方法和你的排序来激活地图移动bool。

这个想法是当地图将像素投影移动到坐标时会发生变化,一旦它们相同,你就完成了移动。

我用更新更完整的代码更新了这个,它有双重绘图的问题。

我们不会对阴影传递做任何事情,因为我们只会对每次抽签通过双重计算,这是一种浪费。

随意提出任何问题:)

谢谢, 克里斯

答案 1 :(得分:8)

我有同样的问题并以类似的方式“解决”它,但我觉得不那么复杂: 由于重写computeScroll()对我不起作用,我也过度使用了onTouchEvent。然后我使用了一个Handler,它在50ms后调用一个方法调用,如果地图中心改变了,同样会再次发生,如果地图中心没有改变,则调用监听器。我在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);
    }
}

但我也在等待一个真正的解决方案......

答案 2 :(得分:2)

我对这个问题没有一个满意的解决方案,但我可以告诉我做了什么来部分解决它。

  • 我将MapView子类化并覆盖computeScroll()方法,该方法获取地图的当前中心点并将其与最后已知的中心点(存储为volatile进行比较子类中的字段)。如果中心点已更改,它会向地图的侦听器触发一个事件(我为此定义了一个自定义侦听器接口)。
  • 侦听器是一个实例化AsyncTask的子类并执行它的活动。在执行服务器数据提取之前,此任务在其doInBackGround()方法中暂停100毫秒。
  • 当侦听器活动收到第二个map-move事件时(由于地图移动的步进效果,它会执行),它会检查刚刚执行的AsyncTask的状态。如果该任务仍在运行,它将cancel()。然后它创建一个新任务,然后执行该任务。

总体效果是,当侦听器在几毫秒之间获得一系列映射移动事件时,实际触发任务执行服务器获取的唯一一个是序列中的最后一个。缺点是它在地图移动发生和服务器提取之间引入了一点延迟。

我对它不满意,它很难看,但它减轻了这个问题。我会看到更好的解决方案。

答案 3 :(得分:2)

我用一个线程解决了它,它似乎工作得很好。它不仅可以检测中心变化,还可以缩放变化。好吧,检测是在缩放和滚动结束后完成的。如果您需要在向上移动第一根手指时检测缩放更改,则可以稍微修改我的代码以检测不同的指针。但我不需要它,所以不包括它并为你留下一些功课: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
    }
}

答案 4 :(得分:1)

使用最新版本的谷歌地图API(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();
    }
});