Custom Mobile Scroller

Got a LiveCode personal license? Are you a beginner, hobbyist or educator that's new to LiveCode? This forum is the place to go for help getting started. Welcome!

Moderators: Klaus, FourthWorld, heatherlaine, kevinmiller

Post Reply
William Jamieson
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 212
Joined: Fri Feb 01, 2013 1:31 am
Location: Palo Alto, CA put williamdjamieso into tEmail / put n@gmail.com after tEmail/ revmail tEmail
Contact:

Custom Mobile Scroller

Post by William Jamieson » Wed Sep 24, 2014 9:54 pm

Hello. I have a number of stacks built and tested on Android that all function perfectly well except that the native mobile scroller that I am using is intercepting the touch messages to the card after half a centimeter distance is swiped. Has anyone created a custom scroller that works in the message path, or within livecode so that I can intercept messages from the scroller? Anyone have any proposed ideas how to build a really smooth functioning mobile scroller?

Code:
I am using this command to be specific: MobileControlCreate "Scroller"

Device:
I am testing on this device: Samsung Galaxy s3

Object:
I have a sliding object that acts as a switch under the Native scroller object created by the code above.

Problem:
After a few pixel swipe, all mouse and touch messages cease to get passed to any of my livecode scripts and get intercepted by the mobile scroller rendering all my controls on the screen useless.

Thank you all for your input and your help!!

Lets hope that we can come up with one common solution for everyone!

-Will

newtronsols
Posts: 192
Joined: Tue Mar 11, 2014 12:57 pm

Re: Custom Mobile Scroller

Post by newtronsols » Thu Sep 25, 2014 10:35 pm

I have the scroller created in on opencard - I think I read this somewhere on the forum.

I also use in:

on scrollerDidScroll....
set the top of group "entry" to -vOffset

where group "entry" are all my scrolling fields etc grouped.

This was my own 'trial and error-lots' approach when all other means failed.

I have downloaded the standard LC examples and they work - but I suspect it was because I was using old/new Mobgui objects that I had problems and ended up with my very strange answer - that works.

I'd love to find a way to set the fullscreenmode of me to ?[ideally working like exactfit] to work with a scrolling group. So a scrolling group resizes landscape/portrait on all Android devices. If you figure this out for Android let me know.

One of my scrolling apps is:
https://play.google.com/store/apps/deta ... p&hl=en_GB

William Jamieson
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 212
Joined: Fri Feb 01, 2013 1:31 am
Location: Palo Alto, CA put williamdjamieso into tEmail / put n@gmail.com after tEmail/ revmail tEmail
Contact:

Re: Custom Mobile Scroller

Post by William Jamieson » Sat Sep 27, 2014 11:50 pm

Thank you for your input newTronsols

Using the method you described above (using the vScroll) I have got the page to sucessfully scroll without pixelation or "jumping" (movements that should be smooth looking but move more than a few pixels at a time) but the difficult part for me was including the momentum and physics in such a way that the vScroll remained smooth. The main reason I believe is that I created a loop. But when I timed the loop on my android device, it was sending the message to change the vScroll every 130 milliseconds because that is how fast the processor could function, but still made the movement of the scroll very choppy for me.

If anyone has a solution to that issue, that would be another way of solving the overall problem.

-Will

William Jamieson
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 212
Joined: Fri Feb 01, 2013 1:31 am
Location: Palo Alto, CA put williamdjamieso into tEmail / put n@gmail.com after tEmail/ revmail tEmail
Contact:

Re: Custom Mobile Scroller

Post by William Jamieson » Mon Sep 29, 2014 1:29 am

So I solved the issue. I ended up using a moving group with the touch handlers. It is not perfect like facebook, but I think its as good as the scrolling done by Linkedin's app.

To create momentum, I modified the animation engine to have a new handler called "decelerate". aeMoveTo and aeGeneral have to modified as well.

The next issue was to understand the way people were using their finger to gently and briefly swipe the screen. The problem with this is that the sample rate from the touchMove handler or the MouseMove handler are about 130 milliseconds which is in no way possible on a touch that is less than that. To overcome this I created a repeat loop that samples and builds a list that goes for about 110 milliseconds or so with 10 datapoints, then used that to calculate an initial velocity.

I kept the equations for distance and velocity simple by using physics equations rather than calculus. They are: X = Xi + Vo * t - (0.5) * a * t ^ 2 and Vf = sqrt(Vo^2 - 2 * a * X)

Below I will post the scripts I used to get the mobile scroller to scroll smoothly while accepting light and quick swipes.

Group Script:

Code: Select all

local sTracking, yMousei, yLoci, xLoci, sIsScrolling, tUpperBound, tLowerBound
local sIsMoving, sTouchEnded, sMovement

constant kTouchSensitivity = 5 --minimum pixels on a swipe-tap
constant kMoveSensitivity = 0.06 --minimum velocity to create momentum
constant kScreenSwipeTime = 250 --number of milliseconds that are considered a long scroll rather than a swipe

on Momentum
   if sIsScrolling is "true" then -- from the initial touch
      put "false" into sIsScrolling
      put "false" into sIsMoving
      ##calculate the speed
      if abs((item 2 of line 1 of sTracking) - (item 2 of the last line of sTracking)) >= kScreenSwipeTime then --total screen touch time big
         put "Scrolling" & cr after sMovement
         if ((item 2 of line 1 of sTracking) - (item 2 of line 3 of sTracking)) <> 0 then --Dont move if stopped
            put ((item 1 of line 1 of sTracking) - (item 1 of line 3 of sTracking)) / \
                  ((item 2 of line 1 of sTracking) - (item 2 of line 3 of sTracking)) into tInitialVelocity
            if abs(tInitialVelocity) > kMoveSensitivity then --No micro adjustments
               put "true" into sIsMoving
               aeMoveTo (the long id of me), (item 1 of the loc of me), 1, 20000, "decelerate", tInitialVelocity, tUpperBound, tLowerBound
            end if
         end if
      end if
      ##Makes sure that swipe gets sent too if sScrolling is true, and touch is released
      Swipe
   end if
end Momentum

on Swipe
   if sIsMoving is "false" then
      ##Swipe --Not caught by the handlers
      if line 10 of sMovement is empty then exit Swipe
      if abs((item 1 of line 1 of sMovement) - (item 1 of line 10 of sMovement)) > kTouchSensitivity then
         put ((item 1 of line 1 of sMovement) - (item 1 of line 10 of sMovement)) / \
               ((item 2 of line 1 of sMovement) - (item 2 of line 10 of sMovement)) into tInitialVelocity
         put "true" into sIsMoving
         aeMoveTo (the long id of me), (item 1 of the loc of me), 1, 20000, "decelerate", (1.3 * tInitialVelocity), tUpperBound, tLowerBound
      end if
   end if
end Swipe

on aeMoveDone
   put "false" into sIsMoving
   ChangeMovingStatus "false"
end aeMoveDone


on TouchRelease
   Momentum
end TouchRelease

on TouchStart
   global gGap
   if sIsMoving is "true" then
      aeCancel
   end if
   
   ChangeMovingStatus "true"
   put "false" into sIsMoving
   put empty into sTracking
   put the mouseV into yMousei
   put item 1 of the loc of me into xLoci
   put item 2 of the loc of me into yLoci
   put "true" into sIsScrolling
   put the bottom of grp "Nav Bar" of this card + (0.5 * the height of me) into tUpperBound
   put the bottom of this card - (0.5 * the height of me) into tLowerBound
   startRecording
end TouchStart

on TouchMove pID, pX, pY
   if sIsScrolling is "true" then
      if the mouse is down then
         put the mouseV - yMousei into tDelta
         ##Create lower and upper boundaries
         if item 2 of the loc of me + tDelta > tUpperBound then
            set the loc of me to xLoci, tUpperBound
         else if item 2 of the loc of me + tDelta < tLowerBound then
            set the loc of me to xLoci, tLowerBound
         else
            set the loc of me to xLoci, yLoci + tDelta
         end if
         put tDelta & comma & the milliseconds & cr before sTracking
      else
         Momentum
      end if
   end if
end TouchMove

on TouchEnd
   Momentum
end TouchEnd

on StartRecording
   put empty into sMovement
   repeat with i = 1 to 10
      put the mouseV & comma & the milliseconds & cr before sMovement
      wait 10 milliseconds with messages
   end repeat
   if abs((item 1 of line 1 of sMovement) - (item 1 of line 10 of sMovement)) > kTouchSensitivity then
      if sIsScrolling is "false" then
         put "false" into sMoving
         swipe
      end if
   end if
end StartRecording
Here is the AE General handler rewritten for deceleration

Code: Select all

command aeGeneral
   lock screen
   local tTimeLost,tFramesLost,tElapsed
   local tX,tY,tDuration,tMethod,tEndX,tStartX,tDestX,tStartY,tDestY,tStartTime,tStartVelocity,tDirection
   local tUpperBound,tLowerBound
   
   ##Stops the move immediately
   if tCancel is "true" then
      put "false" into tCancel
      repeat for each line pControl in the keys of tAEEasing
         send "aeMoveDone" to pControl
      end repeat
      exit aeGeneral
   end if
   
   if sAEFrameRate is not a number then aeResetFrameRate
   put the milliseconds into tElapsed
   put the milliseconds - sTimeTaken into tTimeLost
   put tTimeLost - (1000/ sAEFrameRate) into tTimeLost
   put ((1000/sAeFramerate) + tTimeLost) into tFramesLost
   if tFramesLost<>0 then
      put 1000/tFramesLost into tFramesLost
   end if
   if tFramesLost>sAEFrameRate then put sAEFrameRate into tFramesLost
   put tFramesLost into sAERealFrameRate
   put the millisecs into sTimeTaken
   --lock screen
   repeat for each line pControl in the keys of tAEEasing
      
      send "aeEnterFrame"  && "aeMoveTo" to pcontrol
      put item 1 of tAEEasing[pControl] into tDestX 
      put item 2 of tAEEasing[pControl] into tDestY
      put item 3 of tAEEasing[pControl] into tDuration
      put item 4 of tAEEasing[pControl] into tMethod
      put item 5 of tAEEasing[pControl] into tStartX
      put item 6 of tAEEasing[pControl] into tStartY
      put item 7 of tAEEasing[pControl] into tStartTime
      put item 8 of tAEEasing[pControl] into tStartVelocity
      put item 9 of tAEEasing[pControl] into tUpperBound
      put item 10 of tAEEasing[pControl] into tLowerBound
      
      ##Determine if the velocity is negative or positive
      if tStartVelocity > 0 then
         put "down" into tDirection
         put tStartY + ((tStartVelocity ^ 2) * 0.5 * (1/sAcceleration)) - 5 into tDestY
      else if tStartVelocity < 0 then
         put "up" into tDirection
         put tStartY + ((-1) * ((tStartVelocity ^ 2) * 0.5 * (1/sAcceleration))) + 5 into tDestY
      end if
      
      if tStartTime="pending" then next repeat
      switch tMethod
         case "in"
            put aeEaseIn(tStartX,tDestX,tDuration,the milliseconds-tStartTime,3) into tX
            put aeEaseIn(tStartY,tDestY,tDuration,the milliseconds-tStartTime,3) into ty
            break
         case "out"
            put aeEaseOut(tStartX,tDestX,tDuration,the milliseconds-tStartTime,aeExponent) into tX
            put aeEaseOut(tStartY,tDestY,tDuration,the milliseconds-tStartTime,aeExponent) into ty
            break
         case "decelerate"
            --            put aeDecelerate(tStartX,the milliseconds-tStartTime,tStartVelocity) into tX
            put tStartX into tX
            put aeDecelerate(tStartY,the milliseconds-tStartTime,tStartVelocity,tDirection,tDestY,tUpperBound,tLowerBound) into ty
            break
         case "inOut"
            put aeEaseInOut(tStartX,tDestX,tDuration,the milliseconds-tStartTime,3) into tX
            put aeEaseInOut(tStartY,tDestY,tDuration,the milliseconds-tStartTime,3) into ty
            break
         case "bounce"
            put aeBounceEaseOut(tStartX,tDestX,tDuration,the milliseconds-tStartTime) into tX
            put aeBounceEaseOut(tStartY,tDestY,tDuration,the milliseconds-tStartTime) into ty
            break
         case "overshoot"
            put aeOverShootEaseOut(tStartX,tDestX,tDuration,the milliseconds-tStartTime) into tX
            put aeOverShootEaseOut(tStartY,tDestY,tDuration,the milliseconds-tStartTime) into ty
            break
         default
            put aeEaseIn(tStartX,tDestX,tDuration,the milliseconds-tStartTime,1) into tX
            put aeEaseIn(tStartY,tDestY,tDuration,the milliseconds-tStartTime,1) into ty
            break
      end switch
      if the milliseconds-tStartTime<tDuration then
         set the loc of pControl to tX,tY
      else
         if tMethod is not "decelerate" then
            if tDestx,tDestY is a point then
               set the loc of pControl to tDestX,tDestY
            end if
         else
            ##Case decelerate ending
         end if
         delete variable tAeEasing[pControl]
         send "aeMoveDone" to pControl
      end if
      send "aeExitFrame" && "aeMoveTo" to pControl
   end repeat
   --   unlock screen
   unlock screen
   put the milliseconds-tElapsed into tElapsed
   if the keys of tAeEasing is not empty then
      if tElapsed < 1000 / sAEFrameRate then
         if "aeGeneral" is not in the pendingmessages then
            send "aeGeneral" to me in (1000/sAEFRameRate)- tElapsed millisecs
         end if
      else
         if "aeGeneral" is not in the pendingmessages then
            send "aeGeneral" to me in 5 millisecs
         end if
      end if
   end if
   --   wait 0 milliseconds with messages
end aeGeneral
Here is the AE MoveTo handler rewritten for deceleration

Code: Select all

on aeMoveTo
   local tControl,tX,tY,tDuration,tMethod,theValue,tVelocity,tUpperBound,tLowerBound
   if sAEFrameRate is empty then aeResetFrameRate
   repeat with i=1 to paramcount()
      if i<paramcount() then
         put param(i)&"," after theValue
      else
         put param(i) after theValue
      end if
   end repeat
   put item 1 of theValue into tControl
   put item 2 of theValue into tX
   put item 3 of theValue into tY
   put item 4 of theValue into tDuration
   put item 5 of theValue into tMethod
   put item 6 of theValue into tVelocity
   put item 7 of theValue into tUpperBound
   put item 8 of theValue into tLowerBound
   if there is no tControl then return "error:"&&tControl&&"is not a valid control or group or stack"
   if word 1 of the long name of tControl is "card" then return "error: can not move a card"
   if tX is not an integer then return "error:"&&tX&","&tY&&"is not a valid point"
   if tY is not an integer then return "error:"&&tX&","&tY&&"is not a valid point"
   if tduration is not a number then return "error:"&&tDuration&&"is not a valid duration"
   if tMethod is not among the items of "in,out,inout,bounce,overshoot,decelerate" then return "error:"&&tDuration&&"is not a valid easing method. Must be in,out,inout,bounce,overshoot,decelerate"
   if not sAELockMoves then
      put tX,tY,tDuration,tMethod,item 1 of the the loc of tControl,item 2 of the loc of tControl,the milliseconds,tVelocity,tUpperBound,tLowerBound into tAeEasing[the long id of tControl]
      if "aeGeneral" is not in the pendingmessages then
         aeGeneral
      end if
   else
      put tX,tY,tDuration,tMethod,item 1 of the the loc of tControl,item 2 of the loc of tControl,"pending",tVelocity,tUpperBound,tLowerBound into tAeEasing[the long id of tControl]
   end if
end aeMoveTo
Here is a handler that I wrote to stop the momentum when a new touch is placed on the group

Code: Select all

local aeExponent, tCancel

command SetAEExponent pExponent
   put pExponent into aeExponent
end SetAEExponent

command aeCancel
   put "true" into tCancel
end aeCancel
Here is the AE Deceleration Handler that I wrote

Code: Select all

function aeDecelerate pStart,pElapsedTime,pStartVelocity,pDirection,pFinish,pUpperBound,pLowerBound
   try
      if pDuration<=pElapsedTime then return pEnd
      if pElapsedTime<=0 then return pStart
      
      if pDirection = "down" then
         put pStart + pStartVelocity * pElapsedTime - (0.5 * (sAcceleration) * ((pElapsedTime) ^ 2)) into Xf
         if pFinish <= Xf or pUpperBound < Xf then
            aeCancel
         end if
      else if pDirection = "up" then
         put pStart + pStartVelocity * pElapsedTime - (0.5 * ((-1) * sAcceleration) * ((pElapsedTime) ^ 2)) into Xf
         if pFinish > Xf or pLowerBound > Xf then
            aeCancel
         end if
      end if
      return Xf
      
   catch theError
      return theError
   end try
   
end aeDecelerate
On the stack I preopened with this:

Code: Select all

on preOpenStack
   set the compositorTileSize of this stack to 128
   set the compositorType of this stack to "software"
   set the acceleratedrendering of me to true
   set the fullscreenmode of me to "ShowAll"
   go to card "Edit Event"
end preOpenStack
I also set the layermode of all the objects within the group to "dynamic"


BTW If you dont have the Animation Engine, give up livecoding now because you are making it way more difficult than it needs to be. The animation engine is a must have for anyone that is not an expert programmer and it is opensource and able to be modified. My code above only works with the Animation Engine open in Livecode. Well worth every penny.

I hope this helps anyone else who has been frustrated with trying to get smooth and responsive scrolling of large groups on mobile devices. Old skoolers, please feel free to contribute if you have any improvements!

-Will

keram
Posts: 340
Joined: Fri Nov 08, 2013 4:22 am

Re: Custom Mobile Scroller

Post by keram » Mon Sep 29, 2014 11:00 am

Hi Will,
William Jamieson wrote:I hope this helps anyone else who has been frustrated with trying to get smooth and responsive scrolling of large groups on mobile devices.
Will your scroller work with any group, like Data Grid?

I'm mainly interested in making the Data Grid scrolling more responsive and faster.

keram
Using the latest stable version of LC Community 6.7.x on Win 7 Home Premium, 64bit

newtronsols
Posts: 192
Joined: Tue Mar 11, 2014 12:57 pm

Re: Custom Mobile Scroller

Post by newtronsols » Thu Oct 02, 2014 9:50 pm

Interestingly I was testing using:
set the top of group "entry" to -vOffset
set the left of group "entry" to -hOffset

It 'feels' horribly wrong when you combine the two.

For left/right with Twitter on Android it is entirely different to the up/down motion. Perhaps I need left/right 'traction'.

William Jamieson
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 212
Joined: Fri Feb 01, 2013 1:31 am
Location: Palo Alto, CA put williamdjamieso into tEmail / put n@gmail.com after tEmail/ revmail tEmail
Contact:

Re: Custom Mobile Scroller

Post by William Jamieson » Sat Oct 04, 2014 4:20 am

Yeah I havent tried it with side to side motion. Let me know what changes you make to do that.

I also noticed that there is a really big bug in the code when testing. (BTW I have barely tested this) On my Android device, I don't know the action that does it, but I was able to accelerate the group at rapid speeds flying to, evidently the loc of 320,32000!!! LOL Please help me debug this...

@Keram
I suggest you use the Merg Datagrid Scroller. It is a plugin that does a lot better than my script at scrolling and works really well with the datagrid. It is real easy to implement and only costs a few bucks. Find it at http://www.mergext.com

newtronsols
Posts: 192
Joined: Tue Mar 11, 2014 12:57 pm

Re: Custom Mobile Scroller

Post by newtronsols » Wed Nov 05, 2014 1:25 pm

Have you been able to combine Livecode 7, a scroller (LC/or Mobgui, other) and set the fullscreenmode - any mode - that works successfully?

I simply... [laugh]..want a scroller that resizes with a fullscreenmode - ideally exactFit - and works portrait/landscape.

William Jamieson
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 212
Joined: Fri Feb 01, 2013 1:31 am
Location: Palo Alto, CA put williamdjamieso into tEmail / put n@gmail.com after tEmail/ revmail tEmail
Contact:

Re: Custom Mobile Scroller

Post by William Jamieson » Wed Nov 05, 2014 7:37 pm

@ NewTronsols: I have not had any issue with using the native scroller with fullscreenmode "exactfit". Although I have not tried it in Livecode 7 yet. I am still doing the deployment of my apps through 6.6.2 due the amount of bugs still in the system for android on 7.

Post Reply

Return to “Getting Started with LiveCode - Complete Beginners”