android - 如何捕捉到 map 平移和缩放真的完成了?

标签 android google-maps drag dynamic-data

我正在尝试编写一个应用程序,该应用程序将在用户平移或缩放数据时动态加载要映射的数据。

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

有一些方法,例如创建一个空的叠加层来控制 onTouch 事件等,但是 map 平移可能会在用户完成触摸后持续很长时间,因为 GMaps 使用某种惯性使平移更平滑.

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

关于如何精确处理平移和缩放整理有什么想法吗?

最佳答案

经过数小时的研究并做出了一些决定。它有一些缺点和优点,我将进一步描述。

我们应该做的主要事情是重写 MapView 的一些方法来处理它的绘制行为。如果我们不能覆盖 draw() 方法,我们应该找到另一种方法。还有另一种可能被覆盖的 View 派生方法 - computeScroll() 方法。随着 map 继续填充,它会被重复调用。我们所要做的就是设置一些时间阈值来捕获这次是否不再调用 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());
        }
    }
}

那么这种方法的优缺点呢?
优点:
- 事件处理 map 被平移或缩放的任何一种方式。处理触摸事件、使用的硬件键,甚至以编程方式触发的事件(如 setZoom() 或 animate() 方法)。
- 如果用户快速多次单击缩放按钮,能够跳过不必要的数据加载。只有在点击停止后事件才会触发。
缺点:
- 完全不可能取消缩放或平移操作(也许我会在未来添加此功能)

希望这个小类(class)能帮到你。

关于android - 如何捕捉到 map 平移和缩放真的完成了?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3567420/

相关文章:

java - 无法解析符号 "content_frame"

swift - 将标记图标设置为 UIImage 并使用背景适当调整其大小以使其显示为图标

javascript - 如何防止可拖动元素移出边界?

swift - 使用 UIPanGestureRecognizer 在限定区域内拖动 UIView

android - "App Engine Connected Android Project"不可用

java - 向 RecyclerView 添加额外项目(将本地存储的数据与在线服务器数据混合)

android - 恢复交易inapp购买android

javascript - Google Maps API - map 未加载

c# - 如何在谷歌地图或必应 map 上检索建筑物的坐标(长、宽、高)?

jquery - 在 jQuery 中拖动 Div - 当鼠标速度较慢时效果很好,但在鼠标快速移动时会失败