programacion - Mapas de Android: ¿Cómo hacer clic en un mapa durante mucho tiempo?
manual android studio avanzado (8)
Ambrosio
Modifiqué la demo de la biblioteca mapview-overlay-manager . para ejecutar este código con un doble toque:
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;
}
}
Espero eso ayude.
¿Cómo hago clic largo tiempo en una vista del mapa para que aparezca un marcador de lugar en ese punto del mapa?
He intentado un par de maneras sin éxito:
1) Utilizando setOnLongClickListener
en el MapvView
que nunca detectó los clics largos.
2) Mi otra idea fue extender MapView
para anular dispatchTouchEvent
.. Crear un GestureDetector para responder a la devolución de llamada de pulsación larga. Pero estaba atrapado a mitad de camino aquí, ya que no podía manejar mi subclase Mapview. es decir
MyMapview mymapview; //MyMapView extends MapView
//results in a classcast exception
mymapView = (MyMapView) findViewById(R.id.map);
3) La única otra forma en que sé cómo probar esto es: Detectar un MotionEvent.ACTION_DOWN
y publicar un MotionEvent.ACTION_DOWN
retrasado en un controlador y detectar una pulsación larga si los otros dos eventos: acton_move o action_up, no han ocurrido.
¿Puede alguien reflexionar sobre alguno de estos métodos para detectar prensas largas?
Esta biblioteca de mapview-overlay-manager es super. La razón por la que onLongPress se activa cuando se "desplaza" es porque la biblioteca no tiene en cuenta el multitouch. Puede solucionar esto rechazando onLongPress si hay más de un puntero involucrado como tal:
public void onLongPress(MotionEvent motionEvent, ManagedOverlay arg1) {
if (motionEvent.getPointerCount() > 1) return;
... your logic here ...
}
Esto funciona con su MapActivity y le permitirá:
- Establezca su propio umbral de tiempo para lo que constituye una pulsación larga en el mapa (0,8 segundos es bueno para mí).
- No interprete un evento de desplazamiento, multitoque o no multitáctil como una pulsación larga. También le permite establecer una tolerancia para que el dedo de alguien se mueva ligeramente en la pantalla mientras mantiene presionada una tecla larga.
Esta solución se basa en http://www.kind-kristiansen.no/2011/android-handling-longpresslongclick-on-map-revisited/ . Agregué detección de puntos x e y para que el desplazamiento no se vea como una pulsación larga. Esta solución no es tan elegante como la de Roger porque uso variables de clase y coloco el código en mi MapActivity existente en lugar de extender MapView y crear un oyente como lo hizo él. Pero si desea seguir con su código pero tiene un mejor soporte no multitáctil, simplemente saque los puntos x e y los agregue a los suyos.
Variables de clase establecidas en la parte superior de mi 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
Agrega esta función en tu 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''s 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''s 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''s "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''s where you add code to:
// put a marker on the map, save the point to your SQLite database, etc
*/
}
}
}
}
return super.dispatchTouchEvent(ev);
}
Hay una implementación para manejar la prensa larga en los mapas aquí:
http://www.kind-kristiansen.no/2011/android-handling-longpresslongclick-on-map-revisited/
He encontrado una forma aún más fácil. Simplemente haga una superposición como la primera superposición en la lista que no dibuja nada y úsela para reconocer gestos utilizando el Detector de gestos. Entonces debería devolver el valor verdadero si controlaba el evento para que no se propague.
List<Overlay> overlays = mapView.getOverlays();
overlays.clear();
overlays.add(new MapGestureDetectorOverlay(new MyOnGestureListener()));
Y aquí está la clase:
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;
}
}
La mejor manera que conozco para hacer esto es usar el código abierto mapview-overlay-manager y usar su escucha de gestos que proporciona una devolución de llamada para
public void onLongPress(MotionEvent e, ManagedOverlay overlay)
Si va con el post / mensaje retrasado a un controlador (una solución que uso yo mismo), puede ser útil probar si el mapa se ha movido, es decir, si mapView.getMapCenter()
, mapView.getLatitudeSpan()
y mapView.getLongitudeSpan()
devuelve lo mismo que cuando el puntero bajó.
También obtuve una ClassCastException cuando intenté usar MyMapView. Como solución alternativa, creé un objeto MyMapView en lugar de un objeto MapView y lo agregué programáticamente al diseño, y funcionó muy bien.
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);