moagrius / TileView

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.
MIT License
1.46k stars 337 forks source link

setScaleToFit doesn't work as expected #5

Closed klattimer closed 11 years ago

klattimer commented 11 years ago

If I call setScaleToFit I'd expect the view to scale to fit the minimum scale required, however in the method calculateMinimumScaleToFit, we see that it uses the maximum between minimumScaleX and minimumScaleY.

I've changed this to use the minimum however the scale is still never set.

If I add to setScaleToFit() a call to setScale(minScale) at the end the view shows up entirely black.

I've also noticed that when you are scaled to the minScale you can still scale the view smaller than the min scale with a pinch gesture.

klattimer commented 11 years ago

I've added lots of code to fix this particular issue but it's messy.

First I've added protected void onSizeChanged (int w, int h, int oldw, int oldh) { setScaleToFit(true); }

To the TileView

then I added setScale(minScale) to the setScaleToFit method. This is less than ideal but works.

What I'm left with is rendering at scale 1.0 then a jump to the minScale. What I'd like is to have the view show at it's minScale and then zoom in slowly to fill the screen. I'd also like to have the tileview centered on it's parent view rather than aligned to the top left.

klattimer commented 11 years ago

An issue that has been introduced by doing this is that when the map is smaller than the screensize the downsample appears on top of the tileview or visa versa - i'm not entirely sure what is happening but it's like there are two separate views visible, one is at the minScale size, one is scaling when I pinch, the one at minScale size is on top and the view which is scaling is underneath.

moagrius commented 11 years ago

setScaleToFit sets a flag (boolean).

if it's true, then the contents of the TileView will always fit within the bounds of the View itself (there will be no empty space).

The comparison of scales is to determine which axis to limit, assuming the aspect ratio of the content does not exactly match the aspect ratio of the View. It'll probably need to scroll in one direction, so this comparison determines which axis to throttle.

If true, it disregards any minimum assigned with setScaleLimits. The maximum would still apply.

If false, then the minimum is whatever was set with setScaleLimits (if at all).

Does that help?

klattimer commented 11 years ago

Not entirely as it doesn't effect my desired outcome, you see I want to set the minimum to the actual minimum, not the largest of the two minimums in each axis, and I want the map to be centered inside the view. The CATiledLayer on iOS works exactly like this in apple's example code with a little extra magic to perform the alignment to center after zoom changes.

It's actually more convoluted than that but I'll summarise as much as I can a little.

First we need the map to entirely be visible on screen, centered in the parent view. I first achieved this with a slight change to the calculateMinimumScale to use the min of the minimumX and minimumY rather than the max which allows me to fit the map to the screen width or height thus eliminating scrolling when set to the minimum scale. Then using TileView.onSizeChanged to set the scale after the map had loaded. That last part was a big mistake and caused the bug that I started to see (as per my last comment prior to this one btw. comment numbering would be a good feature here)...

Now what I'm doing is, I've changed calculateMinimumScale again to this time ask the parent it's size if getWidth and getHeight is zero, so I can now call setScaleToFit after object initialisation and it will scale it so the entire map fits (usual behaviour of scaleToFit methods). Therefore the initial state of the map exposes (purposefully) a black background and the map sits in it's entirety in the top left of the screen.

This works in so far as, the map is now entirely visible on screen however I still need to get it to center correctly, which I'll also be working on in the coming days.

Hope I'm not generating too much noise, I'll hopefully have time to push all this stuff back up to you so you can see what I'm doing and expand/improve the API a bit.

moagrius commented 11 years ago

If you setScaleToFit(false), the min/max with setScaleLimits will be respected. You can then have whatever minimum scale you want.

Another user wanted to center the map in it's View a while ago - while this isn't strictly in the wheelhouse of the widget, and IMO should be managed with normal Android FW positioning techniques, there is a convo about how to achieve it and a quick gist in this thread from the previous version: https://github.com/moagrius/MapView/issues/39

Hope I'm not generating too much noise

Absolutely note

I'll hopefully have time to push all this stuff back up to you so you can see what I'm doing and expand/improve the API a bit.

Cool. FWIW, I'm much more likely to merge new features, or improvements, and am extremely unlikely to merge anything that breaks existing API. Also, "bit size" merges are preferred (e.g., one change per merge request)

klattimer commented 11 years ago

The tips you gave in moagrius/MapView#39 do kindof work, however they don't work entirely and I had to made some adjustments to get them close to what I wanted. I moved the code mentioned into the setScale method, so that the margins are set at the point of scaling. However when I do this the edge which has a margin doesn't scroll correctly to that particular end, e.g. if I set the topMargin then zoom in to the map I'm no longer able to scroll to the very top of the map.

moagrius commented 11 years ago

Hm, not sure why it would behave like you're describing. Maybe use an intermediately ViewGroup with margins, with just one child - the TileView - with MATCH_PARENT.

The TileView itself is going to act like any other View in terms of layout, and will respect it's containing layout manager (ViewGroup) as would any other View. What happens with the content of the TileView can be managed however you want, and you should be able to access all the relevant events with listeners, rather than rewriting core methods (like setScale), but if that's how you feel like it's easiest to get where you want to be, then by all means go for it.

moagrius commented 11 years ago

can i close this issue?