
com.qozix.tileview.TileView Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of TileView Show documentation
Show all versions of TileView Show documentation
The TileView widget is a subclass of ViewGroup that provides a mechanism to asynchronously display tile-based images, with additional functionality for 2D dragging, flinging, pinch or double-tap to zoom, adding overlaying Views (markers), built-in Hot Spot support, dynamic path drawing, multiple levels of detail, and support for any relative positioning or coordinate system.
The newest version!
package com.qozix.tileview;
import java.util.HashSet;
import java.util.List;
import android.content.Context;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.view.View;
import com.qozix.layouts.AnchorLayout;
import com.qozix.layouts.TranslationLayout;
import com.qozix.layouts.ZoomPanLayout;
import com.qozix.tileview.detail.DetailLevelEventListener;
import com.qozix.tileview.detail.DetailLevelPatternParser;
import com.qozix.tileview.detail.DetailManager;
import com.qozix.tileview.geom.PositionManager;
import com.qozix.tileview.graphics.BitmapDecoder;
import com.qozix.tileview.graphics.BitmapDecoderHttp;
import com.qozix.tileview.hotspots.HotSpot;
import com.qozix.tileview.hotspots.HotSpotEventListener;
import com.qozix.tileview.hotspots.HotSpotManager;
import com.qozix.tileview.markers.CalloutManager;
import com.qozix.tileview.markers.MarkerEventListener;
import com.qozix.tileview.markers.MarkerManager;
import com.qozix.tileview.paths.DrawablePath;
import com.qozix.tileview.paths.PathHelper;
import com.qozix.tileview.paths.PathManager;
import com.qozix.tileview.samples.SampleManager;
import com.qozix.tileview.tiles.TileManager;
import com.qozix.tileview.tiles.TileRenderListener;
import com.qozix.tileview.tiles.selector.TileSetSelector;
import com.qozix.tileview.tiles.selector.TileSetSelectorMinimalUpScale;
/**
* The TileView widget is a subclass of ViewGroup that provides a mechanism to asynchronously display tile-based images,
* with additional functionality for 2D dragging, flinging, pinch or double-tap to zoom, adding overlaying Views (markers),
* built-in Hot Spot support, dynamic path drawing, multiple levels of detail, and support for any relative positioning or
* coordinate system.
*
* A minimal implementation:
*
* {@code
* TileView tileView = new TileView(this);
* tileView.setSize(3000,5000);
* tileView.addDetailLevel(1.0f, "path/to/tiles/%col%-%row%.jpg");
* }
*
* A more advanced implementation might look like:
* {@code
* TileView tileView = new TileView(this);
* tileView.setSize(3000,5000);
* tileView.addTileViewEventListener(someMapEventListener);
* tileView.defineRelativeBounds(42.379676, -71.094919, 42.346550, -71.040280);
* tileView.addDetailLevel(1.000f, "tiles/boston-1000-%col%_%row%.jpg", 256, 256);
* tileView.addDetailLevel(0.500f, "tiles/boston-500-%col%_%row%.jpg", 256, 256);
* tileView.addDetailLevel(0.250f, "tiles/boston-250-%col%_%row%.jpg", 256, 256);
* tileView.addDetailLevel(0.125f, "tiles/boston-125-%col%_%row%.jpg", 128, 128);
* tileView.addMarker(someView, 42.35848, -71.063736);
* tileView.addMarker(anotherView, 42.3665, -71.05224);
* tileView.addMarkerEventListener(someMarkerEventListener);
* }
*
*/
public class TileView extends ZoomPanLayout {
private HashSet tileViewEventListeners = new HashSet();
private DetailManager detailManager = new DetailManager();
private PositionManager positionManager = new PositionManager();
private HotSpotManager hotSpotManager = new HotSpotManager( detailManager );
private SampleManager sampleManager;
private TileManager tileManager;
private PathManager pathManager;
private MarkerManager markerManager;
private CalloutManager calloutManager;
/**
* Constructor to use when creating a TileView from code. Inflating from XML is not currently supported.
* @param context (Context) The Context the TileView is running in, through which it can access the current theme, resources, etc.
*/
public TileView( Context context ) {
super( context );
sampleManager = new SampleManager( context, detailManager );
addView( sampleManager );
tileManager = new TileManager( context, detailManager );
addView( tileManager );
pathManager = new PathManager( context, detailManager );
addView( pathManager );
markerManager = new MarkerManager( context, detailManager );
addView( markerManager );
calloutManager = new CalloutManager( context, detailManager );
addView( calloutManager );
detailManager.addDetailLevelEventListener( detailLevelEventListener );
tileManager.setTileRenderListener( renderListener );
addZoomPanListener( zoomPanListener );
addGestureListener( gestureListener );
requestRender();
}
//------------------------------------------------------------------------------------
// PUBLIC API
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
// Event Management API
//------------------------------------------------------------------------------------
/**
* Register an event listener callback object for this TileView.
* Note this is method adds a listener to an array of listeners, and does not set
* a single listener member a single listener.
* @param listener (TileViewEventListener) an implementation of the TileViewEventListener interface
*/
public void addTileViewEventListener( TileViewEventListener listener ) {
tileViewEventListeners.add( listener );
}
/**
* Removes a TileViewEventListener object from those listening to this TileView.
* @param listener (TileViewEventListener) an implementation of the TileViewEventListener interface
*/
public void removeTileViewEventListener( TileViewEventListener listener ) {
tileViewEventListeners.remove( listener );
}
//------------------------------------------------------------------------------------
// Rendering API
//------------------------------------------------------------------------------------
/**
* Request that the current tile set is re-examined and re-drawn.
* The request is added to a queue and is not guaranteed to be processed at any particular
* time, and will never be handled immediately.
*/
public void requestRender(){
tileManager.requestRender();
}
/**
* Notify the TileView that it may stop rendering tiles. The rendering thread will be
* sent an interrupt request, but no guarantee is provided when the request will be responded to.
*/
public void cancelRender() {
tileManager.cancelRender();
}
/**
* Enables or disables tile image caching (in-memory and on-disk)
* @param shouldCache (boolean) true to enable caching, false to disable it (default)
*/
public void setCacheEnabled( boolean shouldCache ) {
tileManager.setCacheEnabled( shouldCache );
}
/**
* Sets a custom class to perform the decode operation when tile bitmaps are requested.
* By default, a BitmapDecoder implementation is provided that renders bitmaps from the context's Assets,
* but alternative implementations could be used that fetch images via HTTP, or from the SD card, or resources, SVG, etc.
* This signature is identical to calling setTileDecoder and setDownsampleDecoder with the same decoder instance as the parameter.
* {@link BitmapDecoderHttp} is an example of such an implementation.
* @param decoder (BitmapDecoder) A class instance that implements BitmapDecoder, and must define a decode method, which accepts a String file name and a Context object, and returns a Bitmap
*/
public void setDecoder( BitmapDecoder decoder ) {
setTileDecoder( decoder );
setDownsampleDecoder( decoder );
}
/**
* Sets a custom class to perform the decode operation when tile bitmaps are requested for tile images only.
* By default, a BitmapDecoder implementation is provided that renders bitmaps from the context's Assets,
* but alternative implementations could be used that fetch images via HTTP, or from the SD card, or resources, SVG, etc.
* {@link BitmapDecoderHttp} is an example of such an implementation.
* @param decoder (BitmapDecoder) A class instance that implements BitmapDecoder, and must define a decode method, which accepts a String file name and a Context object, and returns a Bitmap
*/
public void setTileDecoder( BitmapDecoder decoder ) {
tileManager.setDecoder( decoder );
}
/**
* Sets a custom class to perform the decode operation when tile bitmaps are requested for downsample images only.
* By default, a BitmapDecoder implementation is provided that renders bitmaps from the context's Assets,
* but alternative implementations could be used that fetch images via HTTP, or from the SD card, or resources, SVG, etc.
* {@link BitmapDecoderHttp} is an example of such an implementation.
* @param decoder (BitmapDecoder) A class instance that implements BitmapDecoder, and must define a decode method, which accepts a String file name and a Context object, and returns a Bitmap
*/
public void setDownsampleDecoder( BitmapDecoder decoder ) {
sampleManager.setDecoder( decoder );
}
/**
* Get the {@link TileSetSelector} implementation currently used to select tile sets.
* @return TileSetSelector implementation currently in use.
*/
public TileSetSelector getTileSetSelector() {
return detailManager.getTileSetSelector();
}
/**
* Set the tile selection method, defaults to {@link TileSetSelectorMinimalUpScale}
* Implement the {@link TileSetSelector} interface to customize how tile sets are selected.
* @param selector (TileSetSelector) implementation that handles tile set selection as scale is changed.
*/
public void setTileSetSelector(TileSetSelector selector) {
detailManager.setTileSetSelector(selector);
}
/**
* Defines whether tile bitmaps should be rendered using an AlphaAnimation
* @param enabled (boolean) true if the TileView should render tiles with fade transitions
*/
public void setTransitionsEnabled( boolean enabled ) {
tileManager.setTransitionsEnabled( enabled );
}
/**
* Define the duration (in milliseconds) for each tile transition.
* @param duration (int) the duration of the transition in milliseconds.
*/
public void setTransitionDuration( int duration ) {
tileManager.setTransitionDuration( duration );
}
//------------------------------------------------------------------------------------
// Detail Level Management API
//------------------------------------------------------------------------------------
/**
* Defines the total size, in pixels, of the tile set at 100% scale.
* The TileView wills pan within it's layout dimensions, with the content (scrollable)
* size defined by this method.
* @param width (int) total width of the tiled set
* @param height (int) total height of the tiled set
*/
@Override
public void setSize( int w, int h ) {
// super (define clip area)
super.setSize( w, h );
// coordinate with other components
detailManager.setSize( w, h );
// notify manager for relative positioning
positionManager.setSize( w, h );
}
/**
* Register a tile set to be used for a particular detail level.
* Each tile set to be used must be registered using this method,
* and at least one tile set must be registered for the TileView to render any tiles.
* @param detailScale (float) scale at which the TileView should use the tiles in this set.
* @param pattern (String) string path to the location of the tile image files, to be parsed by a DetailLevelPatternParser
*/
public void addDetailLevel( float detailScale, String pattern ) {
detailManager.addDetailLevel( detailScale, pattern, null );
}
/**
* Register a tile set to be used for a particular detail level.
* Each tile set to be used must be registered using this method,
* and at least one tile set must be registered for the TileView to render any tiles.
* @param detailScale (float) scale at which the TileView should use the tiles in this set.
* @param pattern (String) string path to the location of the tile image files, to be parsed by a DetailLevelPatternParser
* @param downsample (String) string path to the location of an optional non-tiled single image file that will fill the tile view, on a z-layer below tiles
*/
public void addDetailLevel( float detailScale, String pattern, String downsample ){
detailManager.addDetailLevel( detailScale, pattern, downsample );
}
/**
* Register a tile set to be used for a particular detail level.
* Each tile set to be used must be registered using this method,
* and at least one tile set must be registered for the TileView to render any tiles.
* @param detailScale (float) scale at which the TileView should use the tiles in this set.
* @param pattern (String) string path to the location of the tile image files, to be parsed by a DetailLevelPatternParser
* @param downsample (String) string path to the location of an optional non-tiled single image file that will fill the tile view, on a z-layer below tiles
* @param tileWidth (int) size of each tiled column
* @param tileHeight (int) size of each tiled row
*/
public void addDetailLevel( float detailScale, String pattern, String downsample, int tileWidth, int tileHeight ){
detailManager.addDetailLevel( detailScale, pattern, downsample, tileWidth, tileHeight );
}
/**
* Clear all previously registered zoom levels. This method is experimental.
*/
public void resetDetailLevels(){
detailManager.resetDetailLevels();
refresh();
}
/**
* While the detail level is locked (after this method is invoked, and before unlockDetailLevel is invoked),
* the DetailLevel will not change, and the current DetailLevel will be scaled beyond the normal
* bounds. Normally, during any scale change the details manager searches for the DetailLevel with
* a registered scale closest to the defined scale. While locked, this does not occur.
*/
public void lockDetailLevel(){
detailManager.lockDetailLevel();
}
/**
* Unlocks a DetailLevel locked with lockDetailLevel
*/
public void unlockDetailLevel(){
detailManager.unlockDetailLevel();
}
/**
* pads the viewport by the number of pixels passed. e.g., setViewportPadding( 100 ) instructs the
* TileView to interpret it's actual viewport offset by 100 pixels in each direction (top, left,
* right, bottom), so more tiles will qualify for "visible" status when intersections are calculated.
* @param padding (int) the number of pixels to pad the viewport by
*/
public void setViewportPadding( int padding ) {
detailManager.setPadding( padding );
}
/**
* Define a custom parser to manage String file names representing image tiles
* @param parser (DetailLevelPatternParser) parser that returns String objects from passed pattern, column and row.
*/
public void setTileSetPatternParser( DetailLevelPatternParser parser ) {
detailManager.setDetailLevelPatternParser( parser );
}
//------------------------------------------------------------------------------------
// Positioning API
//------------------------------------------------------------------------------------
/**
* Register a set of offset points to use when calculating position within the TileView.
* Any type of coordinate system can be used (any type of lat/lng, percentile-based, etc),
* and all positioned are calculated relatively. If relative bounds are defined, position parameters
* received by TileView methods will be translated to the the appropriate pixel value.
* To remove this process, use undefineRelativeBounds
* @param left (double) the left edge of the rectangle used when calculating position
* @param top (double) the top edge of the rectangle used when calculating position
* @param right (double) the right edge of the rectangle used when calculating position
* @param bottom (double) the bottom edge of the rectangle used when calculating position
*/
public void defineRelativeBounds( double left, double top, double right, double bottom ) {
positionManager.setBounds( left, top, right, bottom );
}
/**
* Unregisters arbitrary bounds and coordinate system. After invoking this method, TileView methods that
* receive position method parameters will use pixel values, relative to the TileView's registered size (at 1.0d scale)
*/
public void undefineRelativeBounds() {
positionManager.unsetBounds();
}
/**
* Translate a relative x and y position into a Point object with x and y values populated as pixel values, relative to the size of the TileView.
* @param x (int) relative x position to be translated to absolute pixel value
* @param y (int) relative y position to be translated to absolute pixel value
* @return Point a Point object with x and y values calculated from the relative Position x and y values
*/
public Point translate( double x, double y ) {
return positionManager.translate( x, y );
}
/**
* Translate a List of relative x and y positions (double array... { x, y }
* into Point objects with x and y values populated as pixel values, relative to the size of the TileView.
* @param positions (List) List of 2-element double arrays to be translated to Points (pixel values). The first double should represent the relative x value, the second is y
* @return List List of Point objects with x and y values calculated from the corresponding x and y values
*/
public List translate( List positions ) {
return positionManager.translate( positions );
}
/**
* Divides a number by the current scale value, effectively flipping scaled values. This can be useful when
* determining a relative position or dimension from a real pixel value.
* @param value (double) The number to be inversely scaled.
* @return (double) The inversely scaled product.
*/
public double unscale( double value ) {
return value / getScale();
}
/**
* Scrolls (instantly) the TileView to the x and y positions provided.
* @param x (double) the relative x position to move to
* @param y (double) the relative y position to move to
*/
public void moveTo( double x, double y ) {
Point point = positionManager.translate( x, y, getScale() );
scrollToPoint( point );
requestRender();
}
/**
* Scrolls (instantly) the TileView to the x and y positions provided, then centers the viewport to the position.
* @param x (double) the relative x position to move to
* @param y (double) the relative y position to move to
*/
public void moveToAndCenter( double x, double y ) {
Point point = positionManager.translate( x, y, getScale() );
scrollToAndCenter( point );
requestRender();
}
/**
* Scrolls (with animation) the TIelView to the relative x and y positions provided.
* @param x (double) the relative x position to move to
* @param y (double) the relative y position to move to
*/
public void slideTo( double x, double y ) {
Point point = positionManager.translate( x, y, getScale() );
slideToPoint( point );
}
/**
* Scrolls (with animation) the TileView to the x and y positions provided, then centers the viewport to the position.
* @param x (double) the relative x position to move to
* @param y (double) the relative y position to move to
*/
public void slideToAndCenter( double x, double y ) {
Point point = positionManager.translate( x, y, getScale() );
slideToAndCenter( point );
}
/**
* Scales and moves TileView so that each of the passed points is visible.
* @param points (List) List of 2-element double arrays to be translated to Points (pixel values). The first double should represent the relative x value, the second is y
*/
public void framePoints( List points ) {
double topMost = -Integer.MAX_VALUE;
double bottomMost = Integer.MAX_VALUE;
double leftMost = Integer.MAX_VALUE;
double rightMost = -Integer.MAX_VALUE;
for( double[] coordinate : points ) {
double x = coordinate[0];
double y = coordinate[1];
if(positionManager.contains( x, y )){
topMost = Math.max( topMost, x );
bottomMost = Math.min( bottomMost, x );
leftMost = Math.min( leftMost, y );
rightMost = Math.max( rightMost, y );
}
}
Point topRight = translate( topMost, rightMost );
Point bottomLeft = translate( bottomMost, leftMost );
int width = bottomLeft.x - topRight.x;
int height = bottomLeft.y - topRight.y;
double scaleX = Math.abs( getWidth() / (double) width );
double scaleY = Math.abs( getHeight() / (double) height );
double destinationScale = Math.min( scaleX, scaleY );
double middleX = ( rightMost + leftMost ) * 0.5f;
double middleY = ( topMost + bottomMost ) * 0.5f;
moveToAndCenter( middleY, middleX );
setScaleFromCenter( destinationScale );
}
//------------------------------------------------------------------------------------
// Marker, Callout and HotSpot API
//------------------------------------------------------------------------------------
/**
* Markers added to this TileView will have anchor logic applied on the values provided here.
* E.g., setMarkerAnchorPoints(-0.5f, -1.0f) will have markers centered horizontally, positioned
* vertically to a value equal to - 1 * height.
* Note that individual markers can be assigned specific anchors - this method applies a default
* value to all markers added without specifying anchor values.
* @param anchorX (float) the x-axis position of a marker will be offset by a number equal to the width of the marker multiplied by this value
* @param anchorY (float) the y-axis position of a marker will be offset by a number equal to the height of the marker multiplied by this value
*/
public void setMarkerAnchorPoints( float anchorX, float anchorY ) {
markerManager.setAnchors( anchorX, anchorY );
}
/**
* Add a marker to the the TileView. The marker can be any View.
* No LayoutParams are required; the View will be laid out using WRAP_CONTENT for both width and height, and positioned based on the parameters
* @param view (View) View instance to be added to the TileView
* @param x (double) relative x position the View instance should be positioned at
* @param y (double) relative y position the View instance should be positioned at
* @return (View) the View instance added to the TileView
*/
public View addMarker( View view, double x, double y ) {
Point point = positionManager.translate( x, y );
return markerManager.addMarker( view, point.x, point.y );
}
/**
* Add a marker to the the TileView. The marker can be any View.
* No LayoutParams are required; the View will be laid out using WRAP_CONTENT for both width and height, and positioned based on the parameters
* @param view (View) View instance to be added to the TileView
* @param x (double) relative x position the View instance should be positioned at
* @param y (double) relative y position the View instance should be positioned at
* @param aX (float) the x-axis position of a marker will be offset by a number equal to the width of the marker multiplied by this value
* @param aY (float) the y-axis position of a marker will be offset by a number equal to the height of the marker multiplied by this value
* @return (View) the View instance added to the TileView
*/
public View addMarker( View view, double x, double y, float anchorX, float anchorY ) {
Point point = positionManager.translate( x, y );
return markerManager.addMarker( view, point.x, point.y, anchorX, anchorY );
}
/**
* Removes a marker View from the TileView's view tree.
* @param view (View) The marker View to be removed.
*/
public void removeMarker( View view ) {
markerManager.removeMarker( view );
}
/**
* Moves an existing marker to another position.
* @param view The marker View to be repositioned.
* @param x (double) relative x position the View instance should be positioned at
* @param y (double) relative y position the View instance should be positioned at
*/
public void moveMarker( View view, double x, double y ){
if( markerManager.indexOfChild( view ) > -1 ){
Point point = positionManager.translate( x, y );
LayoutParams params = view.getLayoutParams();
if( params instanceof AnchorLayout.LayoutParams ) {
AnchorLayout.LayoutParams anchorLayoutParams = (AnchorLayout.LayoutParams) params;
anchorLayoutParams.x = point.x;
anchorLayoutParams.y = point.y;
view.setLayoutParams( anchorLayoutParams );
markerManager.requestLayout();
}
}
}
/**
* Scroll the TileView so that the View passed is centered in the viewport
* @param view (View) the View marker that the TileView should center on.
* @params animate (boolean) should the movement use a transition effectg
*/
public void moveToMarker( View view, boolean animate ) {
if( markerManager.indexOfChild( view ) > -1 ){
LayoutParams params = view.getLayoutParams();
if( params instanceof AnchorLayout.LayoutParams ) {
AnchorLayout.LayoutParams anchorLayoutParams = (AnchorLayout.LayoutParams) params;
int scaledX = (int) ( anchorLayoutParams.x * getScale() );
int scaledY = (int) ( anchorLayoutParams.y * getScale() );
Point point = new Point( scaledX, scaledY );
if( animate ) {
slideToAndCenter( point );
} else {
scrollToAndCenter( point );
}
}
}
}
/**
* Scroll the TileView so that the View passed is centered in the viewport
* @param view (View) the View marker that the TileView should center on.
*/
public void moveToMarker( View view ) {
moveToMarker( view, false );
}
/**
* Register a MarkerEventListener. Unlike standard touch events attached to marker View's (e.g., View.OnClickListener),
* MarkerEventListeners do not consume the touch event, so will not interfere with scrolling. While the event is
* dispatched from a Tap event, it's routed though a hit detection API to trigger the listener.
* @param listener (MarkerEventListener) listener to be added to the TileView's list of MarkerEventListeners
*/
public void addMarkerEventListener( MarkerEventListener listener ) {
markerManager.addMarkerEventListener( listener );
}
/**
* Removes a MarkerEventListener from the TileView's registry.
* @param listener (MarkerEventListener) listener to be removed From the TileView's list of MarkerEventListeners
*/
public void removeMarkerEventListener( MarkerEventListener listener ) {
markerManager.removeMarkerEventListener( listener );
}
/**
* Add a callout to the the TileView. The callout can be any View.
* No LayoutParams are required; the View will be laid out using WRAP_CONTENT for both width and height, and positioned based on the parameters
* Callout views will always be positioned at the top of the view tree (at the highest z-index), and will always be removed during any touch event
* that is not consumed by the callout View.
* @param view (View) View instance to be added to the TileView's
* @param x (double) relative x position the View instance should be positioned at
* @param y (double) relative y position the View instance should be positioned at
* @return (View) the View instance added to the TileView's
*/
public View addCallout( View view, double x, double y ) {
Point point = positionManager.translate( x, y );
return calloutManager.addMarker( view, point.x, point.y );
}
/**
* Add a callout to the the TileView. The callout can be any View.
* No LayoutParams are required; the View will be laid out using WRAP_CONTENT for both width and height, and positioned based on the parameters
* Callout views will always be positioned at the top of the view tree (at the highest z-index), and will always be removed during any touch event
* that is not consumed by the callout View.
* @param view (View) View instance to be added to the TileView's
* @param x (double) relative x position the View instance should be positioned at
* @param y (double) relative y position the View instance should be positioned at
* @param aX (float) the x-axis position of a callout view will be offset by a number equal to the width of the callout view multiplied by this value
* @param aY (float) the y-axis position of a callout view will be offset by a number equal to the height of the callout view multiplied by this value
* @return (View) the View instance added to the TileView's
*/
public View addCallout( View view, double x, double y, float anchorX, float anchorY ) {
Point point = positionManager.translate( x, y );
return calloutManager.addMarker( view, point.x, point.y, anchorX, anchorY );
}
/**
* Removes a callout View from the TileView's view tree.
* @param view The callout View to be removed.
* @return (boolean) true if the view was in the view tree and was removed, false if it was not in the view tree
*/
public boolean removeCallout( View view ) {
if( calloutManager.indexOfChild( view ) > -1 ) {
calloutManager.removeView( view );
return true;
}
return false;
}
/**
* Register a HotSpot that should fire an listener when a touch event occurs that intersects that rectangle.
* The HotSpot moves and scales with the TileView.
* @param hotSpot (HotSpot) the hotspot that is tested against touch events that occur on the TileView
* @return HotSpot the hotspot created with this method
*/
public HotSpot addHotSpot( HotSpot hotSpot ){
hotSpotManager.addHotSpot( hotSpot );
return hotSpot;
}
/**
* Register a HotSpot that should fire an listener when a touch event occurs that intersects that rectangle.
* The HotSpot moves and scales with the TileView.
* @param positions (List) List of paired doubles { x, y } that represents the points that make up the region.
* @return HotSpot the hotspot created with this method
*/
public HotSpot addHotSpot( List positions ) {
List points = positionManager.translate( positions );
Path path = PathHelper.pathFromPoints( points );
path.close();
RectF bounds = new RectF();
path.computeBounds( bounds, true );
Rect rect = new Rect();
bounds.round( rect );
Region clip = new Region( rect );
HotSpot hotSpot = new HotSpot();
hotSpot.setPath( path, clip );
return addHotSpot( hotSpot );
}
/**
* Register a HotSpot that should fire an listener when a touch event occurs that intersects that rectangle.
* The HotSpot moves and scales with the TileView.
* @param positions (List) List of paired doubles { x, y } that represents the points that make up the region.
* @param listener (HotSpotEventListener) listener to attach to this hotspot, which will be invoked if a Tap event is fired that intersects the hotspot's Region
* @return HotSpot the hotspot created with this method
*/
public HotSpot addHotSpot( List positions, HotSpotEventListener listener ) {
HotSpot hotSpot = addHotSpot( positions );
hotSpot.setHotSpotEventListener( listener );
return hotSpot;
}
/**
* Remove a HotSpot registered with addHotSpot
* @param hotSpot (HotSpot) the hotspot to remove
* @return (boolean) true if a hotspot was removed, false if not
*/
public void removeHotSpot( HotSpot hotSpot ) {
hotSpotManager.removeHotSpot( hotSpot );
}
/**
* Register a HotSpotEventListener with the TileView. This listener will fire if any hotspot's region intersects a Tap event.
* @param listener (HotSpotEventListener) the listener to be added.
*/
public void addHotSpotEventListener( HotSpotEventListener listener ) {
hotSpotManager.addHotSpotEventListener( listener );
}
/**
* Remove a HotSpotEventListener from the TileView's registry.
* @param listener (HotSpotEventListener) the listener to be removed
*/
public void removeHotSpotEventListener( HotSpotEventListener listener ) {
hotSpotManager.removeHotSpotEventListener( listener );
}
//------------------------------------------------------------------------------------
// Path Drawing API
//------------------------------------------------------------------------------------
/**
* Register a Path and Paint that will be drawn on a layer above the tiles, but below markers.
* This Path's will be scaled with the TileView, but will always be as wide as the stroke set for the Paint.
* @param drawablePath (DrawablePath) a DrawablePath instance to be drawn by the TileView
* @return DrawablePath the DrawablePath instance passed to the TileView
*/
public DrawablePath drawPath( DrawablePath drawablePath ) {
return pathManager.addPath( drawablePath );
}
/**
* Register a Path and Paint that will be drawn on a layer above the tiles, but below markers.
* This Path's will be scaled with the TileView, but will always be as wide as the stroke set for the Paint.
* @param positions (List) List of doubles { x, y } that represent the points of the Path.
* @return DrawablePath the DrawablePath instance passed to the TileView
*/
public DrawablePath drawPath( List positions ) {
List points = positionManager.translate( positions );
return pathManager.addPath( points );
}
/**
* Register a Path and Paint that will be drawn on a layer above the tiles, but below markers.
* This Path's will be scaled with the TileView, but will always be as wide as the stroke set for the Paint.
* @param positions (List) List of doubles { x, y } that represent the points of the Path.
* @param paint (Paint) the Paint instance that defines the style of the drawn path.
* @return DrawablePath the DrawablePath instance passed to the TileView
*/
public DrawablePath drawPath( List positions, Paint paint ) {
List points = positionManager.translate( positions );
return pathManager.addPath( points, paint );
}
/**
* Removes a DrawablePath from the TileView's registry. This path will no longer be drawn by the TileView.
* @param drawablePath (DrawablePath) the DrawablePath instance to be removed.
*/
public void removePath( DrawablePath drawablePath ) {
pathManager.removePath( drawablePath );
}
/**
* Returns the Paint instance used by default. This can be modified for future Path paint operations.
* @return Paint the Paint instance used by default.
*/
public Paint getPathPaint(){
return pathManager.getPaint();
}
//------------------------------------------------------------------------------------
// Memory Management API
//------------------------------------------------------------------------------------
/**
* Clear bitmap image files, appropriate for Activity.onPause
*/
public void clear() {
tileManager.clear();
sampleManager.clear();
pathManager.setShouldDraw( false );
}
/**
* Clear bitmap image files, appropriate for Activity.onPause (mirror for .clear)
*/
public void pause() {
clear();
}
/**
* Clear tile image files and remove all views, appropriate for Activity.onDestroy
* References to TileView should be set to null following invocations of this method.
*/
public void destroy() {
tileManager.clear();
sampleManager.clear();
pathManager.clear();
}
/**
* Restore visible state (generally after a call to .clear()
* Appropriate for Activity.onResume
*/
public void resume(){
updateViewport();
tileManager.requestRender();
sampleManager.update();
pathManager.setShouldDraw( true );
}
/**
* Request the TileView reevaluate tile sets, rendered tiles, samples, invalidates, etc
*/
public void refresh() {
updateViewport();
tileManager.updateTileSet();
tileManager.requestRender();
sampleManager.update();
redraw();
}
//------------------------------------------------------------------------------------
// PRIVATE API
//------------------------------------------------------------------------------------
// make sure we keep the viewport UTD, and if layout changes we'll need to recompute what tiles to show
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout( changed, l, t, r, b );
updateViewport();
requestRender();
}
// let the zoom manager know what tiles to show based on our position and dimensions
private void updateViewport(){
int left = getScrollX();
int top = getScrollY();
int right = left + getWidth();
int bottom = top + getHeight();
detailManager.updateViewport( left, top, right, bottom );
}
// tell the tile renderer to not start any more tasks, but it can continue with any that are already running
private void suppressRender() {
tileManager.suppressRender();
}
//------------------------------------------------------------------------------------
// Private Listeners
//------------------------------------------------------------------------------------
private ZoomPanListener zoomPanListener = new ZoomPanListener() {
@Override
public void onZoomPanEvent(){
}
@Override
public void onScrollChanged( int x, int y ) {
updateViewport();
for ( TileViewEventListener listener : tileViewEventListeners ) {
listener.onScrollChanged( x, y );
}
}
@Override
public void onScaleChanged( double scale ) {
detailManager.setScale( scale );
for ( TileViewEventListener listener : tileViewEventListeners ) {
listener.onScaleChanged( scale );
}
}
@Override
public void onZoomStart( double scale ) {
detailManager.lockDetailLevel();
detailManager.setScale( scale );
for ( TileViewEventListener listener : tileViewEventListeners ) {
listener.onZoomStart( scale );
}
}
@Override
public void onZoomComplete( double scale ) {
detailManager.unlockDetailLevel();
detailManager.setScale( scale );
requestRender(); // put this here instead of gesture listener so we catch animations and pinches
for ( TileViewEventListener listener : tileViewEventListeners ) {
listener.onZoomComplete( scale );
}
}
};
private DetailLevelEventListener detailLevelEventListener = new DetailLevelEventListener(){
@Override
public void onDetailLevelChanged() {
requestRender();
for ( TileViewEventListener listener : tileViewEventListeners ) {
listener.onDetailLevelChanged();
}
}
/*
* do *not* update scale in response to changes in the zoom manager
* transactions are one-way - set scale on TileView (ZoomPanLayout)
* and pass those to DetailManager, which then distributes, manages
* and notifies all other interested parties.
*/
@Override
public void onDetailScaleChanged( double scale ) {
}
};
private GestureListener gestureListener = new GestureListener(){
@Override
public void onDoubleTap( Point point ) {
for ( TileViewEventListener listener : tileViewEventListeners ) {
listener.onDoubleTap( point.x, point.y );
}
}
@Override
public void onDrag( Point point ) {
suppressRender();
for ( TileViewEventListener listener : tileViewEventListeners ) {
listener.onDrag( point.x, point.y );
}
}
@Override
public void onFingerDown( Point point ) {
suppressRender();
for ( TileViewEventListener listener : tileViewEventListeners ) {
listener.onFingerDown( point.x, point.y );
}
}
@Override
public void onFingerUp( Point point ) {
if ( !isFlinging() ) {
requestRender();
}
for ( TileViewEventListener listener : tileViewEventListeners ) {
listener.onFingerUp( point.x, point.y );
}
}
@Override
public void onFling( Point startPoint, Point finalPoint ) {
suppressRender();
for ( TileViewEventListener listener : tileViewEventListeners ) {
listener.onFling( startPoint.x, startPoint.y, finalPoint.x, finalPoint.y );
}
}
@Override
public void onFlingComplete( Point point ) {
requestRender();
for ( TileViewEventListener listener : tileViewEventListeners ) {
listener.onFlingComplete( point.x, point.y );
}
}
@Override
public void onPinch( Point point ) {
suppressRender();
for ( TileViewEventListener listener : tileViewEventListeners ) {
listener.onPinch( point.x, point.y );
}
}
@Override
public void onPinchComplete( Point point ) {
requestRender();
for ( TileViewEventListener listener : tileViewEventListeners ) {
listener.onPinchComplete( point.x, point.y );
}
}
@Override
public void onPinchStart( Point point ) {
suppressRender();
for ( TileViewEventListener listener : tileViewEventListeners ) {
listener.onPinchStart( point.x, point.y );
}
}
@Override
public void onTap( Point point ) {
markerManager.processHit( point );
hotSpotManager.processHit( point );
for ( TileViewEventListener listener : tileViewEventListeners ) {
listener.onTap( point.x, point.y );
}
}
@Override
public void onScrollComplete( Point point ) {
requestRender();
for ( TileViewEventListener listener : tileViewEventListeners ) {
listener.onScrollChanged( point.x, point.y );
}
}
};
private TileRenderListener renderListener = new TileRenderListener(){
@Override
public void onRenderCancelled() {
}
@Override
public void onRenderComplete() {
for ( TileViewEventListener listener : tileViewEventListeners ) {
listener.onRenderComplete();
}
}
@Override
public void onRenderStart() {
for ( TileViewEventListener listener : tileViewEventListeners ) {
listener.onRenderStart();
}
}
};
//------------------------------------------------------------------------------------
// Public static interfaces and classes
//------------------------------------------------------------------------------------
/**
* Interface for implementations to receive TileView events. This interface consolidates several disparate
* listeners (Gestures, ZoomPan Events, TileView events) into a single unit for ease of use.
*/
public static interface TileViewEventListener {
/**
* Fires when a ACTION_DOWN event is raised from the TileView
* @param x (int) the x position of the event
* @param y (int) the y position of the event
*/
public void onFingerDown( int x, int y );
/**
* Fires when a ACTION_UP event is raised from the TileView
* @param x (int) the x position of the event
* @param y (int) the y position of the event
*/
public void onFingerUp( int x, int y );
/**
* Fires while the TileView is being dragged
* @param x (int) the x position of the event
* @param y (int) the y position of the event
*/
public void onDrag( int x, int y );
/**
* Fires when a user double-taps the TileView
* @param x (int) the x position of the event
* @param y (int) the y position of the event
*/
public void onDoubleTap( int x, int y );
/**
* Fires when a user taps the TileView
* @param x (int) the x position of the event
* @param y (int) the y position of the event
*/
public void onTap( int x, int y );
/**
* Fires while a user is pinching the TileView
* @param x (int) the x position of the event
* @param y (int) the y position of the event
*/
public void onPinch( int x, int y );
/**
* Fires when a user starts a pinch action
* @param x (int) the x position of the event
* @param y (int) the y position of the event
*/
public void onPinchStart( int x, int y );
/**
* Fires when a user completes a pinch action
* @param x (int) the x position of the event
* @param y (int) the y position of the event
*/
public void onPinchComplete( int x, int y );
/**
* Fires when a user initiates a fling action
* @param sx (int) the x position of the start of the fling
* @param sy (int) the y position of the start of the fling
* @param dx (int) the x position of the end of the fling
* @param dy (int) the y position of the end of the fling
*/
public void onFling( int sx, int sy, int dx, int dy );
/**
* Fires when a fling action has completed
* @param x (int) the final x scroll position of the TileView after the fling
* @param y (int) the final y scroll position of the TileView after the fling
*/
public void onFlingComplete( int x, int y );
/**
* Fires when the TileView's scale has updated
* @param scale (double) the new scale of the TileView (0-1)
*/
public void onScaleChanged( double scale );
/**
* Fires when the TileView's scroll position has updated
* @param x (int) the new x scroll position of the TileView
* @param y (int) the new y scroll position of the TileView
*/
public void onScrollChanged( int x, int y );
/**
* Fires when a zoom action starts (typically through a pinch of double-tap action,
* or by programmatic animated zoom methods.
* @param scale (double) the new scale of the TileView (0-1)
*/
public void onZoomStart( double scale );
/**
* Fires when a zoom action ends (typically through a pinch of double-tap action,
* or by programmatic animated zoom methods.
* @param scale (double) the new scale of the TileView (0-1)
*/
public void onZoomComplete( double scale );
/**
* Fires when the TileView should start using a new DetailLevel
*/
public void onDetailLevelChanged( );
/**
* Fires when the rendering thread has started to update the visible tiles.
*/
public void onRenderStart();
/**
* Fires when the rendering thread has completed updating the visible tiles, but before cleanup
*/
public void onRenderComplete();
}
/**
* Convenience class that implements {@TileViewEventListener}
*/
public static class TileViewEventListenerImplementation implements TileViewEventListener {
/**
* Fires when a ACTION_DOWN event is raised from the TileView
* @param x (int) the x position of the event
* @param y (int) the y position of the event
*/
public void onFingerDown( int x, int y ) {
}
/**
* Fires when a ACTION_UP event is raised from the TileView
* @param x (int) the x position of the event
* @param y (int) the y position of the event
*/
public void onFingerUp( int x, int y ) {
}
/**
* Fires while the TileView is being dragged
* @param x (int) the x position of the event
* @param y (int) the y position of the event
*/
public void onDrag( int x, int y ) {
}
/**
* Fires when a user double-taps the TileView
* @param x (int) the x position of the event
* @param y (int) the y position of the event
*/
public void onDoubleTap( int x, int y ) {
}
/**
* Fires when a user taps the TileView
* @param x (int) the x position of the event
* @param y (int) the y position of the event
*/
public void onTap( int x, int y ) {
}
/**
* Fires while a user is pinching the TileView
* @param x (int) the x position of the event
* @param y (int) the y position of the event
*/
public void onPinch( int x, int y ) {
}
/**
* Fires when a user starts a pinch action
* @param x (int) the x position of the event
* @param y (int) the y position of the event
*/
public void onPinchStart( int x, int y ) {
}
/**
* Fires when a user completes a pinch action
* @param x (int) the x position of the event
* @param y (int) the y position of the event
*/
public void onPinchComplete( int x, int y ) {
}
/**
* Fires when a user initiates a fling action
* @param sx (int) the x position of the start of the fling
* @param sy (int) the y position of the start of the fling
* @param dx (int) the x position of the end of the fling
* @param dy (int) the y position of the end of the fling
*/
public void onFling( int sx, int sy, int dx, int dy ) {
}
/**
* Fires when a fling action has completed
* @param x (int) the final x scroll position of the TileView after the fling
* @param y (int) the final y scroll position of the TileView after the fling
*/
public void onFlingComplete( int x, int y ) {
}
/**
* Fires when the TileView's scale has updated
* @param scale (double) the new scale of the TileView (0-1)
*/
public void onScaleChanged( double scale ) {
}
/**
* Fires when the TileView's scroll position has updated
* @param x (int) the new x scroll position of the TileView
* @param y (int) the new y scroll position of the TileView
*/
public void onScrollChanged( int x, int y ) {
}
/**
* Fires when a zoom action starts (typically through a pinch of double-tap action,
* or by programmatic animated zoom methods.
* @param scale (double) the new scale of the TileView (0-1)
*/
public void onZoomStart( double scale ) {
}
/**
* Fires when a zoom action ends (typically through a pinch of double-tap action,
* or by programmatic animated zoom methods.
* @param scale (double) the new scale of the TileView (0-1)
*/
public void onZoomComplete( double scale ) {
}
/**
* Fires when the TileView should start using a new DetailLevel
* @param oldZoom (int) the zoom level the TileView was using before the change
* @param currentZoom (int) the zoom level the TileView has changed to
*/
public void onDetailLevelChanged() {
}
/**
* Fires when the rendering thread has started to update the visible tiles.
*/
public void onRenderStart() {
}
/**
* Fires when the rendering thread has completed updating the visible tiles, but before cleanup
*/
public void onRenderComplete() {
}
}
public PositionManager getPositionManager() {
return positionManager;
}
public PathManager getPathManager() {
return pathManager;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy