ScrollView - takes too long to render 20 horizontal level icons - how to fix/optimize?

Any suggestions/code/ideas on how to address a performance issue I have with my code using ScrollView.  

Background:

* Using Scrollview to create a horizontal scrolling set of level status boxes.  I have about 20 levels currently.

* When the scene loads it has to create the display objects for each of the 20 levels, even through only about 4 appear on the Scroller at any one stage

* I have looked at TableView however I can’t see how I can use it for a horizontal scrolling list (otherwise this is really what I want)

Question - How can I improve/fix this performance issue?

Ideas - The obvious idea is to write code to only have 4 visible level display objects at a time and reuse these.  Kind of like reinventing the TableView but for the ScrollView just to get horizontal operation.  I’m not sure how the scrollview will go however, as it would only be filled by 4 (not 20).  Perhaps I would still need to fill the scrollview with the outer most Rectangle for each level for the 20 levels…

Did you ever find a solution to this?  I have been trying to implement something similar, images on  a horizontal scrollview but can’t figure it out.  I have tried it out many ways, this is one that I don’t understand why it doesnt render the images horizontally.
------ [ 
local widget = require( “widget” )
 
local  imageFiles = {
    “Icon-xxhdpi.png”,
    “Icon-xxhdpi.png”,
    “Icon-xxhdpi.png”,
    “Icon-xxhdpi.png”,
    “Icon-xxhdpi.png”,
    “Icon-xxhdpi.png”,
}
– ScrollView listener
local function scrollListener( event )
 
    local phase = event.phase
    if ( phase == “began” ) then print( “Scroll view was touched” )
    elseif ( phase == “moved” ) then print( “Scroll view was moved” )
    elseif ( phase == “ended” ) then print( “Scroll view was released” )
    end
 
    – In the event a scroll limit is reached…
    if ( event.limitReached ) then
        if ( event.direction == “up” ) then print( “Reached top limit” )
        elseif ( event.direction == “down” ) then print( “Reached bottom limit” )
        elseif ( event.direction == “left” ) then print( “Reached left limit” )
        elseif ( event.direction == “right” ) then print( “Reached right limit” )
        end
    end
 
    return true
end
 
– Create the widget
local scrollView = widget.newScrollView
{
    left = 0,
    top = 50,
    width = display.contentWidth,
    height = 144,
    bottomPadding = 0,
    id = “onBottom”,
    horizontalScrollDisabled = false,
    verticalScrollDisabled = true,
    listener = scrollListener,
 
}
scrollView:insert( imageFiles )

-----] end

A white screen displays, and the horizontal touch event logs, but no images.  Any suggestions?

Well, based on your code you are trying to insert text strings into a displayObject (scrollView). You would need to call display.newImage for each of those images and then insert each of them into the scrollView, making sure to offset the position manually.

Richard, the strings you are referring to,

local  imageFiles = {
    “Icon-xxhdpi.png”,
    “Icon-xxhdpi.png”,
    “Icon-xxhdpi.png”,
    “Icon-xxhdpi.png”,
    “Icon-xxhdpi.png”,
    “Icon-xxhdpi.png”,
}

I thought that was a table storing image files.  Wrong?  I believe that I tried it the way you are inferring and could not get the images to set correctly.  How do you manually set the images in a scroll view?  I could set one crrectly, but others to the right, not so much.

thanks for the suggestions.

A scrollView is basically a special type of displayGroup that allows you to scroll with your finger. So to add images in code, you need to make the code object (“display object”) that displays the image.

“Icon-xxhdpi.png” is not an image. It’s just a text string. As with any other programming language, you need to tell the computer (phone) what to do with the string.

[lua]-- Makes an image, provided that it’s in the same directory as main.lua

– you can say stuff like “images/filename.png” if it’s in a subfolder

local myImage1 = display.newImage(“Icon-xxhdpi.png”)[/lua]

From there, you can insert the image displayObject into your scrollView.

[lua]myCoolScrollView:insert(myImage1)[/lua]

If you have a long list of images to make, you can automate the process somewhat.For example, using your imageFiles{} table,

[lua] – Makes a regular table, where each index is a displayObject.

local imageObjects = {}

for i = 1, #imageFiles do

  imageObjects[i] = display.newImage(imageFiles[i])

  myCoolScrollView:insert(imageObjects[i])

end[/lua]

Did you ever find a solution to this?  I have been trying to implement something similar, images on  a horizontal scrollview but can’t figure it out.  I have tried it out many ways, this is one that I don’t understand why it doesnt render the images horizontally.
------ [ 
local widget = require( “widget” )
 
local  imageFiles = {
    “Icon-xxhdpi.png”,
    “Icon-xxhdpi.png”,
    “Icon-xxhdpi.png”,
    “Icon-xxhdpi.png”,
    “Icon-xxhdpi.png”,
    “Icon-xxhdpi.png”,
}
– ScrollView listener
local function scrollListener( event )
 
    local phase = event.phase
    if ( phase == “began” ) then print( “Scroll view was touched” )
    elseif ( phase == “moved” ) then print( “Scroll view was moved” )
    elseif ( phase == “ended” ) then print( “Scroll view was released” )
    end
 
    – In the event a scroll limit is reached…
    if ( event.limitReached ) then
        if ( event.direction == “up” ) then print( “Reached top limit” )
        elseif ( event.direction == “down” ) then print( “Reached bottom limit” )
        elseif ( event.direction == “left” ) then print( “Reached left limit” )
        elseif ( event.direction == “right” ) then print( “Reached right limit” )
        end
    end
 
    return true
end
 
– Create the widget
local scrollView = widget.newScrollView
{
    left = 0,
    top = 50,
    width = display.contentWidth,
    height = 144,
    bottomPadding = 0,
    id = “onBottom”,
    horizontalScrollDisabled = false,
    verticalScrollDisabled = true,
    listener = scrollListener,
 
}
scrollView:insert( imageFiles )

-----] end

A white screen displays, and the horizontal touch event logs, but no images.  Any suggestions?

Well, based on your code you are trying to insert text strings into a displayObject (scrollView). You would need to call display.newImage for each of those images and then insert each of them into the scrollView, making sure to offset the position manually.

Richard, the strings you are referring to,

local  imageFiles = {
    “Icon-xxhdpi.png”,
    “Icon-xxhdpi.png”,
    “Icon-xxhdpi.png”,
    “Icon-xxhdpi.png”,
    “Icon-xxhdpi.png”,
    “Icon-xxhdpi.png”,
}

I thought that was a table storing image files.  Wrong?  I believe that I tried it the way you are inferring and could not get the images to set correctly.  How do you manually set the images in a scroll view?  I could set one crrectly, but others to the right, not so much.

thanks for the suggestions.

A scrollView is basically a special type of displayGroup that allows you to scroll with your finger. So to add images in code, you need to make the code object (“display object”) that displays the image.

“Icon-xxhdpi.png” is not an image. It’s just a text string. As with any other programming language, you need to tell the computer (phone) what to do with the string.

[lua]-- Makes an image, provided that it’s in the same directory as main.lua

– you can say stuff like “images/filename.png” if it’s in a subfolder

local myImage1 = display.newImage(“Icon-xxhdpi.png”)[/lua]

From there, you can insert the image displayObject into your scrollView.

[lua]myCoolScrollView:insert(myImage1)[/lua]

If you have a long list of images to make, you can automate the process somewhat.For example, using your imageFiles{} table,

[lua] – Makes a regular table, where each index is a displayObject.

local imageObjects = {}

for i = 1, #imageFiles do

  imageObjects[i] = display.newImage(imageFiles[i])

  myCoolScrollView:insert(imageObjects[i])

end[/lua]

Richard9 > thank you for your tips. I’m a newbie and i don’t really understand the step 3 (automate process). Would you kindly explain a little bit more that step?Thank you

Do you have a sample code with this technic with 2 or 3 images that a newbie can understand?

I have made this code : 

display.setStatusBar( display.HiddenStatusBar ) local widget = require( "widget" ) -- Our ScrollView listener local function scrollListener( event ) local phase = event.phase local direction = event.direction if "began" == phase then --print( "Began" ) elseif "moved" == phase then --print( "Moved" ) elseif "ended" == phase then --print( "Ended" ) end -- If the scrollView has reached it's scroll limit if event.limitReached then if "up" == direction then print( "Reached Top Limit" ) elseif "down" == direction then print( "Reached Bottom Limit" ) elseif "left" == direction then print( "Reached Left Limit" ) elseif "right" == direction then print( "Reached Right Limit" ) end end return true end -- Create a ScrollView local scrollView = widget.newScrollView { left = 0, top = 0, width = display.contentWidth, height = display.contentHeight, bottomPadding = 0, id = "onBottom", horizontalScrollDisabled = false, verticalScrollDisabled = true, listener = scrollListener, } --Create a Rectangle local myRectangle1 = display.newRect( 50, 420, 50, 50 ) myRectangle1:setFillColor( 1, 0.2, 0.2 ) local myRectangle2 = display.newRect( 120, 420, 50, 50 ) myRectangle2:setFillColor( 1, 0.2, 0.2 ) local myRectangle3 = display.newRect( 190, 420, 50, 50 ) myRectangle3:setFillColor( 1, 0.2, 0.2 ) local myRectangle4 = display.newRect( 260, 420, 50, 50 ) myRectangle4:setFillColor( 1, 0.2, 0.2 ) local myRectangle5 = display.newRect( 330, 420, 50, 50 ) myRectangle5:setFillColor( 1, 0.2, 0.2 ) local myRectangle6 = display.newRect( 400, 420, 50, 50 ) myRectangle6:setFillColor( 1, 0.2, 0.2 ) local myRectangle7 = display.newRect( 470, 420, 50, 50 ) myRectangle7:setFillColor( 1, 0.2, 0.2 ) local myRectangle8 = display.newRect( 540, 420, 50, 50 ) myRectangle8:setFillColor( 1, 0.2, 0.2 ) local myRectangle9 = display.newRect( 610, 420, 50, 50 ) myRectangle9:setFillColor( 1, 0.2, 0.2 ) scrollView:insert( myRectangle1 ) scrollView:insert( myRectangle2 ) scrollView:insert( myRectangle3 ) scrollView:insert( myRectangle4 ) scrollView:insert( myRectangle5 ) scrollView:insert( myRectangle6 ) scrollView:insert( myRectangle7 ) scrollView:insert( myRectangle8 ) scrollView:insert( myRectangle9 )

Using your “step 3” i wonder what would it looks like?

Thank you

local xPos = 50 -- this is the first rectangle position local yPos = 420 -- this is the yPosition of all your rectangles for i = 1, 9 do -- you have 9 rectangles, so this means to the stuff inside 9 times -- This rectangle is local, so "myRectangle" doesn't exist outside this loop. -- instead, the rects would be scrollView[1], scrollView[2], etc. -- The (optional) first argument of any display object is the parentgroup, so you -- can save using the :insert() command later. local myRectangle = display.newRect(scrollView, xPos, yPos, 50, 50) myRectangle:setFillColor(1, 0.2, 0.2) -- Now you need to increment the xPos so each rectangle is not on the same spot xPos = xPos + 70 end

Hey Richard9,

Sorry for the late reply.

Really appreciated what you didi for me :wink:

It works!

Thanks

Richard9,

I was wondering if you can help me on that one.

I would like to replace the rectangle by images…

my actual code (working) :

--Create a Rectangle local xPos = 50 -- this is the first rectangle position local yPos = 0 -- this is the yPosition of all your rectangles for i = 1, 10 do -- you have 9 rectangles, so this means to the stuff inside 9 times -- This rectangle is local, so "myRectangle" doesn't exist outside this loop. -- instead, the rects would be scrollView[1], scrollView[2], etc. -- The (optional) first argument of any display object is the parentgroup, so you -- can save using the :insert() command later. local myRectangle = display.newRect(xPos, yPos, 91, 122) scrollView:insert( myRectangle ) myRectangle:setFillColor(1, 0.2, 0.2) myRectangle.anchorY = 1 myRectangle.y = display.actualContentHeight + display.screenOriginY -- Now you need to increment the xPos so each rectangle is not on the same spot xPos = xPos + 100 end  

But how should i add series of images…let’s say 10 (same as our rectangle exemple)?

Should i name my images “image01.png,  image02.png…”

Also i think that this line : 

local myRectangle = display.newRect(xPos, yPos, 91, 122) 

will change into : 

local myRectangle = display.newImageRect(??????????, width, height) 

Thank you for your time

Well you have two options

  1. Name them sequentially like you said. inside the loop you would use:

    local myRectangle = display.newImageRect(scrollView, “image”…i, 91, 122)

“…” is a way to join two strings together. So “dude”…1 will appear as “dude1”. In this case i is a number from the loop.

  1. Name them whatever you want, but put them in a table.

    – somewhere before the loop local images = {} images[1] = “coolrect.png” images[2] = “otherimg.png” images[3] = “bluedog.png” – etc… – then later, inside the loop local myRectangle = display.newImageRect(scrollView, images[i], 91, 122)

I prefer #2 because you can specify image sizes too!

local images = {} -- tables can contain tables too! images[1] = { file = "supercool.png", width = 121, height = 244 } images[2] = { file = "homersimpson.png", width=22, height=92 } -- then later, in the loop local myRectangle = display.newImageRect(scrollView, images[i].file, images[i].width, images[i].height)

Hi Richar9,

Thank you for your reply.

This is not working…i have a error in the corona simulator : 

File: ERROR

ERROR: table expected. If this is a function call, you might have used ‘.’ instead of ‘:’

stack traceback:

[C]: in function ‘insert’

?: in function ‘insert’

main.lua:74: in main chunk

 

Here is the code for option 1 ( i named my images “image01.png, image02.png”): 

display.setStatusBar( display.HiddenStatusBar ) local backok = display.newImageRect( "bg.jpg", 360, 570 ) backok.x = display.contentCenterX backok.y = display.contentCenterY local backslide = display.newRect( 0, 396, 0, 173 ) backslide:setFillColor( .365, .808, .773 ) backslide.anchorY = 1 backslide.width = display.contentWidth backslide.x = display.contentCenterX backslide.y = display.actualContentHeight + display.screenOriginY local widget = require( "widget" ) -- Our ScrollView listener local function scrollListener( event ) local phase = event.phase local direction = event.direction if "began" == phase then --print( "Began" ) elseif "moved" == phase then --print( "Moved" ) elseif "ended" == phase then --print( "Ended" ) end -- If the scrollView has reached it's scroll limit if event.limitReached then if "up" == direction then print( "Reached Top Limit" ) elseif "down" == direction then print( "Reached Bottom Limit" ) elseif "left" == direction then print( "Reached Left Limit" ) elseif "right" == direction then print( "Reached Right Limit" ) end end return true end -- Create a ScrollView local scrollView = widget.newScrollView { left = 0, top = -25, width = display.contentWidth, height = display.contentHeight, bottomPadding = 0, id = "onBottom", horizontalScrollDisabled = false, verticalScrollDisabled = true, listener = scrollListener, hideBackground = true, } --Create a Rectangle local xPos = 50 -- this is the first rectangle position local yPos = 0 -- this is the yPosition of all your rectangles for i = 1, 10 do -- you have 9 rectangles, so this means to the stuff inside 9 times -- This rectangle is local, so "myRectangle" doesn't exist outside this loop. -- instead, the rects would be scrollView[1], scrollView[2], etc. -- The (optional) first argument of any display object is the parentgroup, so you -- can save using the :insert() command later. local myRectangle = display.newImageRect(scrollView, "image"..i, 91, 122) scrollView:insert( myRectangle ) myRectangle:setFillColor(1, 0.2, 0.2) myRectangle.anchorY = 1 myRectangle.y = display.actualContentHeight + display.screenOriginY -- Now you need to increment the xPos so each rectangle is not on the same spot xPos = xPos + 100 end local groupslide = display.newGroup() groupslide:insert( backslide ) groupslide:insert( scrollView ) groupslide.alpha = 0 local myRoundedRect = display.newRoundedRect( 150, 150, 100, 50, 12 ) myRoundedRect.strokeWidth = 3 myRoundedRect:setFillColor( 0.5 ) myRoundedRect:setStrokeColor( 1, 0, 0 )

And for option 2 : 

File: ERROR

ERROR: table expected. If this is a function call, you might have used ‘.’ instead of ‘:’

stack traceback:

[C]: in function ‘insert’

?: in function ‘insert’

main.lua:78: in main chunk

display.setStatusBar( display.HiddenStatusBar ) local backok = display.newImageRect( "bg.jpg", 360, 570 ) backok.x = display.contentCenterX backok.y = display.contentCenterY local backslide = display.newRect( 0, 396, 0, 173 ) backslide:setFillColor( .365, .808, .773 ) backslide.anchorY = 1 backslide.width = display.contentWidth backslide.x = display.contentCenterX backslide.y = display.actualContentHeight + display.screenOriginY local widget = require( "widget" ) -- Our ScrollView listener local function scrollListener( event ) local phase = event.phase local direction = event.direction if "began" == phase then --print( "Began" ) elseif "moved" == phase then --print( "Moved" ) elseif "ended" == phase then --print( "Ended" ) end -- If the scrollView has reached it's scroll limit if event.limitReached then if "up" == direction then print( "Reached Top Limit" ) elseif "down" == direction then print( "Reached Bottom Limit" ) elseif "left" == direction then print( "Reached Left Limit" ) elseif "right" == direction then print( "Reached Right Limit" ) end end return true end -- Create a ScrollView local scrollView = widget.newScrollView { left = 0, top = -25, width = display.contentWidth, height = display.contentHeight, bottomPadding = 0, id = "onBottom", horizontalScrollDisabled = false, verticalScrollDisabled = true, listener = scrollListener, hideBackground = true, } local images = {} images[1] = "image01.png" images[2] = "image02.png" images[3] = "image03.png" --Create a Rectangle local xPos = 50 -- this is the first rectangle position local yPos = 0 -- this is the yPosition of all your rectangles for i = 1, 10 do -- you have 9 rectangles, so this means to the stuff inside 9 times -- This rectangle is local, so "myRectangle" doesn't exist outside this loop. -- instead, the rects would be scrollView[1], scrollView[2], etc. -- The (optional) first argument of any display object is the parentgroup, so you -- can save using the :insert() command later. local myRectangle = display.newImageRect(scrollView, images[i], 91, 122) scrollView:insert( myRectangle ) myRectangle:setFillColor(1, 0.2, 0.2) myRectangle.anchorY = 1 myRectangle.y = display.actualContentHeight + display.screenOriginY -- Now you need to increment the xPos so each rectangle is not on the same spot xPos = xPos + 100 end local groupslide = display.newGroup() groupslide:insert( backslide ) groupslide:insert( scrollView ) groupslide.alpha = 0 local myRoundedRect = display.newRoundedRect( 150, 150, 100, 50, 12 ) myRoundedRect.strokeWidth = 3 myRoundedRect:setFillColor( 0.5 ) myRoundedRect:setStrokeColor( 1, 0, 0 )

Thank you for your time.

 

Well the really obvious problem here is that your loop has a count of 10 but there are only 3 images. So it’s trying to call images[4] but it doesn’t exist.

If you only want to loop as many times as you have images, then…

for i = 1, #images do -- #images is the total count of the images table.

Also, I would remove scrollView from the display.newImageRect call. Just let the second line do its job (the insert line)

Hi Richar9,

Thank you for that, It is my bad! I should have been more careful about this!!

Now it is working but…

I’m loosing xPos, yPos inside :  

local myRectangle = display.newRect(xPos, yPos, 91, 122)

I have now  : 

local myRectangle = display.newImageRect(images[i], 91, 122)

Trying to add : 

local myRectangle = display.newImageRect(xPos, yPos, images[i], 91, 122)

or

local myRectangle = display.newImageRect(images[i], xPos, yPos, 91, 122)

doesn’t work.

Thank you for your time. I am really learning a lot with your methods.

i still didn’t find anything to fix this issue…this is really strange!

Look at the documentation for newImageRect.  newImageRect does not support x/y inside the original call. So leave it out of object creation and then set .x and .y separately.

local img = display.newImageRect(parent, file, width, height) img.x = xPos img.y = yPos