2010-08-25 48 views
10

Tôi đang cố gắng viết một ứng dụng sẽ tự động tải dữ liệu vào bản đồ trong khi người dùng mở hoặc thu phóng dữ liệu đó.Làm cách nào để thu phóng và thu phóng bản đồ đó thực sự đã kết thúc?

Tôi cần theo dõi khi bản đồ kết thúc để thay đổi trạng thái chế độ xem (dừng quay hoặc thu phóng) và sau đó tải một phần dữ liệu mới để tạo điểm đánh dấu. Nhưng trên thực tế, API Google Maps không có bất kỳ sự kiện nào để xử lý việc này.

Có một số phương pháp như tạo lớp phủ trống để kiểm soát onTouch sự kiện, v.v. Tuy nhiên, bản đồ panning có thể kéo dài sau khi người dùng hoàn thành liên lạc của mình vì GMaps sử dụng một số loại quán tính để làm cho chảo mượt mà hơn.

Tôi đã cố gắng phân loại MapView nhưng phương pháp draw() của nó là final do đó không thể ghi đè.

Bất kỳ ý tưởng nào về cách xử lý chính xác việc xoay và thu phóng hoàn thiện?

Trả lời

12

Giờ nghiên cứu và một số quyết định đã được tìm thấy. Nó có một số khuyết điểm và thuận mà tôi sẽ mô tả thêm.

Điều chính chúng ta nên làm là ghi đè lên một số phương thức của MapView để xử lý hành vi vẽ của nó. Trong trường hợp chúng ta không thể ghi đè phương thức draw(), chúng ta nên tìm cách khác. Có một dẫn xuất khác từ View có thể được ghi đè - phương thức computeScroll(). Nó được gọi lặp đi lặp lại khi bản đồ tiếp tục padding. Tất cả những gì chúng ta phải làm là đặt một số ngưỡng thời gian để bắt nếu computeScroll không còn được gọi nữa.
Dưới đây là những gì tôi đã làm:

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

} 

Sau đó, bạn nên đăng ký xử lý sự kiện trong MapActivity của bạn như thế này:

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

Bây giờ những gì về ưu và nhược điểm của phương pháp này?
Ưu điểm:
- Sự kiện xử lý bản đồ đường đi được quét hoặc thu phóng. Sự kiện chạm, các phím phần cứng được sử dụng, ngay cả các sự kiện được kích hoạt theo chương trình cũng được xử lý (như phương thức setZoom() hoặc animate()).
- Có khả năng bỏ qua tải dữ liệu không cần thiết nếu người dùng nhấp vào nút thu phóng nhanh chóng vài lần. Sự kiện sẽ chỉ kích hoạt sau khi các nhấp chuột sẽ ngừng.
Nhược điểm:
- Nó là khá không thể hủy phóng to hoặc di chuyển hành động (có lẽ tôi sẽ thêm khả năng này trong tương lai)

Hope lớp này ít sẽ giúp bạn.

+0

Một vấn đề nhỏ khác mà tôi nhận thấy khi sử dụng giải pháp này là việc thay đổi drawable cho một trong các 'OverlayItem' hoặc thay đổi rotation, computeScroll() cũng sẽ được kích hoạt, làm cho 'onPanChangeListener' cháy. – rogerkk

+0

Không phải tất cả các nhà xây dựng lớp của bạn đều khởi tạo nội bộ _this, last_center_pos, last_zoom. Theo như tôi có thể nói, họ nên. – Theo

+0

(! Last_center_pos.equals (getMapCenter()) ||! Is_touched) phải là (! Last_center_pos.equals (getMapCenter()) &&! Is_touched) nếu không, trình nghe pan sẽ phát liên tục khi người dùng không chạm vào màn hình. Bạn đã có nó ngay trong bình luận ở trên dòng mà bạn mô tả các điều kiện sử dụng 'và'. – Theo

0

Dự án MapChange, được đăng lần đầu trên similar question here, đã giúp tôi hoàn thành nhiệm vụ tương tự mà bạn đã yêu cầu.

+0

Như tôi thấy họ sử dụng kỹ thuật của tôi. Có lẽ họ thậm chí còn lấy cảm hứng từ câu trả lời của tôi được hiển thị ở trên :) Dù sao tôi đánh giá cao rằng ai đó đã làm cho nó như là một thư viện riêng biệt. –

Các vấn đề liên quan