TileView is a subclass of android.view.ViewGroup that asynchronously displays, pans and zooms tile-based images. Plugins are available for features like markers, hotspots, and path drawing.

Overview

Release Badge

This project isn't maintained anymore. It is now recommended to use https://github.com/peterLaurence/MapView.

MapView is maintained by Peter, one of our main contributors. MapView is an highly optimized Kotlin version (which can be used from Java projects) that's very cool and fast. It's 100% production ready and open source. Peter uses this widget in his app at https://github.com/peterLaurence/TrekMe/.

TileView: Version 4

The TileView widget is a subclass of ViewGroup renders and positions bitmap tiles to compose a larger, original image, often one too large to display normally.

tileview

Vesion 4 is effectively the promotion of version 3 from beta to production-ready, fixing the issues brought up by you, the users. While normally this'd probably be done using a series of minor version bumps, and this might be 3.4.11, there was a change to the base package name, and this requires a major change according to semver, since it's a breaking change for past versions.

Also of note, the universal (2-D) ScrollView class, and related classes like ScalingScrollView (which is a subclass of the 2D universal ScrollView, but also manages scaling and scale gestures) is now it's own repository: https://github.com/moagrius/ScrollView, and available with implementation 'com.moagrius:scrollview:1.0.3'. It is now included in the TileView project using normal gradle dependency operations, specifically using api rather than implementation but otherwise similar to other dependencies and is being pulled down from jcenter and reconciled normally.

Demos for ScrollView are in the ScrollView repo. Demos for TileView are in this repository.

Feel free to use ScrollView as a standalone widget if you don't need image tiling.

Change Log

4.0.7 (most recent)

  • Fixed bug with MarkerPlugin where markers clipped when scaled beyond 100%.

4.0.5 (most recent)

  • Save and restore instance state properly implemented.
  • Removed a bug that produced an increasing number of delayed callbacks from a Handler. This is a serious memory leak and all users on versions 4.0.3 and 4.0.4 should immediately upgrade to 4.0.5.

4.0.4

  • Update ScalingScrollView to version 1.0.10, which provides additional methods and method exposure (many private access methods became protected)

4.0.3

  • You can scale greater than 1f now without flicker, and without clipping.
  • Remote images should decode now with a much higher rate of success, nearing 100%.
  • Marker plugin API has been updated and show now work propertly (see TileViewDemoAdvanced Activity).
  • LowFidelityBackgroundPlugin now scaled and positions appropriately with the TileView host.
  • The COVER and CONTAIN MinimumScaleModes should work properly now.
  • Upgraded from com.moagrius:scrollview:1.0.4 to 1.0.9

TileView

Very large images in Android will often result in an OutOfMemoryError. Memory is finite, and bitmaps take a great deal of it. TileView solves this by stitching together small pieces of the image (tiles) and displaying them reconstructed in the area of the screen visible to your user.

Out of the box, the TileView will manage the tiled image, including subsampling when needed, flinging, dragging, scaling, and multiple levels of detail.

Additional plugins are provided to allow adding overlaying Views (markers), info windows, hot spots, path drawing, and coordinate translation.

Version 4 is fairly young. If you're need a stable version, the last release of version 2 is 2.7.7, and is available as a branch, here: https://github.com/moagrius/TileView/tree/version-2.7.7, or from the releases page. You can also get it from gradle using the old namespace: implementation 'com.qozix:tileview:2.7.7'

No further work will be done on version 2.

This is truly open source. I'm very happy to accept improvements or bug fixes - no caveats; create an issue, branch, and create a PR. While I'm not super interested in finding bugs and don't want the issues page to become a wasteland (I already know what most of them are, and will start to document them as this make its way into the wild), I am very interested in fixing those bugs - if you have the time to locate and correct a bug, please open a PR.

Installation

Add this to your app module's build.gradle.

implementation 'com.moagrius:tileview:4.0.7'
Quick Setup
  1. Tile an image into image slices of a set size, e.g., 256x256 instructions // TODO
  2. Name the tiles by the column and row number, e.g., 'tile-1-2.png' for the image tile that would be at the 2nd column from left and 3rd row from top.
  3. Create a new application with a single activity ('Main').
  4. Save the image tiles to your assets directory.
  5. Add the latest version to your gradle dependencies.
  6. In the Main Activity, use this for onCreate:
@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  TileView tileView = new TileView.Builder(this)
  	.setSize(3000, 3000)
  	.defineZoomLevel("tile-%d-%d.png")
  	.build();
  setContentView(tileView);
}

That's it. You should have a tiled image that only renders the pieces of the image that are within the current viewport, and pans and zooms with gestures.

Note that String replacements for rows and columns is not required - you can supply literally any Object instance to a DetailLevel, and a BitmapProvider implementation can use that Object to generate a Bitmap instance however you want.

What Changed

As a user, the biggest things you'll notice are:

  1. You no longer need redandant tile sets or detail levels. If your image doesn't change the details (e.g., show different images or labels at different zoom levels), you don't need to create tiles sets besides the original, full size one. The program will now use subsampling to do this work for you without any setup on your part: one call to defineDetail(anyObject) is sufficient.
  2. No more BitmapProvider. We're just doing way too much management of Bitmaps, including re-use and caching, to allow any stray Bitmap to wander in. This may change in the future, but the replacement for now is StreamProvider - basically the same thing but you just return an InputStream _instead of a Bitmap, and we take care of the rest.
  3. Greatly improved bitmap management. For details, see How It Works
  4. Touch events are now managed appropriately, rather than blindly consumed. We now use the same logic as the framework's ScrollView, so you can assign normal OnClickListeners or similiar gesture management devices on things like markers and callouts, without interrupting drag or fling operations.
  5. Plugin architecture. Only sign up for things you need - if you don't need markers, don't install the plugin, and keep things snappy and simple. Check TileViewDemoSimple for a very bare implementation, and TileViewDemoAdvanced for a more dressed up version.
  6. Much smaller codebase. Almost all the magic now happens in either Tile or TileView - if you understand those 2 classes, you understand the majority of the project.
  7. Decomposition. This takes a little explanation. There are now 3 major, public widgets: ScrollView, ScalingScrollView, and TileView. Each inherits from the last. You'll notice the demo module has Activities for each of these classes.
ScrollView

com.moagrius.widgets.ScrollView is a clone of AOSP's ScrollView and HorizontalScrollView, with some inspiration taken from RecyclerView and GestureDetector. That means the APIs will be very familiar to anyone who's used the framework-provided ScrollView. The big difference here is that com.moagrius.widgets.ScrollView functions along both axes, without configuation. For example, if you have a layout that matches parent width and expands vertically, ScrollView will scroll it vertically - the converse holds. If you have a layout that larger in both directions than the parent, ScrollView will scroll in any direction.

Again, no configration is required - there is no orientation attribute - it does it's best to figure out what makes sense.

ScalingScrollView

ScalingScrollView extends ScrollView and adds scaling APIs, as well as built-in scaling gesture managent (pinch and double tap to zoom). By default, it will visually scale its content, but that can be disabled with a single setter setShouldVisuallyScaleContents(false), and you can manage your content with a porportional scaled value. Similarly, it will lay itself out to the current scale value, unless that too is disabled setWillHandleContentSize(true)

At some point, ScalingScrollView will be it's own repository, and will be a dependency of TileView.

TileView inherits from ScalingScrollView, and adds tiling functionality.

From here down may be a bit dry but might be interesting to people interested in contributing to the process.

How It Works

Problem

You have an image too large to display with an OutOfMemoryError.

Solution

Chop the image into tiles and reconstruct them to fill the viewport, and only the viewport. As the user scrolls (or scales), aggressively recycle any bitmap data that is not visible to the user at any given time, and render any new "tiles" that are not within the viewable area.

Basic operation

Use the width and height of the view that contains this image, plus it's scroll position, to determine space we call the viewport.

Divide the corners of the viewport (left, top, right, bottom) by the size of the cell, which provide the start and end row and column. Round down the left and top and round up the right and bottom to make sure there are not empty patches - as long as any pixel of a tile is visible in the viewport, it must be rendered (one reason - among many - to carefully consider tile size, and not default to as large as possible).

From start column to end column and start row to end row are the tiles (imagine a 2D array) that should be rendered at that moment.

For example, let's consider...

  • image is 10,000 square
  • viewport is 1080 by 1920
  • viewport is at scroll position (1000, 500)
  • so our computed viewport is (1000, 500 - 2080, 2420)
  • out tiles are 256 square

so grid is

  1. start column 1000 / 256 = 3
  2. end column is 2080 / 256 = 9
  3. start row is 500 / 256 = 2
  4. end row is 2420 / 256 = 10

Iterate between those points...

for (int i = grid.columns.start; i < grid.columns.end; i++) {
  for (int j = grid.rows.start; j < grid.rows.end; j++) {

And your grid looks like this:

(03, 02), (04, 02), (05, 02), (06, 02), (07, 02), (08, 02), (09, 02)
(03, 03), (04, 03), (05, 03), (06, 03), (07, 03), (08, 03), (09, 03)
(03, 04), (04, 04), (05, 04), (06, 04), (07, 04), (08, 04), (09, 04)
(03, 05), (04, 05), (05, 05), (06, 05), (07, 05), (08, 05), (09, 05)
(03, 06), (04, 06), (05, 06), (06, 06), (07, 06), (08, 06), (09, 06)
(03, 07), (04, 07), (05, 07), (06, 07), (07, 07), (08, 07), (09, 07)
(03, 08), (04, 08), (05, 08), (06, 08), (07, 08), (08, 08), (09, 08)
(03, 09), (04, 09), (05, 09), (06, 09), (07, 09), (08, 09), (09, 09)
(03, 10), (04, 10), (05, 10), (06, 10), (07, 10), (08, 10), (09, 10)

As long as there's only the one size, scale is uniformly applied - just multiple tile size in the previous math by the current scale, and your grid remains intact.

Scaling

At every half way point, the image is at 50% or smaller, so subsample it to save memory: https://developer.android.com/topic/performance/graphics/load-bitmap#load-bitmap

Remember that each subsample (representing half the size) will actually quarter the amount of memory required for the bitmap. Do this at every half: 50%, 25, 12.5, 6.25, 3.625, etc.

When you add another detail level, things get a little more complicated, but not terribly...

Detail/Zoom Levels
  • Subsampling starts over at the last provided detail level, so it's no longer directly taken from the scale
  • Your grid now has to be "unscaled" - now it's tile size * scale (as it was before), but now must be unscaled by an amount equal to the emulation of the detail level.
  • This unscaled value is effectively the inverse of the scale, so 50% scale would be times 2, 25% would be times 4, 12.5% would be times 8
  • However, this is a constant value, so anything from 50% to 26% would be times 2, so a better descript might be "zoom left shift one" or (zoom << 1)
  • This works fairly well as is at this point, but on large screens with high density, you end up with lots of very small tiles, unless you provide a ton of detail levels.
  • If you have a single detail level and let the program subsample for you, the total memory footprint stays (about) the same, but the number of times the disk or caches are hit is very high, and this can appear very slow
  • To remedy this, we use "patching". With patching, things get a lot more complicated.
Patching
  • "Patching" is basically grabbing as many subsampled tiles as needed to create a full sized tiles, stitching them together into a single bitmap, and stuffing that in the cache
  • A very important number here is the "image subsample". this is distinct from the "zoom sample" described above
  • The image subsample is derived from the distance you are from the last provided detail level. so if you have only 1 detail level (0) and you're at 20%, you're at image subsample 4 (50% would be 2, 25% would be 4, 12.5% would be 8, etc)
  • To do that, your grid math has to change a little. you now have to round down to the nearest image subsample on the west and north, and round up to the nearest image subsample on the south and east, and skip a number of columns and rows equal to the image subsample
  • So now your viewport computation becomes very different:
for (int i = grid.columns.start; i < grid.columns.end; i += imageSample) {
  for (int j = grid.rows.start; j < grid.rows.end; j += imageSample) {

And for each of those tiles, we grab the last good detail level and fill in the blanks, so tile (0,4) would draw it's neighbors:

(0,0), (0,1), (0,2), (0,3)
(1,0), (1,1), (1,2), (1,3)
(2,0), (2,1), (2,2), (2,3)
(3,0), (3,1), (3,2), (3,3)

This allows us to then save the "patched" bitmap tile to both a memory and a disk cache, so we're not decoding very large files and discarding excess data after the first time.

The next thing that comes up is when changing between zoom levels. If we just swap out levels, the screen will be blank for a second, regardless of whether that's a defined, supplied detail level, or just subsampling the tiles. Since View.onDraw uses a Canvas, we need to redraw the entire thing each time invalidation occurs. That means if you switch between zoom level, the canvas will clear the old tiles before drawing the new tiles. This behavior is generally good (and what ensures that we're generally only going to drawing as much bitmap data as we need to fill the screen, even if we do a bad job of cleaning up), but in this case the visual effect is jarring.

To remedy this, we:

  • Populate a Region instance with the virtual viewport (scaled)
  • For each fully decoded tile in the current (the detail level we just entered), punch a hole in that Region. What's left are pixels that will remain unfilled after we draw current tiles.
  • For each fully decoded and drawn tiles from the previous detail level - the detail level we're exiting - we use Region.quickReject to determine if that previous tile intersects any part of the virtual viewport that will not be filled with current tile pixel data.
    • If a previous tile does intersect this unfilled region, we need to draw it.
    • If a previous tile does not intersect this unfilled region, that means we have completely covered that area with pixel data from the new level, and can both destroy the previous tile (and it's bitmap), as well as remove it from the list of previous tiles we're testing.
      • Since we remove every previous tile that's covered by current tiles, this set will become empty once the current viewport is filled with current pixel data.

So all the preceding "works" pretty well, but decoding the same data over and over is a lot of work that we can probably skip if we're smart about it.

Bitmap Caching and Re-use

Emphasis on and reuse - the reuse bit is as important as (maybe even more than) the traditional caching piece.

The default providers assume we're reading tiles from disk already, so the only thing that goes into disk-cache are the patched tiles, mentioned above (this behavior can be modified, if for example you're reading tiles from a server). Everything, however, is subject to a memory cache. The default is one-quarter of available RAM, but the more you can spare for this, the better your performance will be. This serves as both a straight bitmap cache, and a pool of re-usable objects: When we first go to decode a tile, we check if that exact bitmap is in the memory cache - if it is, great, use it - if it's not, grab the oldest bitmap in the cache and re-use the underlying bitmap (BitmapFactory.Options.inBitmap)

Reusing bimaps prevents frequent, large memory allocation and de-allocation, and can greatly improve the perceived performance of your app. I'll let Colt explain: https://www.youtube.com/watch?v=_ioFW3cyRV0

Threading

Threading is accomplished using a dedicated ThreadPoolExecutor. It's basically a fixed size pool with a pool size equal to the number of cores on the device (so 2 or 4 commonly, on newer phones as much as 8). There's a 0 keep-alive and tasks are queued with a LinkedBlockingQueue. A custom factory provides Thread instances that use Thread.MIN_PRIORITY to we don't consume resource the main thread needs to perform nice scrolls and flings. Going further on this, each Tile (which is a Runnable submitted to the ThreadPoolExecutor also calls Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST); which gives a larger range than Thread.setPriority, and this is actually very noticeable. On a real Nexus 6, the jank without call to setThreadPriority isnt' terrible but is obvious, and immediately remedied with the call just mentioned.

This general approach is very similar to what you'd get with Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()).

Comments
  • Multithread tile rendering 2

    Multithread tile rendering 2

    Improvements of this PR were discussed on https://github.com/moagrius/TileView/issues/250

    Also fix issue found out on discussion https://github.com/moagrius/TileView/pull/263

    Do not call addTileToCurrentTileCanvasViewif bitmap was not rendered. Proper calling onRenderTaskCancelled whenever the whole rendering is canceled. Also added a shutdown method in TileRenderPoolExecutor to avoid receiving more tiles to render.

    opened by bnsantos 91
  • Rendering bugs

    Rendering bugs

    Occasionally when zooming I see rendering bugs:

    • No tiles drawn
    • Subset of tiles not drawn

    I attached a screenshot showing an example. This happend when quickly zooming out, my screen became blank (no tiles were drawn). Then when slowly zooming in again some tiles showed up but not all of them.

    I've been trying to figure out what's going on myself, but it's like finding a needle in a haystack :)

    screenshot_20160614-090823

    opened by tsuijten 52
  • Out of memory, zooming, ZoomPanListener

    Out of memory, zooming, ZoomPanListener

    Thanks for this wonderful library. I have put together several mapping apps which display tiles downloaded from servers. They have a fairly large number of zoom levels (up to 20). I have also created a couple of buttons for "zoom in" and "zoom out" which double and halve the scale respectively. I have noticed that if I set the initial zoom level (scale) so that the map is "zoomed out", the library crashes with an out of memory error. The exact zoom level at which this happens depends on the device. I have worked around this by initializing the library so that the map starts out quite "zoomed in". I have also noticed that if I start the map "zoomed in", and zoom out using my zoom buttons, no out of memory error occurs. Also, the "zoom wraparound", in which the scale of the map goes from its minimum to maximum value when the map is double tapped also causes an out of memory error in the library. I have intercepted this and blocked it to prevent the crash. Also, I notice that occasionally when the zoom out button is tapped, my "getBitmap" callback is not called by the library. If I then pan the map, my "getBitmap" callback is called and the map fills correctly. Also, I have noticed that the current detail level is not set correctly when the "onZoomEnd" callback is called in the ZoomPanListener . Finally, I have noticed from analyzing crashes, and just wanted to confirm, that the "getBitmap" callback is called from an AsyncTask, so there is no need for me to start up my own AsyncTask in getBitmap() when downloading a tile. Again, thanks for the library.

    opened by chrisbrossard 33
  • Scaling behavior

    Scaling behavior

    Hi Mike,

    It's been a long time :) I plan to update my android app which uses TileView 2, and I'm looking into the code to check that I won't miss any feature I need. So far, it's looking good. Although I noticed something strange: in the demo (the last advanced one), I can't zoom in or out and keep a point between my fingers at a fixed position. We can do this in v2. In v3, the zoom is sort of hard to control. Even the code into ScalingScrollView seems mostly taken from the former ZoomPanLayout. So at this point I don't understand why the v3 behaves differently. Any idea?

    version 4 
    opened by p-lr 26
  • Ability to tweak multi threaded image loading

    Ability to tweak multi threaded image loading

    First of all, thanks for this great library!

    I'm using TileView to display large paintings. The tiles are downloaded from the internet. I have setShouldRenderWhilePanning set to true because it offers the best user experience. Sometimes while flinging the animation gets jaggy.

    I feel this might be because there are multiple thread working on downloading the images. Maybe when using a single thread, the tiles might be loaded a little less fast, but would also have less overhead and allow more resources for Android to smoothly show the animations.

    Right now the TileRenderPoolExecutor is tightly coupled with the TileView, it would be great if there was some way to tweak the TileRenderPoolExecutor or add an abstraction layer (interface) for the class in charge of loading the tile-set (with TileRenderPoolExecutor as the default implementation)

    Additionally I use TileView inside a ViewPager which allows the user to browse through an art collection. Each individual TileView creates and stops a PoolExecutor which also adds overhead. It would make more sense to share a PoolExecutor between TileView's

    What are your thoughts on this? Of course I'm willing to help by creating a pull request :)

    opened by tsuijten 26
  • MinimumScaleMode.Fit and coordinates

    MinimumScaleMode.Fit and coordinates

    Hey, thanks to @tsuijten and @peterLaurence PR #318 introduced the MinimumScaleMode.Fit mode adding whitespaces when totally zoomed out. However, this broke the coordinates of tap events. A short example: Lets say I have an image of 1000 width * 500 height and set MinimumScaleMode.FIT. Zoomed totally in at 1.0 and tapping the lower right I get x,y of 1000,500. Fine. Zooming out it adds the whitespaces and centres the image, also fine. However, if I tap the lower right now I get x,y of something like 1000,2500 because the added whitespace. This offset is calculated in ZoomPanLayout.onLayout() with translateX and translateY and should be used to offset the MotionEvent the onTouchEvent() gets.

    opened by krabo0om 23
  • Sometimes, some tiles are not rendered

    Sometimes, some tiles are not rendered

    Hi,

    Most of the time, tiles are correctly rendered. But sometimes, after an intense unzoom, some tiles are not rendered. At the beginning, i thought that my BitmapProvider was the culprit. Its implementation is very close to BitmapProviderAssets, because it looks for tiles located on the sd card. So i tried to log any Exception raised by BitmapFactory.decodeFile. But there were none. At the moment, the only clue i have is that it happens just after a drop in memory usage of my app. I made a screen shot of that memory usage:

    screen

    As a side note, even if some tiles are not rendered, if i want them appear, i just have to scroll (or zoom / unzoom) a little to trigger tiles painting.

    opened by p-lr 23
  • Loading more than one tile async

    Loading more than one tile async

    Hey, I did a PR while ago when the lib was still in version 1.something to fetch more than one tile at the same time. And it seems that this code was not introduced in version 2.0, all tiles are loaded in sequence (takes too long to load all tiles if they're not as a resource inside the app).

    Do you have interest in updating your lib to load more than one tile at the same time? I could possibly work on that and create another PR but before I would like to know why do you remove the code I did for this (was there a bug) not in the scope of the lib?

    Thanks,

    enhancement question 
    opened by bnsantos 23
  • Multistore Map

    Multistore Map

    We are using tilevew to create maps for a multistore building. After default Tileview to recall a picture for the fist level we need to Exchange to the second to the second level (second picture), the picture can not show on mobile.The coding we use is like the above. Please provide us some suggestion on how to solve the problem.

    初始化TileView之后调用这个方法去添加瓦片图第一次可以,但我第二次调用的时候,图片显示不出来 private void initTiledView(TileView tileView,int floor){
 mapLayout.removeAllViews();
 if (MODEL_TYPE == 1) {
 mapLayout.setBackgroundColor(this.getResources().getColor(
 R.color.frame_grey));
 } else {//有地图
 if(mapW==10400){
 mapScale=0.231f;
 }else{
 mapScale=0.25f;
 }
 String detailLevel800 = expoId + "/.nomedia/" + "map/tiles-800/"+floor+"/"+expoId+"-"+floor+"-%row%-%col%.gif";
 String detailLevelsamples = expoId + "/.nomedia/" + "map/samples/"+floor+"/hall.gif";
 String detailLevel125 = expoId + "/.nomedia/" + "map/tiles-125/"+floor+"/"+expoId+"-"+floor+"-%row%-%col%.gif";
 tileView.setSize(mapW, mapH);
 tileView.addDetailLevel(1.0f, detailLevel800, detailLevelsamples, 800, 800);
 tileView.addDetailLevel(mapScale, detailLevel125,detailLevelsamples , 800, 800);
 tileView.setMarkerAnchorPoints(-0.5f, -1.0f);
 tileView.setScaleLimits(0, 1.5);
 tileView.setScale(0.15);
 tileView.setTransitionsEnabled(true);
 tileView.defineRelativeBounds(0, 0, mapW, mapH);
// Paint paint = tileView.getPathPaint();
// paint.setShadowLayer(10, 2, 2, Color.RED);
// paint.setPathEffect(new CornerPathEffect(5));
 mapLayout.addView(tileView);
 }
 
 }

    opened by longhua68 23
  • Marker scaling

    Marker scaling

    With MapView I was using addMarkerAtZoom because the markers didn't scale so I used "addMarkerAtZoom" to have different size depending on the zoom lvl. that wasn't perfect but now that method is gone and markers do not scale.

    Any suggestions?

    opened by Shusshu 22
  • Using lat/long in defineBounds()

    Using lat/long in defineBounds()

    I have some problem with using lat/long coords.

    What latitude should i pass as top and bottom of defineBounds(left, top, right, bottom) method?

    if (top < bottom) e.g.tileView.defineBounds(30.3225, 59.9777, 30.3438, 59.9896); then translateY(double y) returns wrong value (markers on wrong places);

    But if I swap top and bottom (top > bottom), e.g. tileView.defineBounds(30.3225, 59.9896, 30.3438, 59.9777); then translateY(double y) works correct, but contains(double x, double y) method returns wrong value.

    How can I solve this problem?

    opened by arso8 21
  • implementation 'com.qozix:tileview:2.7.7' doesn't seem to work

    implementation 'com.qozix:tileview:2.7.7' doesn't seem to work

    I tried adding implementation 'com.qozix:tileview:2.7.7' to my build file (I have old code that needs to use the old version) but I can't get it to work with 2.7.7. Which repository does it reside in?

    The best I can find that is working is 2.2.9 which I think is in Maven Central.

    opened by Derpalus 1
  • Mike passed away

    Mike passed away

    Hello, I'm Pierre, a contributor on this project.

    It is with great sadness that I have to inform you that Mike Dunn (@moagrius ), the maintainer of TileView, has passed away. Mike and I worked together on TileView since late 2015. We've spent countless hours on code reviews.

    This is truly a great loss for everyone and the open source community. Mike, I'll never forget you.

    opened by p-lr 5
  • build() method starts to return void instead of TileView

    build() method starts to return void instead of TileView

    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      TileView tileView = new TileView.Builder(this)
      	.setSize(3000, 3000)
      	.defineZoomLevel("tile-%d-%d.png")
      	.build();
      setContentView(tileView);
    }
    

    So, now how we can use it?

    opened by mwe8 1
  • `android.os.BadParcelableException` regarding `com.moagrius.widget.ScrollView$SavedState.<init>`

    `android.os.BadParcelableException` regarding `com.moagrius.widget.ScrollView$SavedState.`

    Hello my app is getting this crash, on different kinds of Android devices. I don't see any of my code in the stack trace, so I'm not sure how to fix it. Any ideas?

    I think it's trying to restore the position of the TileView. I don't mind if it ends up at a different default position, as long as the app doesn't crash.

    Thanks

    PS if you would like to try the app on your own device, you can download it here: https://play.google.com/store/apps/details?id=nyc.underway.underway

    java.lang.RuntimeException: 
      at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2822)
      at android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:2897)
      at android.app.ActivityThread.-wrap11 (Unknown Source)
      at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1598)
      at android.os.Handler.dispatchMessage (Handler.java:105)
      at android.os.Looper.loop (Looper.java:251)
      at android.app.ActivityThread.main (ActivityThread.java:6572)
      at java.lang.reflect.Method.invoke (Native Method)
      at com.android.internal.os.Zygote$MethodAndArgsCaller.run (Zygote.java:240)
      at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:767)
    Caused by: android.os.BadParcelableException: 
      at android.os.Parcel.readParcelableCreator (Parcel.java:2888)
      at android.os.Parcel.readParcelable (Parcel.java:2814)
      at android.view.AbsSavedState.<init> (AbsSavedState.java:67)
      at android.view.View$BaseSavedState.<init> (View.java:25047)
      at android.view.View$BaseSavedState.<init> (View.java:25036)
      at com.moagrius.widget.ScrollView$SavedState.<init> (ScrollView.java:991)
      at com.moagrius.widget.ScalingScrollView$ScrollScaleState.<init> (ScalingScrollView.java:330)
      at com.moagrius.widget.ScalingScrollView$ScrollScaleState$1.createFromParcel (ScalingScrollView.java:347)
      at com.moagrius.widget.ScalingScrollView$ScrollScaleState$1.createFromParcel (ScalingScrollView.java:345)
      at android.os.Parcel.readParcelable (Parcel.java:2823)
      at android.os.Parcel.readValue (Parcel.java:2717)
      at android.os.Parcel.readSparseArrayInternal (Parcel.java:3165)
      at android.os.Parcel.readSparseArray (Parcel.java:2379)
      at android.os.Parcel.readValue (Parcel.java:2774)
      at android.os.Parcel.readArrayMapInternal (Parcel.java:3084)
      at android.os.BaseBundle.unparcel (BaseBundle.java:257)
      at android.os.Bundle.getSparseParcelableArray (Bundle.java:958)
      at com.android.internal.policy.PhoneWindow.restoreHierarchyState (PhoneWindow.java:2128)
      at android.app.Activity.onRestoreInstanceState (Activity.java:1101)
      at android.app.Activity.performRestoreInstanceState (Activity.java:1056)
      at android.app.Instrumentation.callActivityOnRestoreInstanceState (Instrumentation.java:1260)
      at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2795)
    
    opened by danramteke 1
  • App killed in background crashes when returning to foreground

    App killed in background crashes when returning to foreground

    Steps to reproduce:

    1. Put TileView in layout.
    2. Set layout as content view in Activity
    3. Background the app.
    4. Kill the process (either system or Terminate Application from logcat)
    5. Foreground the app.

    Expected Results: Does not crash

    Actual Results: App crashes. Stack dump below.

    Note: This does appear to be from the save state from com.moagrius.widget.ScalingScrollView.

    FATAL EXCEPTION: main Process: com.example, PID: 28646 android.os.BadParcelableException: ClassNotFoundException when unmarshalling: com.moagrius.widget.ScalingScrollView$ScrollScaleState at android.os.Parcel.readParcelableCreator(Parcel.java:2839) at android.os.Parcel.readParcelable(Parcel.java:2765) at android.view.AbsSavedState.<init>(AbsSavedState.java:67) at android.view.View$BaseSavedState.<init>(View.java:26247) at android.view.View$BaseSavedState.<init>(View.java:26236) at com.moagrius.widget.ScrollView$SavedState.<init>(ScrollView.java:991) at com.moagrius.widget.ScalingScrollView$ScrollScaleState.<init>(ScalingScrollView.java:330) at com.moagrius.widget.ScalingScrollView$ScrollScaleState$1.createFromParcel(ScalingScrollView.java:347) at com.moagrius.widget.ScalingScrollView$ScrollScaleState$1.createFromParcel(ScalingScrollView.java:345) at android.os.Parcel.readParcelable(Parcel.java:2774) at android.os.Parcel.readValue(Parcel.java:2668) at android.os.Parcel.readSparseArrayInternal(Parcel.java:3118) at android.os.Parcel.readSparseArray(Parcel.java:2351) at android.os.Parcel.readValue(Parcel.java:2725) at android.os.Parcel.readArrayMapInternal(Parcel.java:3037) at android.os.BaseBundle.initializeFromParcelLocked(BaseBundle.java:288) at android.os.BaseBundle.unparcel(BaseBundle.java:232) at android.os.Bundle.getSparseParcelableArray(Bundle.java:1010) at com.android.internal.policy.PhoneWindow.restoreHierarchyState(PhoneWindow.java:2133) at android.app.Activity.onRestoreInstanceState(Activity.java:1135) at android.app.Activity.performRestoreInstanceState(Activity.java:1090) at android.app.Instrumentation.callActivityOnRestoreInstanceState(Instrumentation.java:1317) at android.app.ActivityThread.handleStartActivity(ActivityThread.java:2991) at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:180) at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:165) at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:142) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1816) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:193) at android.app.ActivityThread.main(ActivityThread.java:6718) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

    bug willfix 
    opened by AaronKTowne 1
Releases(v3.0.1)
Owner
Mike Dunn
I am a human person
Mike Dunn
FixedHeaderTableLayout is a powerful Android library for displaying complex data structures and rendering tabular data composed of rows, columns and cells with scrolling and zooming features. FixedHeaderTableLayout is similar in construction and use as to Android's TableLayout

FixedHeaderTableLayout is a powerful Android library for displaying complex data structures and rendering tabular data composed of rows, columns and cells with scrolling and zooming features. FixedHeaderTableLayout is similar in construction and use as to Android's TableLayout

null 33 Dec 8, 2022
null 2.4k Dec 30, 2022
A 3D Layout for Android,When you use it warp other view,it can became a 3D view,一秒让你的view拥有3D效果!

ThreeDLayout A 3D Layout,When you use it warp other view,it can became a 3D view 中文文档 preview USAGE 1.compile library allprojects { repositories {

androidwing 490 Oct 27, 2022
An Android Layout which has a same function like https://github.com/romaonthego/RESideMenu

ResideLayout An Android Layout which has a same function like https://github.com/romaonthego/RESideMenu. Can be used on Android 1.6(I haven't try it.)

Yang Hui 392 Oct 12, 2022
This component like SwipeRefreshLayout, it is more beautiful than SwipeRefreshLayout.

android-PullRefreshLayout This component like SwipeRefreshLayout, it is more beautiful than SwipeRefreshLayout. Demo Usage Add dependency. dependencie

星一 2.1k Dec 3, 2022
A layout that creates a loading-like progress around it's child ( circle ) on touch, inspired from Destiny's ( PS4 ) accept mechanism

HoldToLoadLayout HoldToLoadLayout is a view group that can contain a single child. It draws your child to middle of layout, and performs loading wheel

Melih Aksoy 79 Feb 8, 2022
ViewStateLayout - Easy way to manage common state templates like loading, empty, error etc.!

ViewStateLayout Easy way to manage common state templates like loading, empty, error etc.! How to Step 1. Add the JitPack repository to your build fil

Kamrul Hasan 7 Dec 15, 2022
A wave view of android,can be used as progress bar.

WaveView ![Gitter](https://badges.gitter.im/Join Chat.svg) A wave view of android,can be used as progress bar. Screenshot APK demo.apk What can be use

Kai Wang 1.3k Dec 28, 2022
Android component which presents a dismissible view from the bottom of the screen

BottomSheet BottomSheet is an Android component which presents a dismissible view from the bottom of the screen. BottomSheet can be a useful replaceme

Flipboard 4.5k Dec 28, 2022
A swipeable - auto resizing view group for android

SwipeableLayout A swipeable - auto resizing view group for android Usage build.gradle compile 'com.wmbest.widget:swipeable-layout:1.0.+@aar' -- or --

Bill Best 114 Nov 25, 2022
Bubble View for Android.

BubbleLayout Bubble View for Android with custom stroke width and color, arrow size, position and direction. BubbleLayout Extends the FrameLayout. Gra

Masayuki Suda 964 Dec 28, 2022
Android library used to create an awesome Android UI based on a draggable element similar to the last YouTube graphic component.

Draggable Panel DEPRECATED. This project is not maintained anymore. Draggable Panel is an Android library created to build a draggable user interface

Pedro Vicente Gómez Sánchez 3k Jan 5, 2023
Android library used to create an awesome Android UI based on a draggable element similar to the last YouTube New graphic component.

Please switch to DragView, for the best support, thank you DraggablePanel Download allprojects { repositories { ... maven { url 'https://jitp

Hoàng Anh Tuấn 103 Oct 12, 2022
Show triangle view.

TriangleLabelView Show triangle view. How to Use To see how the TriangleLabelView are added to your xml layouts, check the sample project. <jp.shts.an

Shota Saito 877 Dec 6, 2022
A library for showing different types of layouts when a list view is empty

Android Empty Layout Please note that this project is not being maintained now. Hopefully a new version will be available soon. A library for showing

Raquib-ul Alam (Kanak) 606 Nov 26, 2022
Material Design Search View Layout, now implemented in Google Maps, Dialer, etc

THIS PROJECT IS DEPRECATED Component is not maintained anymore. Implementation of Lollipop+ Dialer and Google Maps. DEMO Add in View Add to your layou

Sahil Dave 1.1k Dec 22, 2022
A pull to refresh layout for android, the RecyclerRefreshLayout is based on the SwipeRefreshLayout. support all the views, highly customizable, code simplicity, etc. really a practical RefreshLayout!

RecyclerRefreshLayout English | 中文版 RecyclerRefreshLayout based on the {@link android.support.v4.widget.SwipeRefreshLayout} The RecyclerRefreshLayout

dinus_developer 1.7k Nov 10, 2022
Added support to modify text size and indicator width based on the original TabLayout.

XTabLayout——可修改选中项字体大小和指示器长度的TabLayout XTabLayout是基于design包中的TabLayout进行了功能的扩展,在保留原有功能的基础上,增加了修改选中项字体大小、修改指示器长度以及限制屏幕显示范围内显示的Tab个数。 集成步骤: 1.添加XTabLayo

Kennor 660 Dec 20, 2022