MarkersBalloonOverlay - Customise the Balloon
You can customise the appearance and functionality of the Balloon.
To get started take the code from the
MarkersBalloonOverlay example, add an EventName to the MarkersBalloonOverlay1 Initialize call and add these two Subs:
MarkersBalloonOverlay1.Initialize(MapView1, "MarkersBalloonOverlay1", "balloon_overlay1")
Sub MarkersBalloonOverlay1_BalloonClick(ViewTag As String)
Log("The Balloon View with Tag of '"&ViewTag&"' was Clicked")
End Sub
Sub MarkersBalloonOverlay1_BalloonLongClick(ViewTag As String)
Log("The Balloon View with Tag of '"&ViewTag&"' was LongClicked")
End Sub
BalloonClick and
BalloonLongClick are the two events that MarkersBalloonOverlay can generate.
Add those two Subs to the previous example and run the project.
Open a Balloon and tap within it, look at the B4A log - no events are generated.
We need to modify the (read-only)
balloon_overlay.xml file.
So make that file writeable and open it in a text editor.
We want to add a
tag attribute to a View:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="35dip"
android:paddingLeft="10dip"
android:minWidth="200dip"
android:id="@+id/balloon_main_layout"
android:background="@drawable/balloon_overlay_bg_selector"
android:paddingTop="0dip"
android:paddingRight="0dip">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_weight="1"
android:paddingTop="10dip"
android:id="@+id/balloon_inner_layout">
<TextView android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:id="@+id/balloon_item_title"
android:text="balloon_item_title"
android:textSize="16dip"
android:textColor="#FF000000">
</TextView>
<TextView android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:id="@+id/balloon_item_snippet"
android:text="balloon_item_snippet"
android:textSize="12dip"
android:tag="MyDescriptionTag">
</TextView>
</LinearLayout>
<ImageView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/balloon_overlay_close"
android:id="@+id/close_img_button"
android:paddingLeft="10dip"
android:paddingBottom="10dip"
android:paddingRight="8dip"
android:paddingTop="8dip">
</ImageView>
</LinearLayout>
I've simply added
android:tag="MyDescriptionTag" to the TextView with an id of
balloon_item_snippet.
Save the XML file, make it read-only and run the project.
Open a Balloon and tap the description text, look at the log and you'll see the BalloonClick event is now generated.
A long tap will cause the BalloonLongClick event to be generated.
Both event handler Subs are passed the String which you assigned to the View as it's tag attribute.
Log output:
The Balloon View with Tag of 'MyDescriptionTag' was Clicked
The Balloon View with Tag of 'MyDescriptionTag' was LongClicked
Now let's do something more useful, add a new TextView to the Balloon layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="35dip"
android:paddingLeft="10dip"
android:minWidth="200dip"
android:id="@+id/balloon_main_layout"
android:background="@drawable/balloon_overlay_bg_selector"
android:paddingTop="0dip"
android:paddingRight="0dip">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_weight="1"
android:paddingTop="10dip"
android:id="@+id/balloon_inner_layout">
<TextView android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:id="@+id/balloon_item_title"
android:text="balloon_item_title"
android:textSize="16dip"
android:textColor="#FF000000">
</TextView>
<TextView android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:id="@+id/balloon_item_snippet"
android:text="balloon_item_snippet"
android:textSize="12dip"
android:tag="MyDescriptionTag">
</TextView>
<TextView android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:text="Click here for more information"
android:textSize="14dip"
android:tag="MoreInfoTag"
android:textColor="#F00">
</TextView>
</LinearLayout>
<ImageView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/balloon_overlay_close"
android:id="@+id/close_img_button"
android:paddingLeft="10dip"
android:paddingBottom="10dip"
android:paddingRight="8dip"
android:paddingTop="8dip">
</ImageView>
</LinearLayout>
I've added a new TextView with a tag of
MoreInfoTag, it displays the text
Click here for more information and the text is colored red.
Save the XML file, make it read-only and run the project.
Open a Balloon and there's the 'Click here for more information' text underneath the description text.
Click the new text and look at the log - the BalloonClick event is generated and the 'MoreInfoTag' String passed to the event handler Sub.
The Balloon View with Tag of 'MoreInfoTag' was Clicked
So how can we make use of that event?
We need to know which Marker's information was being displayed, we already have a
FocusedMarker variable but that's only currently updated in Activity_Pause.
Let's update FocusedMarker in the BalloonClick Sub and use a Select code block to process the
ViewTag:
Sub MarkersBalloonOverlay1_BalloonClick(ViewTag As String)
Log("The Balloon View with Tag of '"&ViewTag&"' was Clicked")
FocusedMarker=MarkersBalloonOverlay1.FocusedMarker
Select ViewTag
Case "MyDescriptionTag"
Log("Do something")
Case "MoreInfoTag"
Log("Show more information for FocusedMarker")
End Select
End Sub
So that's the basic theory, lets do something useful.
Start with the original
balloon_overlay.xml file - discard the previous changes.
We'll add a new TextView much like the 'Click here for more information' TextView but this time we'll create a 'Zoom in' function:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="35dip"
android:paddingLeft="10dip"
android:minWidth="200dip"
android:id="@+id/balloon_main_layout"
android:background="@drawable/balloon_overlay_bg_selector"
android:paddingTop="0dip"
android:paddingRight="0dip">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_weight="1"
android:paddingTop="10dip"
android:id="@+id/balloon_inner_layout">
<TextView android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:id="@+id/balloon_item_title"
android:text="balloon_item_title"
android:textSize="14dip"
android:textColor="#FF000000">
</TextView>
<TextView android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:id="@+id/balloon_item_snippet"
android:text="balloon_item_snippet"
android:textSize="12dip"
android:textColor="#CCC">
</TextView>
<TextView android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:text="Zoom in"
android:textSize="14dip"
android:tag="ZoomIn"
android:textColor="#F00">
</TextView>
</LinearLayout>
<ImageView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/balloon_overlay_close"
android:id="@+id/close_img_button"
android:paddingLeft="10dip"
android:paddingBottom="10dip"
android:paddingRight="8dip"
android:paddingTop="8dip">
</ImageView>
</LinearLayout>
I've changed the
balloon_item_title textSize to 14dip, the
balloon_item_snippet textColor to grey (#CCC) and added the new
Zoom in TextView.
Update the BalloonClick Sub:
Sub MarkersBalloonOverlay1_BalloonClick(ViewTag As String)
FocusedMarker=MarkersBalloonOverlay1.FocusedMarker
Select ViewTag
Case "ZoomIn"
If MapView1.Zoom<MapView1.GetMaxZoomLevel Then
MapView1.Zoom=MapView1.GetMaxZoomLevel
MapView1.AnimateTo3(FocusedMarker.GeoPoint)
End If
End Select
End Sub
Make sure your new XML file is saved and read-only then run the project.
Open a Balloon and try out the new Zoom in function.
Say you want a 'Zoom in' ImageView instead of a 'Zoom in' TextView.
There is a drawable named
center.png included in the
drawable-nodpi folder, it not the same size as the
balloon_overlay_close.png but let's not worry about that for now:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="35dip"
android:paddingLeft="10dip"
android:minWidth="200dip"
android:id="@+id/balloon_main_layout"
android:background="@drawable/balloon_overlay_bg_selector"
android:paddingTop="0dip"
android:paddingRight="0dip">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_weight="1"
android:paddingTop="10dip"
android:id="@+id/balloon_inner_layout">
<TextView android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:id="@+id/balloon_item_title"
android:text="balloon_item_title"
android:textSize="14dip"
android:textColor="#FF000000">
</TextView>
<TextView android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:id="@+id/balloon_item_snippet"
android:text="balloon_item_snippet"
android:textSize="12dip"
android:textColor="#CCC">
</TextView>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/balloon_overlay_close"
android:id="@+id/close_img_button"
android:paddingLeft="10dip"
android:paddingBottom="4dip"
android:paddingRight="8dip"
android:paddingTop="8dip">
</ImageView>
<ImageView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/center"
android:paddingLeft="10dip"
android:paddingBottom="8dip"
android:paddingRight="8dip"
android:paddingTop="4dip"
android:tag="ZoomIn">
</ImageView>
</LinearLayout>
</LinearLayout>
Enclosing the original balloon__overlay_close ImageView in a new LinearLayout with vertical orientation enables us to add the ZoomIn ImageView so it displays under the balloon_overlay_close ImageView.
No changes are required to the Balloon_Click Sub, save the new XML file and run the project - you can now zoom in using the center.png drawable instead of the previous TextView.
So to summarise:
- MarkersBalloonOverlay will add Click and LongClick event listeners to any View in balloon layout file that has a tag defined, but:
- Click event listeners will only be added if your Activity contains the related BalloonClick Sub.
- LongClick event listeners will only be added if your Activity contains the related BalloonLongClick Sub.
- You must not remove the balloon_item_title, balloon_item_snippet or close_img_button Views from the balloon layout.
- The MarkersBalloonOverlay FocusedMarker property can be used to get the Marker whose Balloon is currently being displayed and the same property can be used to programmatically open a Marker's Balloon.
- You can use any valid Layouts and Widgets in the balloon layout file - your imagination is the limit!
With the release of version 3.00 of OSMDroid i feel that it is now capable of producing professional quality maps.
I am working on a couple of apps that extensively use OSMDroid and as i develop these apps i shall no doubt add new features to the library and fix any bugs...
Have fun and feel free to share any custom balloon layouts that you create.
Martin.