Android-карты: как долго кликать по карте?
Как долго нажимать на карту, чтобы маркер места отображался в этой точке на карте?
Я пробовал пару способов без успеха:
1) Используя setOnLongClickListener
на MapvView
, который никогда не обнаруживал длинные клавиши.
2) Моя другая идея заключалась в расширении MapView
для переопределения dispatchTouchEvent
. Создайте GestureDetector, чтобы ответить на обратный вызов longpress. Но я был застрял на полпути, так как не мог получить доступ к моему подклассу Mapview. то есть.
MyMapview mymapview; //MyMapView extends MapView
//results in a classcast exception
mymapView = (MyMapView) findViewById(R.id.map);
3) Единственный способ узнать, как это сделать:
Обнаружить MotionEvent.ACTION_DOWN
и опубликовать задержанную runnable для обработчика и обнаружить longpress, если не произошло двух других событий: acton_move или action_up.
Может ли кто-нибудь дать мысли о любом из этих методов для обнаружения длинных нажатий?
Ответы
Ответ 1
Я нашел еще более простой способ. Просто сделайте оверлей в качестве первого наложения в списке, который ничего не рисует и не использует для распознавания жестов с помощью GestureDetector. Затем он должен возвращать значение true, если оно обрабатывает событие, чтобы оно не распространялось.
List<Overlay> overlays = mapView.getOverlays();
overlays.clear();
overlays.add(new MapGestureDetectorOverlay(new MyOnGestureListener()));
И вот класс:
public class MapGestureDetectorOverlay extends Overlay implements OnGestureListener {
private GestureDetector gestureDetector;
private OnGestureListener onGestureListener;
public MapGestureDetectorOverlay() {
gestureDetector = new GestureDetector(this);
}
public MapGestureDetectorOverlay(OnGestureListener onGestureListener) {
this();
setOnGestureListener(onGestureListener);
}
@Override
public boolean onTouchEvent(MotionEvent event, MapView mapView) {
if (gestureDetector.onTouchEvent(event)) {
return true;
}
return false;
}
@Override
public boolean onDown(MotionEvent e) {
if (onGestureListener != null) {
return onGestureListener.onDown(e);
}
return false;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
if (onGestureListener != null) {
return onGestureListener.onFling(e1, e2, velocityX, velocityY);
}
return false;
}
@Override
public void onLongPress(MotionEvent e) {
if (onGestureListener != null) {
onGestureListener.onLongPress(e);
}
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
if (onGestureListener != null) {
onGestureListener.onScroll(e1, e2, distanceX, distanceY);
}
return false;
}
@Override
public void onShowPress(MotionEvent e) {
if (onGestureListener != null) {
onGestureListener.onShowPress(e);
}
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
if (onGestureListener != null) {
onGestureListener.onSingleTapUp(e);
}
return false;
}
public boolean isLongpressEnabled() {
return gestureDetector.isLongpressEnabled();
}
public void setIsLongpressEnabled(boolean isLongpressEnabled) {
gestureDetector.setIsLongpressEnabled(isLongpressEnabled);
}
public OnGestureListener getOnGestureListener() {
return onGestureListener;
}
public void setOnGestureListener(OnGestureListener onGestureListener) {
this.onGestureListener = onGestureListener;
}
}
Ответ 2
Лучший способ, которым я это знаю, - использовать open source mapview-overlay-manager и использовать его прослушиватель жестов, который обеспечивает обратный вызов для
public void onLongPress(MotionEvent e, ManagedOverlay overlay)
Ответ 3
Здесь реализована реализация для обработки длинного нажатия на карты:
http://www.kind-kristiansen.no/2011/android-handling-longpresslongclick-on-map-revisited/
Ответ 4
Это работает с вашей MapActivity и позволит вам:
- Установите свой собственный порог времени для того, что составляет длинное нажатие на карте (0,8
секунд хорошо для меня).
- Не интерпретировать событие прокрутки, multi-touch или non-multi-touch в качестве длительного нажатия. Это также позволяет вам установить перенос для кого-то пальца когда-либо слегка двигаются по экрану, когда они удерживают длинную кнопку.
Это решение основано на решении Роджера Кристиансена. Я добавил обнаружение точки x и y, так что прокрутка не рассматривается как длинное нажатие. Это решение не так элегантно, как Roger, потому что я использую переменные класса и помещаю код в существующую MapActivity вместо расширения MapView и создания слушателя, как он. Но если вы хотите придерживаться своего кода, но лучше иметь поддержку без поддержки нескольких касаний, просто извлеките из моей точки х и у точку и добавьте ее в свой.
Переменные класса, установленные в верхней части моей MapActivity:
//variable for determining long press and then automatically adding a pin to the map
private int minMillisecondThresholdForLongClick = 800;
private long startTimeForLongClick = 0;
private float xScreenCoordinateForLongClick;
private float yScreenCoordinateForLongClick;
private float xtolerance=10;//x pixels that your finger can be off but still constitute a long press
private float ytolerance=10;//y pixels that your finger can be off but still constitute a long press
private float xlow; //actual screen coordinate when you subtract the tolerance
private float xhigh; //actual screen coordinate when you add the tolerance
private float ylow; //actual screen coordinate when you subtract the tolerance
private float yhigh; //actual screen coordinate when you add the tolerance
Добавьте эту функцию в свой MapActivity:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
/* We want to capture the place the user long pressed on the map and add a marker (pin) on the map at that lat/long.
* This solution:
* 1. Allows you to set the time threshold for what constitutes a long press
* 2. Doesn't get fooled by scrolling, multi-touch, or non-multi-touch events
* Thank you Roger Kind Kristiansen for the main idea
*/
//get the action from the MotionEvent: down, move, or up
int actionType = ev.getAction();
if (actionType == MotionEvent.ACTION_DOWN) {
//user pressed the button down so let initialize the main variables that we care about:
// later on when the "Action Up" event fires, the "DownTime" should match the "startTimeForLongClick" that we set here
// the coordinate on the screen should not change much during the long press
startTimeForLongClick=ev.getEventTime();
xScreenCoordinateForLongClick=ev.getX();
yScreenCoordinateForLongClick=ev.getY();
} else if (actionType == MotionEvent.ACTION_MOVE) {
//For non-long press actions, the move action can happen a lot between ACTION_DOWN and ACTION_UP
if (ev.getPointerCount()>1) {
//easiest way to detect a multi-touch even is if the pointer count is greater than 1
//next thing to look at is if the x and y coordinates of the person finger change.
startTimeForLongClick=0; //instead of a timer, just reset this class variable and in our ACTION_UP event, the DownTime value will not match and so we can reset.
} else {
//I know that I am getting to the same action as above, startTimeForLongClick=0, but I want the processor
//to quickly skip over this step if it detects the pointer count > 1 above
float xmove = ev.getX(); //where is their finger now?
float ymove = ev.getY();
//these next four values allow you set a tiny box around their finger in case
//they don't perfectly keep their finger still on a long click.
xlow = xScreenCoordinateForLongClick - xtolerance;
xhigh= xScreenCoordinateForLongClick + xtolerance;
ylow = yScreenCoordinateForLongClick - ytolerance;
yhigh= yScreenCoordinateForLongClick + ytolerance;
if ((xmove<xlow || xmove> xhigh) || (ymove<ylow || ymove> yhigh)){
//out of the range of an acceptable long press, reset the whole process
startTimeForLongClick=0;
}
}
} else if (actionType == MotionEvent.ACTION_UP) {
//determine if this was a long click:
long eventTime = ev.getEventTime();
long downTime = ev.getDownTime(); //this value will match the startTimeForLongClick variable as long as we didn't reset the startTimeForLongClick variable because we detected nonsense that invalidated a long press in the ACTION_MOVE block
//make sure the start time for the original "down event" is the same as this event "downTime"
if (startTimeForLongClick==downTime){
//see if the event time minus the start time is within the threshold
if ((eventTime-startTimeForLongClick)>minMillisecondThresholdForLongClick){
//make sure we are at the same spot where we started the long click
float xup = ev.getX();
float yup = ev.getY();
//I don't want the overhead of a function call:
xlow = xScreenCoordinateForLongClick - xtolerance;
xhigh= xScreenCoordinateForLongClick + xtolerance;
ylow = yScreenCoordinateForLongClick - ytolerance;
yhigh= yScreenCoordinateForLongClick + ytolerance;
if ((xup>xlow && xup<xhigh) && (yup>ylow && yup<yhigh)){
//**** safe to process your code for an actual long press ****
//comment out these next rows after you confirm in logcat that the long press works
long totaltime=eventTime-startTimeForLongClick;
String strtotaltime=Long.toString(totaltime);
Log.d("long press detected: ", strtotaltime);
/*
//Now get the latitude/longitude of where you clicked. Replace all the code below if you already know how to translate a screen coordinate to lat/long. I know it works though.
//*****************
//I have my map under a tab so I have to account for the tab height and the notification bar at the top of the phone.
// Maybe there are other ways so just ignore this if you already know how to get the lat/long of the pixels that were pressed.
int TabHeightAdjustmentPixels=tabHost.getTabWidget().getChildAt(0).getLayoutParams().height;
int EntireTabViewHeight = tabHost.getHeight();
Display display = getWindowManager().getDefaultDisplay();
int EntireScreenHeight = display.getHeight();
int NotificationBarHeight=EntireScreenHeight-EntireTabViewHeight;
//the projection is mapping pixels to where you touch on the screen.
Projection proj = mapView.getProjection();
GeoPoint loc = proj.fromPixels((int)(ev.getX(ev.getPointerCount()-1)), (int)(ev.getY(ev.getPointerCount()-1)-TabHeightAdjustmentPixels-NotificationBarHeight));
int longitude=loc.getLongitudeE6();
int latitude=loc.getLatitudeE6();
//*****************
//**** here where you add code to:
// put a marker on the map, save the point to your SQLite database, etc
*/
}
}
}
}
return super.dispatchTouchEvent(ev);
}
Ответ 5
Эта библиотека mapview-overlay-manager супер. Причина onLongPress активируется, когда "прокрутка" связана с тем, что библиотека не учитывает мультитач. Вы можете обойти это, отклонив onLongPress, если в нем задействовано более одного указателя:
public void onLongPress(MotionEvent motionEvent, ManagedOverlay arg1) {
if (motionEvent.getPointerCount() > 1) return;
... your logic here ...
}
Ответ 6
Ambrose,
Я модифицировал демонстрацию библиотеки mapview-overlay-manager. чтобы этот код работал с двойным жестом:
package de.android1.overlaymanager.demo;
import android.os.Bundle;
import android.widget.Toast;
import android.graphics.drawable.Drawable;
import android.view.MotionEvent;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapView;
import com.google.android.maps.MapController;
import com.google.android.maps.GeoPoint;
import de.android1.overlaymanager.*;
public class DemoView extends MapActivity {
MapView mapView;
MapController mapController;
OverlayManager overlayManager;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mapView = (MapView) findViewById(R.id.mapview);
mapView.setBuiltInZoomControls(true);
mapController = mapView.getController();
overlayManager = new OverlayManager(getApplication(), mapView);
}
@Override
public void onWindowFocusChanged(boolean b) {
createOverlayWithListener();
}
public void createOverlayWithListener() {
//This time we use our own marker
final ManagedOverlay managedOverlay = overlayManager.createOverlay("listenerOverlay", getResources().getDrawable(R.drawable.marker));
for (int i = 0; i < 40; i = i + 3) {
managedOverlay.createItem(GeoHelper.geopoint[i], "Item" + i);
}
managedOverlay.setOnOverlayGestureListener(new ManagedOverlayGestureDetector.OnOverlayGestureListener() {
public boolean onZoom(ZoomEvent zoom, ManagedOverlay overlay) {
return false;
}
public boolean onDoubleTap(MotionEvent e, ManagedOverlay overlay, GeoPoint point, ManagedOverlayItem item) {
Drawable defaultmarker = getResources().getDrawable(R.drawable.marker);
ManagedOverlay managedOverlay = overlayManager.createOverlay(defaultmarker);
//creating some marker:
managedOverlay.createItem(point);
//registers the ManagedOverlayer to the MapView
overlayManager.populate();
Toast.makeText(getApplicationContext(), "You created a Marker!", Toast.LENGTH_LONG).show();
return true;
}
public void onLongPress(MotionEvent arg0, ManagedOverlay arg1) {
// TODO Auto-generated method stub
}
public void onLongPressFinished(MotionEvent arg0,
ManagedOverlay arg1, GeoPoint arg2, ManagedOverlayItem arg3) {
// TODO Auto-generated method stub
}
public boolean onScrolled(MotionEvent arg0, MotionEvent arg1,
float arg2, float arg3, ManagedOverlay arg4) {
// TODO Auto-generated method stub
return false;
}
public boolean onSingleTap(MotionEvent arg0, ManagedOverlay arg1,
GeoPoint arg2, ManagedOverlayItem arg3) {
// TODO Auto-generated method stub
return false;
}
});
overlayManager.populate();
}
@Override
protected boolean isRouteDisplayed() {
return false;
}
}
Надеюсь, что это поможет.
Ответ 7
Я также получил исключение ClassCastException, когда пытался использовать MyMapView. В качестве обходного пути я создал объект MyMapView вместо объекта MapView и программно добавил его в макет, и это сработало отлично.
MyMapView mapView = new MyMapView(this, this.getString(R.string.APIMapKey));
mapView.displayZoomControls(false);
mapView.setClickable(true);
FrameLayout item = (FrameLayout) findViewById(R.id.getlocationmap);
item.addView((MapView)mapView);
Ответ 8
Если вы отправляете задержанное сообщение/сообщение обработчику (решение, которое я использую самостоятельно), может быть полезно проверить, перемещена ли карта, то есть, если mapView.getMapCenter()
, mapView.getLatitudeSpan()
и mapView.getLongitudeSpan()
верните то же самое, что и при указании указателя.