I have an application that uses the mapview-overlay-manager code to draw map markers on a MapView using the LazyLoadManager from a web api. As I drag the map, the markers load/unload as expected.
On the tap of a marker I inflate a balloon.xml file and use it to show a balloon above the marker. This is where the problem is. It works, but then suddenly (which I cannot repeat on a consistently) the balloon overlay will stop showing up on the screen.
Its odd though, because the marker still shows that its been tapped, but then the balloon stops showing up. I've checked that the balloon is not null (which it is not), and that the itemInfo is not null. Its just not getting added to the MapView after the call to .addView(...), yet all the params are valid.
Side note: Anytime this happens, all of the overlays turn real dark and the overlay shadows go from semi-transparent to black. I have no idea what is causing that, but it happens at the same time, which makes me believe its a drawing problem.
Code for the above problem is below. Any tips/ideas/etc would be appreciated.
#Override
public boolean onSingleTap(MotionEvent e, ManagedOverlay overlay, GeoPoint point, ManagedOverlayItem item) {
if(mBalloon != null)
{
mMapView.removeView(mBalloon);
mBalloon = null;
}
if(item != null) {
//Toast.makeText(getApplicationContext(), item.getTitle(), Toast.LENGTH_SHORT).show();
MapView.LayoutParams balloonLayoutParams = new MapView.LayoutParams(350, MapView.LayoutParams.WRAP_CONTENT, item.getItemInfo().getMarkerPoint(mMapView.getProjection()), MapView.LayoutParams.BOTTOM_CENTER);
if(mBalloon == null) {
if(mLayoutInflater == null) {
mLayoutInflater = getLayoutInflater();
}
ViewGroup parent = (ViewGroup)mMapView.getParent();
mBalloon = (BalloonLayout) mLayoutInflater.inflate(R.layout.balloon_layout, parent, false);
}
TextView title = (TextView)mBalloon.findViewById(R.id.title);
title.setText(item.getItemInfo().getTitle());
TextView subTitle = (TextView)mBalloon.findViewById(R.id.subTitle);
subTitle.setText(item.getItemInfo().getBalloonSubTitle());
if(DEBUG) Log.d(TAG, "Setting on click listener.");
((ImageButton)mBalloon.findViewById(R.id.go_button)).setOnClickListener(new ViewItemInfoListener(item.getItemInfo()));
mMapView.addView(mBalloon, balloonLayoutParams);
}
return false;
}
});
// Fires off the background event to get the
overlayManager.populate();
}
Have you considered during an OnDrag: removing all the markers and save in a temporary list, start a timer (prob 200-500 ms), and then after the timer has expired repopulate the markers. If another OnDrag occurs before the timer expires, then restart the timer.
Ok, I found the problem. There is a parent method calling this method. Unfortunately this method was being called twice. Once on the onFocusChanged() and once in onCreate(). Removing one of them cured the issue. The icons and balloons were double drawing themselves because of this.
Related
I want to add a custom layout Infowindow on top of the marker. Android Here map Basic pack was having the following features Map.InfoBubbleAdapter() ,hideInfoBubble ,getInfoBubbleContents and isInfoBubbleVisible which are now missing in the Premium pack .
As suggested in Here Map Premium plan Map.InfoBubbleAdapter() ,hideInfoBubble ,getInfoBubbleContents and isInfoBubbleVisible missing?
This are the list of issues.
I have implemented MapOverlay which seems to give custom layout
but its not aligned to the marker.
The infoWindow is getting visible when i am trying to zoom in,even when i have not tuned on the visibility of layout.
The position of infoWindow is not on the geoCordinate that were set to mapOverlay.
I have 20 different marker and i want to show the custom info window when user taps on any marker and turn off the previous marker .
I tried the code below but its not working and there seems no documentation on implementation.
#Override
public boolean onMapObjectsSelected(List<ViewObject> objects) {
for (ViewObject object : objects) {
if (object.getBaseType() == ViewObject.Type.USER_OBJECT && ((MapObject) object).getType() == MapObject.Type.MARKER) {
MapMarker mapMarker = (MapMarker) object;
if(!isMapMarkerViewVisible()){
setInfoWindow(mapMarker);
}else{
removeInfoWindow();
}
return true;
}
}
return false;
}
#Override
public boolean onTapEvent(PointF pointF) {
for (MapObject mapObject : mapObjects) {
if (mapObject.getType() == MapObject.Type.MARKER) {
removeInfoWindow();
}
}
return super.onTapEvent(pointF);
}
private void setInfoWindow(MapMarker mMark) {
infoWindowText.setText(mMark.getTitle());
infoWindowText.setTag(mMark.getTitle());
mapOverlay = new MapOverlay(infoWindowView, mMark.getCoordinate());
hereMap.addMapOverlay(mapOverlay);
}
private boolean isMapMarkerViewVisible(){
return infoWindowView.getVisibility() == View.VISIBLE;
}
private void removeInfoWindow() {
if(infoWindowView!=null) {
if (infoWindowView.getVisibility() == View.VISIBLE) {
infoWindowView.setVisibility(View.INVISIBLE);
}
}
}
The above code behaves strangely .
When i try to zoom in and out the info window strangely appears on the map .Even when i have not set the visibility.
The Above link have the code that was working but its not supporting now.
Different from MapMarkers, the MapLabeledMarkers avoid overlapping with other user-defined labeled icons as well as built-in icons and texts on the map. It provides the possibility of using built-in POI icons by specifying a IconCategory or using an Image. One can specify multilingual texts to the icon. Depending on the current language and secondary language of Map, different language of text can be displayed.
MapLabeledMarker(GeoCoordinate loc)
Creates a MapLabeledMarker at a specific GeoCoordinate with a default POI icon.
MapLabeledMarker(GeoCoordinate loc, Image icon)
Creates a MapLabeledMarker at a specific GeoCoordinate with the specified Image.
For more detailed reference please refer & upgrade to newest version of SDK : https://developer.here.com/documentation/android-premium/api_reference_java/com/here/android/mpa/mapping/MapLabeledMarker.html
I am trying to show the custom layout when user clicks on the marker object.
The basic pack code works properly .
However when i shifted to premium pack it does not have
Map.InfoBubbleAdapter(),hideInfoBubble,getInfoBubbleContents and isInfoBubbleVisible.
I am not even able to open the infoBubble when user click on marker too.
This is the code for custom view in basic pack i used.
hereMap.get(new Map.InfoBubbleAdapter() {
#Override
public View getInfoBubbleContents(MapMarker mapMarker2) {
return null;
}
#Override
public View getInfoBubble(MapMarker mapMarker2) {
View view = LayoutInflater.from(getContext()).inflate(R.layout.layout_marker_infowindow, null);
TextView infoWindowText = view.findViewById(R.id.infoMessage);
infoWindowText.setText(mapMarker2.getTitle());
return view;
}
});
and this is the code that i was using on click of marker
#Override
public boolean onMapObjectsSelected(List<ViewObject> objects) {
for (ViewObject object : objects) {
if (object.getBaseType() == ViewObject.Type.USER_OBJECT && ((MapObject) object).getType() == MapObject.Type.MARKER) {
MapMarker mapMarker = (MapMarker) object;
System.out.println("Title is................." + mapMarker.getTitle());
if (!mapMarker.isInfoBubbleVisible()) {
mapMarker.getInfoBubbleContents();
} else {
mapMarker.hideInfoBubble();
}
return true;
}
}
How can i show the custom info bubble when user click on the marker?
You should use sticky map overlays instead:
https://developer.here.com/documentation/android-premium/api_reference_java/com/here/android/mpa/mapping/MapOverlay.html
Then add your MapOverlay implementation to the Map via Map.addMapOverlay:
https://developer.here.com/documentation/android-premium/api_reference_java/com/here/android/mpa/mapping/Map.html#addMapOverlay-com.here.android.mpa.mapping.MapOverlay-
The idea behind is, that you can use a normal Views (very flexible, your own design, etc.) and stick it to a position on the map (and the map will keep it in place then). This gives you full flexibility on logic and design.
I am trying to allow a User to remove a point (or more specifically, an OverlayItem) from a map. I followed the developer tutorial to get started and implemented the CustomMapView in this tutorial to capture a long press on the map.
So now I have a program which allows a User to place points on the map. My next goal is to let the User remove points. Here is my code for when a User clicks an existing point on the map.
public class OurItemizedOverlay extends ItemizedOverlay {
//Create new list of points
private ArrayList<OverlayItem> mapOverlays = new ArrayList<OverlayItem>();
private Context mapContext;
#Override
protected boolean onTap(final int index) {
Button edit, remove;
//Get index of item tapped
OverlayItem item = mapOverlays.get(index);
//Create Dialog to show point info, allow for edit or removal.
LinearLayout layout = new LinearLayout(mapContext);
layout.setOrientation(LinearLayout.VERTICAL);
LayoutInflater inflater = LayoutInflater.from(mapContext);
AlertDialog.Builder builder = new AlertDialog.Builder(mapContext);
builder.setTitle(item.getTitle());
builder.setMessage(item.getSnippet());
View view = inflater.inflate(R.layout.view_or_edit_location_dialog, null);
builder.setView(view);
builder.show();
//BUTTONS
edit = (Button)view.findViewById(R.id.edit);
remove = (Button)view.findViewById(R.id.delete);
//Edit Button Listener
edit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
//Remove Button Listener
remove.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
removeOverlay(index); <--------
Log.d("View location info", "user clicked delete.");
return;
}
});
return true;
}
Here is my code for removeOverlay.
protected void removeOverlay(int index) {
mapOverlays.remove(index);
com.example.mapproject.MainActivity.mapView.invalidate();
}
After I click on an existing point a dialog is presented offering to remove the point. When I have selected to remove the point, the point still remains on the screen. If I place a new point, the 'deleted' one is removed. However, if I click the 'deleted' point or another existing point, the program crashes with this error.
If you have a clue of what to do, I'd appreciate to hear from you !!
Edit
Following a tip from Vishwa Patel, I remove a point from the map straight away using postInvalidate(). However, I still get indexoutofbounds exceptions when I click where the icon was..
You probably need to call invalidate() on your MapView, forcing it to re-draw itself. As the commenters mentioned, you may also need to re-call populate(). Your app is probably crashing because it's trying to call onTap() for an OverlayItem that doesn't exist. You may also want to try any method that could "refresh" the MapView and/or Overlay, because that is what you need to do to make the OverlayItem disappear.
Try using
com.example.mapproject.MainActivity.mapView.postInvalidate();
since you are making an invalidate call from a non-UI thread, as specified in the documentation for postInvalidate();
I believe I found the solution here. It seems to work so far, the answer was to put the following line into my removeOverlay method,
setLastFocusedIndex(-1);
The code to remove an OverylayItem from my custom Overlay is,
protected void removeOverlay(OverlayItem overlayItem) {
mapOverlays.remove(overlayItem);
MainActivity.mapOverlays.remove(this);
setLastFocusedIndex(-1);
populate();
}
Any thoughts/suggestions are welcome!
My app has a ListView and an EditText sitting below it. For some reason, the TAB key doesn't trigger the onKeyListener. All other keys I'm handling (DEL, ENTER, DPAD_UP/DOWN/CENTER) are received just fine. I added a breakpoint in dispatchKeyEvent, again no luck receiving TAB events.
My app previously had a large TextView for displaying text and during this time, TAB events were received fine. The ListView has now replaced the TextView.
I'm completely mystified as to why the TAB event is no longer being received. This is on a stock Xoom, running ICS 4.0.4 & stock N1, with 2.3.6.
I've compared my current code against the version using a TextView and much of the code is just to handle the ListView in the place of the TextView. Apart from nextFocusLeft and nextFocusRight attributes, nothing else has changed for the EditText.
Edit: I just tried with Go Keyboard and Hacker's Keyboard and TAB is received fine. It appears this is with just some virtual keyboards
I think I may see the problem. Looking at the source for ListView.java, there is a mechanism to consume key events that shift focus within a list item. Check out the comments preceding this method as well as the block of comments in the middle of the method.
/**
* To avoid horizontal focus searches changing the selected item, we
* manually focus search within the selected item (as applicable), and
* prevent focus from jumping to something within another item.
* #param direction one of {View.FOCUS_LEFT, View.FOCUS_RIGHT}
* #return Whether this consumes the key event.
*/
private boolean handleHorizontalFocusWithinListItem(int direction) {
if (direction != View.FOCUS_LEFT && direction != View.FOCUS_RIGHT) {
throw new IllegalArgumentException("direction must be one of"
+ " {View.FOCUS_LEFT, View.FOCUS_RIGHT}");
}
final int numChildren = getChildCount();
if (mItemsCanFocus && numChildren > 0 && mSelectedPosition != INVALID_POSITION) {
final View selectedView = getSelectedView();
if (selectedView != null && selectedView.hasFocus() &&
selectedView instanceof ViewGroup) {
final View currentFocus = selectedView.findFocus();
final View nextFocus = FocusFinder.getInstance().findNextFocus(
(ViewGroup) selectedView, currentFocus, direction);
if (nextFocus != null) {
// do the math to get interesting rect in next focus' coordinates
currentFocus.getFocusedRect(mTempRect);
offsetDescendantRectToMyCoords(currentFocus, mTempRect);
offsetRectIntoDescendantCoords(nextFocus, mTempRect);
if (nextFocus.requestFocus(direction, mTempRect)) {
return true;
}
}
// we are blocking the key from being handled (by returning true)
// if the global result is going to be some other view within this
// list. this is to acheive the overall goal of having
// horizontal d-pad navigation remain in the current item.
final View globalNextFocus = FocusFinder.getInstance().findNextFocus(
(ViewGroup) getRootView(), currentFocus, direction);
if (globalNextFocus != null) {
return isViewAncestorOf(globalNextFocus, this);
}
}
}
return false;
}
Are there multiple focusable items within a single list element? If so, this code will consume the tab key. If that is the case, then you may want to make some of the items unfocusable or consider another design option.
On the default ListViews if you long press on an item the background fades from dark blue to light blue(on Galaxy Tab. Its orange to light orange on some other devices) during the time it takes to register a LongClick. I am assuming this is done somehow with a selector but thus far I've only seen how to use selectors for state:selected, state:focused, and state:pressed.
This page doesn't seem to show anything about a LongClick state so perhaps my assumption that this is accomplished with a selector is incorrect?
Can anyone fill me in on how the default ListView gets this effect and how I can apply it to other views?
So it turned out to be a little bit more difficult than I had thought but I have it working almost correctly now.
Here is the OnTouchListener I ended up using:
listOnTouchListener = new OnTouchListener() {
public boolean onTouch(View v, MotionEvent me){
if (me.getAction() == MotionEvent.ACTION_DOWN){
//This means a finger has come down on top of our view
//We are going to start the animation now.
Log.i(myTag, "Action Down");
Context mContext = getApplicationContext();
Resources res = mContext.getResources();
TransitionDrawable transition = (TransitionDrawable) res.getDrawable(R.drawable.listtransition);
v.setBackgroundDrawable(transition);
//LongClick took approx. 510-530 milliseconds to register after OnTouch. So I set the duration at 500 millis.
transition.startTransition(500);
}else if (me.getAction() == MotionEvent.ACTION_UP){
//This means we didn't hold long enough to get a LongClick
//Set the background back to the normal one.
v.setBackgroundResource(R.drawable.bubblelight);
}else if (me.getAction() == MotionEvent.ACTION_MOVE){
//Do Nothing
}else if (me.getAction() == MotionEvent.ACTION_CANCEL){
Log.i(myTag, "Action Cancel");
//This means we are scrolling on the list, not trying to longpress
//So set the background back to the normal one.
v.setBackgroundResource(R.drawable.bubblelight);
}
return false;
}
};
I also used an OnLongClickListener inside this I set the background back to the normal one.
Here is the Transition XML:
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/bubblelight1" />
<item android:drawable="#drawable/bubbledark" />
</transition>
You may be asking whats with the bubblelight1? More on that in a moment.
Here is the getView() Method I use inside my adapter to return the views that get displayed in the List:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.row, null);
}
Status s = items.get(position);
if (s != null) {
v.setOnTouchListener(listOnTouchListener);
v.setOnLongClickListener(listOnLongClickListener);
tweetTxt = (TextView) v.findViewById(R.id.tweetTxt);
timeTxt = (TextView) v.findViewById(R.id.timeTxt);
if (tweetTxt != null) {
tweetTxt.setText(s.getText());
tweetTxt.setOnTouchListener(gestureListener);
}
if(timeTxt != null){
timeTxt.setText(getTimeFromNow(s.getCreatedAt().getTime()));
//timeTxt.setText(s.getCreatedAt().toLocaleString());
}
}
LinkifyWithTwitter.addLinks(tweetTxt, Linkify.ALL);
return v;
}
v.setOnTouchListener(listOnTouchListener); and
v.setOnLongClickListener(listOnLongClickListener); are the lines that set up the view with the Listeners that I've shown above.
Now about the bubblelight1. bubblelight and bubbledark are the nine patch images I am using when I tried this the first time whenever the transition started instead of the background transitioning from bubblelight to bubbledark, bubblelight would grow bigger and bubbledark would appear inside of bubblelight. So I had a big bubble that was light, a smaller bubble that was dark, then the text inside that. To fix this issue I made a copy of bubblelight and made the bottom and right edges of the ninepatch completely filled in. I just had to do the first image in the transition though, not the second. If I did the second image that way then my text would jump out of the bubble and some it would get shown over the top and along the sides of the bubble. I am not entirely sure why this was happening, or why this fix happened to work. But It does the job for now.
http://developer.android.com/guide/topics/ui/menus.html#context-menu
When the user performs a long-press on an item in a ListView and the list is registered to provide a context menu, the list item signals to the user that a context menu is available by animating its background color—it transitions from orange to white before opening the context menu. (The Contacts application demonstrates this feature.)
So its an animation that is used. I believe it uses the View's own default On...() methods to display it. You may need to worry about giving it clickable="true" or longClickable="true" attributes.
In addition to your OnTouchListener, you can use a Runnable to revert back the background to it's original state so that you don't need to explicitly do it in the OnLongClick handler.
Handler myHandler = new Handler();
...
transition.startTransition(ViewConfiguration.getLongPressTimeout());
ResetBG r = new ResetBG(transition);
myHandler.postDelayed(r, ViewConfiguration.getLongPressTimeout());
where ResetBG is:
class ResetBG implements Runnable {
protected TransitionDrawable myTran;
public Runnable(TransitionDrawable tran) {
myTran = tran;
}
public void run() {
myTran.resetTransition();
}
}