Is this really how the scrollView is supposed to work?

I’ve been trying to use the scrollView for some time, but it behaves far from what I hoped. To give you an idea about what I consider a perfect scroll behaviour, the Android apps list is a great example:

But the Corona scrollView behaves completely different. Here are my main complaints about the Corona scrollView:

  • Constant scroll speed (not controlled by gesture speed)
    I want the scrolling to be fast or slow dependent on the "flick gesture speed! As it is now, the scroll speed is completely independent of the gesture speed, making the scrollView feeling very clumsy.
     
  • No scrolling if the finger is moved even slightly in the opposite direction before the flicking gesture
    Sometimes the user drags the (list) scrollView a bit back and forth, before he wants to scroll a bit further. This isn’t possible now, because such a movement just cancels the scroll.
     
  • No scrolling if the finger is moved longer than a certain distance before flicking
    If the user wants to scroll fast, it’s natural to make a larger flick motion, but if the motion is over a certain distance, the scrollView also seems to cancel the scroll!
     
  • Occasional bugging - the scrollView scrolls in the wrong direction
    This happens maybe one of 30 times I try to scroll and it is not caused by reaching the limits of the scroll area.

I have made a small video showing some of these issues: Video (sorry about the lousy resolution, it’s youtube)

The video is made in the simulator so that the “finger” is visible, but the behavior is the same on the Android device itself. The background color is changed while the mouse is pressed so that it’s easier to see how the flicking/scrolling gesture is done.

The code for the app shown in the video is this:

local widget = require( "widget" ) local w = display.contentWidth local h = display.contentHeight local background = display.newRect(w/2, h/2, w, h) background:setFillColor(0,0,0) local function scrollListener( event ) local phase = event.phase if ( phase == "began" ) then background:setFillColor(0.5,0.2,0.2)   elseif ( phase == "ended" ) then  background:setFillColor(0,0,0) end   return true end local scrollView = widget.newScrollView { x = w/2, y = h/2,   width = w\*0.75, height = h,   scrollHeight = 4000,   horizontalScrollDisabled = true,   hideBackground = true,   listener = scrollListener, } local scrollImg = display.newImageRect( "image.png", w/2, 4000 ) scrollImg.x = w/2 scrollImg.y = 2000 scrollView:insert( scrollImg ) 

My simple question is this:

Is this really how the scrollView is supposed to work, or am I doing something very wrong?

Its the same behavior with tableviews. When using it you can feel its just a bit off.

But are people really accepting this level of performance(*)? Have they all given up and written their own scroll code (as I have been forced to)? What is the point of having a widget that you end up not using?

Surely Corona Labs can do better than this?

(*) If what I experience really is the normal behavior.

Hi,

Would you like to share your scroll code and post it here?

Thanks!

Sure, although it’s a bloody mess to be honest… Use the image posted in my start post to make the code below work.

I use a display group and its y value as a scrollView substitute. Some über-messy things are used to calculate the speed of the swipe etc. And I need to use the framedrawlistener to make the scroll slowdown effect.

But the incredible enough, it seems to work. And if a stupid ass like myself can manage this, why can’t the geniuses at Corona?

local lastDeltaYArr = {0,0,0} local deltaY=0 local lastMoveY=0 local pressStart=0 local scrollSpeedY=0 local yOffs = 0 local maxYOffs = -3000 local w = display.contentWidth local h = display.contentHeight --== The display elements ==-- local bgRect = display.newRect(w/2, h/2, w, h) bgRect:setFillColor(0) -- The display group used as a scrollView local mainGroup = display.newGroup() local scrollImg = display.newImageRect(mainGroup, "image.png", w/2, 4000 ) scrollImg.x = w/2 scrollImg.y = 2000 local function pushDeltaY(dY) for i = #lastDeltaYArr, 2, -1 do lastDeltaYArr[i] = lastDeltaYArr[i-1] end lastDeltaYArr[1] = dY end local function getMainDeltaY() local sum=0 for i = 1, #lastDeltaYArr do sum = sum + lastDeltaYArr[i] end return sum/#lastDeltaYArr end local touchHandler = function(e) local dx = math.abs(e.x - e.xStart) local dy = math.abs(e.y - e.yStart) if (e.phase == "began") then pressStart = system.getTimer() lastMoveY = e.y scrollSpeedY = 0 deltaY = 0 bgRect:setFillColor(1,0.5,0.5) end &nbsp; if (e.phase == "moved") then if (idx ~= -1) then deltaY = e.y - lastMoveY -- print("deltaY = " .. deltaY .. " &nbsp;lastMoveY = " .. lastMoveY) pushDeltaY(deltaY) lastMoveY = e.y mainGroup.y = math.min(0, yOffs + (e.y - e.yStart)) -- print("yOffs: " .. yOffs .. " &nbsp; e.y: " .. e.y .. " &nbsp; e.yStart: " .. e.yStart) -- print("maxYOffs: " .. maxYOffs) if (mainGroup.y \< maxYOffs) then mainGroup.y = maxYOffs end end -- print("mainGroup.y: " .. mainGroup.y) if (mainGroup.y \> 0) then mainGroup.y = 0 end -- print("mainGroup.y: " .. mainGroup.y) end if (e.phase == "ended") then -- print("ENDED: dx=" .. dx .. " dy=" .. dy) yOffs = mainGroup.y -- print("deltaY = " .. deltaY) deltaY = getMainDeltaY() -- print("Mean deltaY = " .. deltaY) if (math.abs(deltaY) \< 7) then scrollSpeedY = 0 else -- Keep on scrolling scrollSpeedY = deltaY\*2 end -- print("scrollSpeedY = " .. scrollSpeedY) -- print("Y speed at letup = " .. deltaY) -- print("yOffs: " .. yOffs) -- print("mainGroup.y: " .. mainGroup.y) bgRect:setFillColor(0) end return true end local frameRedrawListener = function(e) if (math.abs(scrollSpeedY) \> 0.5) then mainGroup.y = math.min(0, yOffs + scrollSpeedY) if (mainGroup.y \< maxYOffs) then print("REACHED BOTTOM") mainGroup.y = maxYOffs scrollSpeedY = 0 end if (mainGroup.y \>= 0) then print("REACHED TOP") mainGroup.y = 0 scrollSpeedY = 0 end yOffs = mainGroup.y -- print("redraw - yOffs: ".. yOffs) if (scrollSpeedY ~= 0) then if (scrollSpeedY \< 0) then scrollSpeedY = scrollSpeedY + 1 else scrollSpeedY = scrollSpeedY - 1 end end -- print("scrollSpeedY = " .. scrollSpeedY .. " &nbsp; mainGroup.y = " .. mainGroup.y) end end Runtime:addEventListener( "enterFrame", frameRedrawListener ) Runtime:addEventListener( "touch", touchHandler )

Again.Sorry for the messy code. This was never meant to be shown in public… Actually it’s all just a emergency solution until I could get the scrollView right…

Right…

Thanks for the code :)  It works OK.

I’m just a complete beginner and don’t see if it’s messy or not.

So it’s obvious that you are not “a stupid ass” and perhaps that the guys at Corona still have some things to learn.

It’s messy and not elegant. Trust me… :wink:

But it works (more or less) like other Android scrollers do and this is what I’m stuck with until I can get scrollView to work in a similar fashion…

I still do not think I have gotten a clear answer to my original question though: Is scrollView supposed to work the way I have described it above? Anyone from Corona want to comment?

Hey runewinse,

The scrollview scroll mechanism is not perfect. We know that and we have it on the todo list.

Except for your points there’s also no capability of scrolling the scrollview in both directions at the same time ( diagonally, let’s say ).

About people accepting the level of performance, this is not performance related, but rather functionality finetuning - related. The scroll capabilities as they are at the moment fullfill their purpose, they just require that finetuning to match the native feeling ( including scrollbar stretching when limit caps are reached ).

Since it’s hard to me to provide you with a ETA on the scrolling refactor, and you say “you’re stuck” with the current implementation, i’ll kindly remind you that the Widget library is opensourced on Git, so you can just implement your code in the momentum scrolling module of our implementation. I know this is not the ideal case for you, but that’s the only quick option i see.

Hope this clarifies,

alex

Hi alex and thanks for replying!

From the users perspective it’s very hard to understand why these basic things are not in place. I don’t know what you guys are working on at the time, but as a paying customer I really wish you would concentrate on getting the basic stuff right before starting on new projects.

(Even if it hasn’t anything to do with scrollViews it’s the same thing applies to the textField, which forces the Corona user to do all kinds of manual hacks to make it work. It’s just not good enough. Sorry.)

You’re right, of course. “Performance” was a bad term. I should have written “feature set” or “behavior”. The point was that it doesn’t behave as any  (non-Corona) scrolling app I have encountered and that makes it awkward.

This is, unfortunately, a couple of notches above my level.

But it’s no crisis on my part. My app works with my custom scroller code. But how about my next app? And other users wanting to use scrollView? C’mon guys! This isn’t rocket science  :slight_smile:

  • I’m a mediocre coder (at best)

  • 3 months ago I hadn’t heard of Corona or lua 

  • I used maybe 5-6 hours to write that scrolling behaviour code

I’m not writing this to brag, quite the opposite. The point is: If I can manage to make a scroller that that works ok in a day, you should be able to do it in 30 minutes - typing with your nose. There is really no excuses for you not doing it. None!

Hey runewinse,

In my whole post, i did not make up any excuses. I just stated the facts. This discussion went on and on about other widget features ( see http://forums.coronalabs.com/topic/37217-list-of-open-widget-20-bugs-promised-features/ ). Same as with other features, when the internal planning will allow it, it will be fixed.

Thanks,

alex

Hi runewinse,

I have some problems with the scrolling of a table - have a feeling that it’s due to a bug in the inbuild scrolling in the table. I’d like to test how your scrolling code works in my project but I have no idea where and how to include it in my code.

You can see the problem I described under point 2 in this thread:

http://forums.coronalabs.com/topic/48876-tweaking-visual-options-of-the-tableview-is-it-a-bug/

and the code is there as well.

So where do I put your code? and what changes do I have to make?

Will appreciate any help with this.

Thanks.

keram

I am a developer myself (in a completely different area than handheld devices) and trust me, I know how frustrating prioritization from the above can be. I have no idea of how big your organisation is though. Maybe  you are the one who decides these things for all I know…  :slight_smile:

BTW: I have not accused you personally for excusing anything. I apologize if it came out that way (English is not my native language).

What I meant was that there is no reason why Corona Labs should release something this seemingly halfhearted. Corona is being advertised a  business app   solution on the front of the Corona Labs web page for goodness sake! This is not consistent with how the widgets are prioritized (or have been prioritized until now). I only have the Basic 2014.2189. Maybe the other (updated) more expensive versions are much better.

But as a quick development platform for small “fullscreen” games, Corona SDK works beautifully   :slight_smile:

Hi, unfortunately I’m lousy at reading other peoples code (and I don’t have the time either). The only thing I can do is to add a slightly more readable version of my code and explain how it works a bit more.

Basically I do this:

  1. Create a displayGroup (display.newGroup()) that contains everything that is scrolled

  2. By modifying the y value of the displayGroup things will scroll

  3. A touch handler takes care of the flick gesture detection

3a. When a finger is pressed to the screen (“began”) the scroll values are reset

3b. When the finger is moving, the three last finger positions (or rather movements) are saved

3c. When the finger leaves the screen, I calculate the mean finger movement from the last 3 movements. Based on this the scroll speed is caculated.

  1. A frameRedrawListener is used to make the scrolling keep on even if the finger is not touching the screen. What happens in this function is that the y value of the displayGroup is changed according to the scroll speed value and then the scroll speed value is reduced a bit. When the scroll speed is small enough (or the top/bottom limit is reached), the scrolling stops.

Hope this helps a bit. I don’t know why, but I am not allowed to add .lua files as an attachment on this forum. Therefore I have to add the main.lua directly into the post (and thus loose all formatting). Sorry about this, but I cannot find any other way to do it…

Good luck!

main.lua:

local MIN_MOVEMENT_THAT_RESULTS_IN_AUTOSCROLL = 3

local lastDeltaYArr = {0,0,0}

local deltaY=0

local lastMoveY=0

local scrollSpeedY=0

local yOffs = 0

– The maximum scroll value (height of the scroll content -  height of screen)

local maxYOffs = -3000

local w = display.contentWidth

local h = display.contentHeight

local bgRect = display.newRect(w/2, h/2, w, h)

bgRect:setFillColor(0)

local mainGroup = display.newGroup()

local scrollImg = display.newImageRect(mainGroup, “image.png”, w/2, 4000 )

scrollImg.x = w/2

scrollImg.y = 2000

local touchHandler = function(e)

if (e.phase == “began”) then

scrollSpeedY = 0

deltaY = 0

lastMoveY = e.y

end

  if (e.phase == “moved”) then

deltaY = e.y - lastMoveY – Delta Y = finger movement distance from last position

– Always save the three last finger movement distances

lastDeltaYArr[3] = lastDeltaYArr[2]

lastDeltaYArr[2] = lastDeltaYArr[1]

lastDeltaYArr[1] = deltaY

lastMoveY = e.y

mainGroup.y = math.min(0, yOffs + (e.y - e.yStart)) – Scroll value while finger still on screen

– Stop at end points of scrolling

if (mainGroup.y < maxYOffs) then mainGroup.y = maxYOffs end

if (mainGroup.y > 0) then mainGroup.y = 0 end

end

if (e.phase == “ended”) then

yOffs = mainGroup.y

– Get mean finger movement from the 3 last registered finger positions

deltaY = (lastDeltaYArr[1] + lastDeltaYArr[2] + lastDeltaYArr[3])/3

– Keep on scrolling only if finger movement is big enough

if (math.abs(deltaY) >= MIN_MOVEMENT_THAT_RESULTS_IN_AUTOSCROLL) then

scrollSpeedY = deltaY*2

else

scrollSpeedY = 0

end

end

return true

end

local frameRedrawListener = function(e)

– The frameDrawListener is called 30 times per second. The code in here

– is used to keep on scrolling after the user input has stopped

if (math.abs(scrollSpeedY) > 0.5) then

– Scroll the view according to the scroll speed

mainGroup.y = math.min(0, yOffs + scrollSpeedY)

yOffs = mainGroup.y

– Bottom check

if (mainGroup.y < maxYOffs) then

mainGroup.y = maxYOffs

scrollSpeedY = 0

end

– Top check

if (mainGroup.y >= 0) then

mainGroup.y = 0

scrollSpeedY = 0

end

– Reduce the scroll speed (scrolling should go slower and slower)

if (scrollSpeedY ~= 0) then

if (scrollSpeedY < 0) then

scrollSpeedY = scrollSpeedY + 1

else

scrollSpeedY = scrollSpeedY - 1

end

end

end

end

Runtime:addEventListener( “enterFrame”, frameRedrawListener )

Runtime:addEventListener( “touch”, touchHandler )

Hi runewinse,

Thanks for your reply and taking time to explain the details - I appreciate it. I’ll look more closely into that.

For your future posts, to add attachments to the posts you have to click on “More Reply Options” button just below the editing window. If lua files will not be accepted then you can zip it and it will work OK.

In order to keep the formatting of the code when posting wrap it in lua tags, but since these tags disappear from this post then click on the button “Post Formatting Tips” and check there.

keram

Hi,

Yes, zipping the .lua file worked ok. Incredible that a Corona forum won’t allow me to upload .lua files… 

Its the same behavior with tableviews. When using it you can feel its just a bit off.

But are people really accepting this level of performance(*)? Have they all given up and written their own scroll code (as I have been forced to)? What is the point of having a widget that you end up not using?

Surely Corona Labs can do better than this?

(*) If what I experience really is the normal behavior.

Hi,

Would you like to share your scroll code and post it here?

Thanks!

Sure, although it’s a bloody mess to be honest… Use the image posted in my start post to make the code below work.

I use a display group and its y value as a scrollView substitute. Some über-messy things are used to calculate the speed of the swipe etc. And I need to use the framedrawlistener to make the scroll slowdown effect.

But the incredible enough, it seems to work. And if a stupid ass like myself can manage this, why can’t the geniuses at Corona?

local lastDeltaYArr = {0,0,0} local deltaY=0 local lastMoveY=0 local pressStart=0 local scrollSpeedY=0 local yOffs = 0 local maxYOffs = -3000 local w = display.contentWidth local h = display.contentHeight --== The display elements ==-- local bgRect = display.newRect(w/2, h/2, w, h) bgRect:setFillColor(0) -- The display group used as a scrollView local mainGroup = display.newGroup() local scrollImg = display.newImageRect(mainGroup, "image.png", w/2, 4000 ) scrollImg.x = w/2 scrollImg.y = 2000 local function pushDeltaY(dY) for i = #lastDeltaYArr, 2, -1 do lastDeltaYArr[i] = lastDeltaYArr[i-1] end lastDeltaYArr[1] = dY end local function getMainDeltaY() local sum=0 for i = 1, #lastDeltaYArr do sum = sum + lastDeltaYArr[i] end return sum/#lastDeltaYArr end local touchHandler = function(e) local dx = math.abs(e.x - e.xStart) local dy = math.abs(e.y - e.yStart) if (e.phase == "began") then pressStart = system.getTimer() lastMoveY = e.y scrollSpeedY = 0 deltaY = 0 bgRect:setFillColor(1,0.5,0.5) end &nbsp; if (e.phase == "moved") then if (idx ~= -1) then deltaY = e.y - lastMoveY -- print("deltaY = " .. deltaY .. " &nbsp;lastMoveY = " .. lastMoveY) pushDeltaY(deltaY) lastMoveY = e.y mainGroup.y = math.min(0, yOffs + (e.y - e.yStart)) -- print("yOffs: " .. yOffs .. " &nbsp; e.y: " .. e.y .. " &nbsp; e.yStart: " .. e.yStart) -- print("maxYOffs: " .. maxYOffs) if (mainGroup.y \< maxYOffs) then mainGroup.y = maxYOffs end end -- print("mainGroup.y: " .. mainGroup.y) if (mainGroup.y \> 0) then mainGroup.y = 0 end -- print("mainGroup.y: " .. mainGroup.y) end if (e.phase == "ended") then -- print("ENDED: dx=" .. dx .. " dy=" .. dy) yOffs = mainGroup.y -- print("deltaY = " .. deltaY) deltaY = getMainDeltaY() -- print("Mean deltaY = " .. deltaY) if (math.abs(deltaY) \< 7) then scrollSpeedY = 0 else -- Keep on scrolling scrollSpeedY = deltaY\*2 end -- print("scrollSpeedY = " .. scrollSpeedY) -- print("Y speed at letup = " .. deltaY) -- print("yOffs: " .. yOffs) -- print("mainGroup.y: " .. mainGroup.y) bgRect:setFillColor(0) end return true end local frameRedrawListener = function(e) if (math.abs(scrollSpeedY) \> 0.5) then mainGroup.y = math.min(0, yOffs + scrollSpeedY) if (mainGroup.y \< maxYOffs) then print("REACHED BOTTOM") mainGroup.y = maxYOffs scrollSpeedY = 0 end if (mainGroup.y \>= 0) then print("REACHED TOP") mainGroup.y = 0 scrollSpeedY = 0 end yOffs = mainGroup.y -- print("redraw - yOffs: ".. yOffs) if (scrollSpeedY ~= 0) then if (scrollSpeedY \< 0) then scrollSpeedY = scrollSpeedY + 1 else scrollSpeedY = scrollSpeedY - 1 end end -- print("scrollSpeedY = " .. scrollSpeedY .. " &nbsp; mainGroup.y = " .. mainGroup.y) end end Runtime:addEventListener( "enterFrame", frameRedrawListener ) Runtime:addEventListener( "touch", touchHandler )

Again.Sorry for the messy code. This was never meant to be shown in public… Actually it’s all just a emergency solution until I could get the scrollView right…

Right…

Thanks for the code :)  It works OK.

I’m just a complete beginner and don’t see if it’s messy or not.

So it’s obvious that you are not “a stupid ass” and perhaps that the guys at Corona still have some things to learn.