2010-08-25 59 views
10

我正试图编写一个应用程序,它将动态加载数据映射到用户平移或缩放它时。如何捕捉地图平移和缩放真的完成?

我需要跟踪地图何时完成以更改其视图状态(停止平移或缩放),然后加载创建标记的新数据部分。但事实上Google Maps API没有任何事件来处理这个问题。

有一些方法可以创建一个空覆盖层来控制事件等等,但是地图平移可能会在用户完成触摸后持续很长时间,因为GMaps会使用某种惯性来平滑平移。

我尝试子类MapView,但它的draw()方法是final因此它不能被覆盖。

任何想法如何精确处理平移和缩放整理?

回答

12

小时的研究和一些决定被发现。它有一些我将进一步描述的缺点和优点。

我们应该做的主要事情是重写一些MapView的方法来处理其绘图行为。如果我们不能重写draw()方法,我们应该找到另一种方法。还有一种可能被重写的派生参数 - computeScroll()方法。它会在地图继续填充时重复调用。我们所要做的就是设置一些时间阈值来捕获computeScroll这次不再被调用。
这里是我做过什么:

import java.util.Timer; 
import java.util.TimerTask; 

import android.content.Context; 
import android.util.AttributeSet; 
import android.view.MotionEvent; 

import com.google.android.maps.GeoPoint; 
import com.google.android.maps.MapView; 

public class EnhancedMapView extends MapView { 
public interface OnZoomChangeListener { 
    public void onZoomChange(MapView view, int newZoom, int oldZoom); 
} 

public interface OnPanChangeListener { 
    public void onPanChange(MapView view, GeoPoint newCenter, GeoPoint oldCenter); 
} 

private EnhancedMapView _this; 

    // Set this variable to your preferred timeout 
private long events_timeout = 500L; 
private boolean is_touched = false; 
private GeoPoint last_center_pos; 
private int last_zoom; 
private Timer zoom_event_delay_timer = new Timer(); 
private Timer pan_event_delay_timer = new Timer(); 

private EnhancedMapView.OnZoomChangeListener zoom_change_listener; 
private EnhancedMapView.OnPanChangeListener pan_change_listener; 


public EnhancedMapView(Context context, String apiKey) { 
    super(context, apiKey); 
    _this = this; 
    last_center_pos = this.getMapCenter(); 
    last_zoom = this.getZoomLevel(); 
} 

public EnhancedMapView(Context context, AttributeSet attrs) { 
    super(context, attrs); 
} 

public EnhancedMapView(Context context, AttributeSet attrs, int defStyle) { 
    super(context, attrs, defStyle); 
} 

public void setOnZoomChangeListener(EnhancedMapView.OnZoomChangeListener l) { 
    zoom_change_listener = l; 
} 

public void setOnPanChangeListener(EnhancedMapView.OnPanChangeListener l) { 
    pan_change_listener = l; 
} 

@Override 
public boolean onTouchEvent(MotionEvent ev) { 
    if (ev.getAction() == 1) { 
     is_touched = false; 
    } else { 
     is_touched = true; 
    } 

    return super.onTouchEvent(ev); 
} 

@Override 
public void computeScroll() { 
    super.computeScroll(); 

    if (getZoomLevel() != last_zoom) { 
        // if computeScroll called before timer counts down we should drop it and start it over again 
     zoom_event_delay_timer.cancel(); 
     zoom_event_delay_timer = new Timer(); 
     zoom_event_delay_timer.schedule(new TimerTask() { 
      @Override 
      public void run() { 
       zoom_change_listener.onZoomChange(_this, getZoomLevel(), last_zoom); 
       last_zoom = getZoomLevel(); 
      } 
     }, events_timeout); 
    } 

    // Send event only when map's center has changed and user stopped touching the screen 
    if (!last_center_pos.equals(getMapCenter()) || !is_touched) { 
     pan_event_delay_timer.cancel(); 
     pan_event_delay_timer = new Timer(); 
     pan_event_delay_timer.schedule(new TimerTask() { 
      @Override 
      public void run() { 
       pan_change_listener.onPanChange(_this, getMapCenter(), last_center_pos); 
       last_center_pos = getMapCenter(); 
      } 
     }, events_timeout); 
    } 
} 

} 

那么你应该在你的MapActivity注册事件处理程序是这样的:

public class YourMapActivity extends MapActivity { 

@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 

    mv = new EnhancedMapView(this, "<your Maps API key here>"); 

    mv.setClickable(true); 
    mv.setBuiltInZoomControls(true); 

    mv.setOnZoomChangeListener(new EnhancedMapView.OnZoomChangeListener() { 
     @Override 
     public void onZoomChange(MapView view, int newZoom, int oldZoom) { 
      Log.d("test", "zoom changed from " + oldZoom + " to " + newZoom); 
     } 
    } 
    mv.setOnPanChangeListener(new EnhancedMapView.OnPanChangeListener() { 
     public void onPanChange(MapView view, GeoPoint newCenter, GeoPoint oldCenter) { 
      Log.d("test", "center changed from " + oldCenter.getLatitudeE6() + "," + oldCenter.getLongitudeE6() + " to " + newCenter.getLatitudeE6() + "," + newCenter.getLongitudeE6()); 
     } 
    } 
} 

现在来谈谈这种方法的优点和缺点?
优点:
- 事件处理地图被平移或缩放。触摸事件,使用的硬件键,甚至以编程方式触发的事件都被处理(如setZoom()或animate()方法)。
- 如果用户快速点击几次缩放按钮,可跳过不必要的数据加载。事件只会在点击停止后才会触发。
缺点:
- 这是很不可能取消缩放或平移动作(也许我会添加在将来这种能力)

希望这个小类会帮助你。

+0

使用此解决方案时我注意到的另一个小问题是,更改我的一个'OverlayItem'的drawable或更改旋转时,computeScroll()也会触发,从而导致'onPanChangeListener'触发。 – rogerkk 2011-04-17 08:15:10

+0

并非所有的类构造函数都会初始化内部_this,last_center_pos,last_zoom。据我所知,他们应该。 – Theo 2011-08-05 17:27:12

+0

(!last_center_pos.equals(getMapCenter())||!is_touched)应该是(!last_center_pos.equals(getMapCenter())&&!is_touched)否则,当用户不触摸屏幕时,pan侦听器会持续触发。您在上面的注释中说明了您使用'和'描述了条件。 – Theo 2011-08-05 17:57:28

0

最初发布在similar question here上的MapChange项目帮助我完成了您要求的同一项任务。

+0

当我看到他们使用我的技术。也许他们甚至受到我上面显示的答案的启发:)无论如何,我明白有人把它作为一个单独的库。 – 2012-04-23 12:56:35