Зная, когда карта перестала прокручиваться (например, "moveend" в javascript API)
Мне нужно обнаружить, когда MapView был прокручен или увеличен, например, событие moveend в javascript API. Я хотел бы подождать, пока представление не перестанет двигаться, поэтому я могу определить, нужно ли мне запрашивать мой сервер для элементов, имеющих прямоугольник просмотра, и если да, отправьте запрос. (на самом деле я отправляю запрос на немного большую область, чем прямоугольник просмотра)
Очевидно, что я предпочитаю не отправлять запрос на данные, если представление все еще перемещается. Но еще хуже то, что я не знаю, что мне нужно отправить другой запрос, оставив в местах карты отсутствующие маркеры.
В настоящее время я подклассифицирую 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".
Есть ли какое-то событие, которое я могу задействовать, которое предупредит меня, когда будет выполнено перемещение карты (или масштабирование)? Если нет, кто-нибудь написал логику, чтобы обнаружить это? В теории я мог бы догадаться, посмотрев на все действия, и поставил что-то немного позже и проверил это... но... это кажется беспорядочным и PITA. Но если кто-то уже написал это...:)
Ответы
Ответ 1
У меня действительно нет удовлетворительного решения этой проблемы, но я могу сказать, что я сделал, чтобы частично решить эту проблему.
- Я подклассифицировал
MapView
и переопределяет метод computeScroll()
, который получает текущую центральную точку карты и сравнивает ее с последней известной центральной точкой (сохраненной как поле volatile
в подклассе). Если центральная точка изменилась, она вызывает событие для слушателя карты (для этого я определил интерфейс пользовательского прослушивателя).
- Слушатель - это действие, которое создает подкласс
AsyncTask
и выполняет его. Эта задача приостанавливается на 100 мс в методе doInBackGround()
перед выполнением выборки данных сервера.
- Когда активность прослушивателя получает второе событие переноса карты (которое он будет делать из-за эффекта шага движения карты), он проверяет состояние только что выполненного
AsyncTask
. Если эта задача все еще запущена, она будет cancel()
. Затем он создает новую задачу и выполняет это.
Общий эффект заключается в том, что, когда слушатели получают шквал событий с перемещением по карте в несколько миллисекунд друг от друга, единственная, которая фактически запускает задачу для выполнения выборки сервера, является последней в последовательности. Недостатком является то, что он вводит небольшую задержку между движением карты и происходит выборка сервера.
Я не доволен этим, это уродливо, но это смягчает проблему. Я хотел бы видеть лучшее решение этого.
Ответ 2
Это метод, который я использую в данный момент, я использовал его и протестировал, хорошо работает.
Просто убедитесь, что ваш метод 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();
и запустите перемещение карты с помощью другого метода, например, выше и отсортированного.
Идея заключается в том, что карта перемещает проекцию пикселя в координаты, будет меняться, как только они будут такими же, вы закончили движение.
Я обновил это с более новым более полным кодом, возникла проблема с двойным рисунком.
Мы ничего не делаем на теневом проходе, так как мы просто удвоим расчет за ничью, которая является пустой тратой.
Не стесняйтесь задавать любые вопросы:)
Спасибо,
Крис
Ответ 3
У меня была такая же проблема и "решена" она аналогичным образом, но я считаю менее сложной:
Поскольку переопределение computeScroll() не сработало для меня, я тоже переопределил onTouchEvent. Затем я использовал обработчик, который вызывает вызов метода через 50 мс, если центр карты изменился, то снова повторится, если центр карты не изменился, вызывается слушатель. Метод, который я вызываю в 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);
}
}
Но я жду настоящего решения для этого тоже...
Ответ 4
Я решил это, используя поток, и, похоже, он работает неплохо. Он не только обнаруживает изменения центра, но и изменяет масштаб. Ну, обнаружение выполняется после масштабирования и прокрутки. Если вам нужно обнаружить изменения масштаба при перемещении вверх по первому пальцу, вы можете немного изменить свой код, чтобы обнаружить разные указатели. Но я не нуждался в этом, поэтому не включил его и оставил для вас домашнюю работу: 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
}
}
Ответ 5
В последней версии API карт Google (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();
}
});