I have an overlay which I want to declutter based on the range from the map center.
I want to detect a change in the current map center so that If a person is constantly moving the map around, panning to another area, I can declutter and repopulate data in the new area.
Is there a listener for this or am I going to have to create a thread to periodically check the map center?
I forget the name of it ..... you'll have to read the API documentation ..... but there are properties/methods of the MapActivity that define the boundries (in terms of lat / long) of the currently displayed map that you can use to determine what the center value would be. If the GPS is reporting the user is currently at XXX value, but they have panned the map in a direction that puts the calculated center YYYY away from where the GPS is reporting, then you can determine "how far away from center" the Map is now positioned. Use the Great Circle method, by the way, when doing the math.
I think something like this might work
When you first load the map get a projection and store the center GeoPoint of the middle of the view the map is being displayed on by using the MapView's Projection class
mapPosition = myMapView.getProjection().fromPixels(x,y)
Will return a GeoPoint related to x,y in this case x y needs to be he center of the MapView
Extend the MapView class
public myMapView extends Mapview { }
So you can override onDispatchDraw
In that method you can check if the zoom level has changed, if it has not then it's probably because the map is being panned, could be other reasons I suppose dependent on what you are doing but it's reasonable to think most redraws are pans if you exclude zooms
Compare the current projection GeoPoint of the center of the screen with the one you stored at startup. If it meets your distance critera perform your logic and update the mapPosition geopoint.
If I understand your question correctly something like that should work. Of course you could over ride other members of the extended class as well if you dont want to check on every redraw, the motion events could be used as well but you said you were having problems with those.
UPDATE:
You might be able to do this directly on MapView vs in a custom container but what I have done in the past is override dispatchTouchEvent on an extended container class that the mapview lives inside, in this case a Framelayout so
public class myFrameLayout extends FrameLayout {
Then inside of that I use something like this to track the state of the touch motions, I will reduce it to the minimum that should work, the "modes" are enums I created, where you would do
logic is when the user lifts the finger at the bottom of the case statement
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_POINTER_UP: // Second Finger
case MotionEvent.ACTION_DOWN: // First finger
mode = GESTURE_MODE.PRE_DRAG;
break;
case MotionEvent.ACTION_MOVE:
if (GESTURE_MODE.PRE_DRAG == mode) // Pan
mode = GESTURE_MODE.DRAG;
else
{
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
mode = GESTURE_MODE.MULTI_TOUCH;
break;
case MotionEvent.ACTION_UP: // All fingers removed
if (GESTURE_MODE.DRAG == mode)
{
// DO YOUR PANNING MOVED FAR LOGIC HERE
}
mode = GESTURE_MODE.NONE;
break;
}
return super.dispatchTouchEvent(event);
}
Related
I have an Imageview with different number of touch points on them. It basically an app which is detecting the swipe between 2 touch points and not allowing the user to swipe any other point or in or out of other direction. It should constrict user to just swipe between two touch points.
Just take a look at following picture:
Now the user should start swiping from point 1 to point 2. if the swipe is not started from starting point 1, it should not color the path between point 1 and point 2.
But if the user successfully swipe between the point 1 and point 2 now swipe between point 2 to 3 should be enabled. Thus user should go through Point 1 to 2, Point 2 to 3 , Point 3 to 4 , point 4 to point 5 to complete round 1.
Please tell me how to achieve this functionality . I know about gestures, gesture overlay etc but none of them fits to my condition as they uses general touch events and gesture directions.
Please suggest me the way to achieve this and keep in mind I want to make this app to be able to run on all type of devices , so I can simply give the hard coded x,y values.
Edit : on Demand
I am posting the link of the app on play store who has same functionality , But I do not know How they are achieving this functionality .
https://play.google.com/store/apps/details?id=al.trigonom.writeletters
If each touch point can be created as individual views(e.g. ImageView), then you can create an inViewInBounds() function.
I used code from here to create code where I needed to detect finger press movement over multiple imageViews:
Rect outRect = new Rect();
int[] location = new int[2];
//Determine if a touch movement is currently over a given view.
private boolean inViewInBounds(View view, int x, int y){
view.getDrawingRect(outRect);
view.getLocationOnScreen(location);
outRect.offset(location[0], location[1]);
return outRect.contains(x, y);
}
To use this function, on a parent view to all those child touch points, set the Click and Touch Listeners:
//This might look redundant but is actually required: "The empty OnClickListener is required to keep OnTouchListener active until ACTION_UP"
parentView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {}
});
//All the work gets done in this function:
parentView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
int x = (int)event.getRawX();
int y = (int)event.getRawY();
// ** myTouchPoint might be an array that you loop through here...
if ( inViewInBounds(myTouchPoint, x, y) ) doLogic(myTouchPoint);
return false;
}
});
The code above only shows detecting when one of your views are 'touched'.
if none are 'touched' but a view is 'active' (e.g. When a touch is detected, set a variable like: viewLastTouched = myTouchPoint) then you would call something like drawingLine(viewLastTouched, x, y) function - for whatever it needed to do to draw the line and/or detect boundaries etc.
They are not using android native java code to build this app.
The app is running with this code
import Runtime.MMFRuntime;
public class Main extends MMFRuntime {
}
This in turn is from https://github.com/ClickteamLLC/android/blob/master/docs/index.md
This is used to package apps / games written using - http://www.clickteam.com/clickteam-fusion-2-5
I am trying to make all my drawn Sprites dragable for a little game. It should be able to touch anywhere and the sprites should move the same distance, the finger moves.
With the following method they will move on an ACTION_MOVE event, but only very slow, a shorter distance and sometimes they dont:
addToX/Y only adds the gap to the coordinates of the sprites
#Override
public boolean onTouchEvent(MotionEvent evt){
switch(evt.getAction()){
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
if(getHistorySize() > 0){
for(int i = 1, n = evt.getHistorySize(); i < n; i++){
int calcX = (int) getHistoricalX(i) - (int) getHistoricalX(i-1);
int calcY = (int) getHistoricalY(i) - (int) getHistoricalY(i-1);
for(Sprite sprite : spriteList) {
sprite.addToX(calcX);
sprite.addToY(calcY);
}
}
}
return true;
}
Any ideas on this?
Assuming your Sprite class is an (potentially-indirect) extension of android.view.View, then you can use setOnDragListener() to define an onDrag() override for them. Then you can use startDrag(...) on them to begin the drag. This is typically triggered by a long-press gesture on the view to be dragged, but in your case you can trigger it from within onTouchEvent() in ACTION_MOVE once (or even ACTION_DOWN). See here for more details on these methods.
Also, with respect to the code you posted, one issue with it that probably explains why it doesn't always work is that you are only using the historical points (which may or may not have accumulated on any particular call to onTouchEvent()). Whether or not getHistorySize() is greater than 0, you should still also use evt.getX() and evt.getY() on each call to onTouchEvent(). But of course, if you use the drag listener approach I suggested instead, you won't need to worry about this.
Update per comment
If you want to move all of the sprites at once, you can put the sprites into a full-screen FrameLayout and attach a GestureDetector that uses a GestureDetector.SimpleOnGestureListener to capture onScroll() callbacks and then calls scrollTo() on the FrameLayout. When the parent FrameLayout scrolls, all of its children sprites will appear to move together.
I make online map and try to add touchable paths/tracks (I change their color when user touches them). On one map I have 6-7 PathOverlays with added onDown event handling:
private class PathOverlayExtended extends PathOverlay
{
public PathOverlayExtended(int color, Context ctx, long trackId, HistoryDetailFragment currentFragment) {
super(color, ctx);
trackIndex = trackId;
fragment = currentFragment;
}
private long trackIndex;
private HistoryDetailFragment fragment;
#Override
public boolean onDown(final MotionEvent event, final MapView mapView) {
fragment.onRoadClicked(trackIndex);
return super.onDown(event,mapView);
}
}
Then I touch one path on screen, it catches event and proceeds through every path. Important: it always starts from the same path (the one added to the Olerlays at the end).
When I replace "return super.onDown(event,mapView)" with "return true", only the last path catches the event and this is not the one I touch (but the one added to the Overlays at the end).
How to check/distinct which Path I touched?
I implemented something similar for detecting a touch on filled Polygons.
It's using Android Regions.
The principle is to "put" the Path that has been drawn in a "Region":
region.setPath(mPath, new Region((int) bounds.left, (int) bounds.top, (int) bounds.right, (int) bounds.bottom));
Then you check if the touched point is in this region with:
region.contains(point.x, point.y);
No idea how this "contains" method is implemented, but it works, and seems quite efficient. Magic. I imagine it should also work for polylines.
You can look at the full code here:
http://code.google.com/p/osmbonuspack/source/browse/trunk/OSMBonusPack/src/org/osmdroid/bonuspack/overlays/Polygon.java
I couldn't find fine solution to my problem, so in the end, I decided to do it in the following way.
Firstly, I made my own PathOverlayExtended class which iherits PathOverlay. Then I added some variables - bounds of the path region (maximum and minimum latitude and longitude).
Secondly, I checkced if tap coordinates fits in theese bounds. That way, I get only those paths which can be understanded as related to my tap.
In the end, I checked distances from tap coordinates to every line segment and chose the smallest one. That's it.
I used viesturz's answer, which helped me very much:
https://code.google.com/p/osmdroid/issues/detail?id=36
Thanks for all answers!
I have a problem with my app : I have a FrameLayout with a Fragment which contains a map from the Google Maps Android APi (v2), and a customized view which I use to display various buttons on top of the map.
I'd like some of these elements not to be shown while the map is being moved, for this I use the onTouchEvent() function of the View :
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
pressed = true;
invalidate();
break;
case MotionEvent.ACTION_UP:
pressed = false;
invalidate();
break;
}
return false;
}
The onDraw() function only displays the elements drawn over the map if pressed == false;
Now, my problem is in the return : if I put false, I say the event wasn't consumed, and the Android OS no longer notifies me when touch events are recorded, thus the MotionEvent.ACTION_UP isn't detected and the buttons stay hidden. If I put true, the touch events are no longer detected this time by the GoogleMap object, and I can't move the map around. Is there a way to keep detecting all future touch events, while not consuming the first event ?
Use onInterceptTouchEvent or dispatchTouchEvent.
You may read about the differences here: Android: Difference between onInterceptTouchEvent and dispatchTouchEvent?
At middle of 2011 set mapView.setSattelite(true) will only show satellite view, but recently we find the same set will also show with roads and names above satelliete view.
May be it's very useful in other country, but in China, when add roads and names above satelliete view, the roads are not on the right position.
Can some one tell me how to remove roads and names above satelliete view?
How to show satellite view of Android mapview only.
I read Class MapView:
setSatellite(boolean on)
Sets the map mode to "satellite" mode, loading tiles of aerial imagery with roads and names superimposed.
Can someone help me out? Help us live on Mars!
public void myClickHandler(View target) {
switch(target.getId()) {
/*case R.id.zoomin:
mapView.getController().zoomIn();
break;
case R.id.zoomout:
mapView.getController().zoomOut();
break;*/
case R.id.sat:
this.mapView.setSatellite(true);
break;
case R.id.street:
this.mapView.setStreetView(true);
break;
case R.id.traffic:
this.mapView.setTraffic(true);
break;
case R.id.normal:
this.mapView.setSatellite(false);
this.mapView.setStreetView(false);
this.mapView.setTraffic(false);
break;
default:
break;
}
// The following line should not be required but it is,
// at least up til Froyo.
this.mapView.postInvalidateDelayed(2000);
}
i believe you need setStreetView, even though it says it's deprecated it works ok for me
#Deprecated
public void setStreetView(boolean on)
Deprecated. Street view availability highlighting is no longer supported. This method operates as a no-op.
Control whether Street View availability (blue outlines) is shown on the map. This is incompatible with Traffic indicators, so they will be deactivated if necessary. Street View availability can be drawn over map tiles or over satellite tiles; however, they are optimized for map tiles.
I don't believe this can be accomplished, because when you are using google mapview, the images come from google themself for road map and sattelite map, unless they do an update for the API to show only sattelite images without road names.