Sprites with touch listener not receiving certain events on scroll view

Hello,

I’m currently implementing a level select module with scroll view as foundation. My problem is I don’t want to use widget buttons, because i need some features of sprites. But my touch listener for that sprite is not called but for the begin phase because of the scroll view. I read something abou widget object:takeFocus(), but thats for getting the focus back to the scroll view. So my guess is I did something wrong requesting focus, but I don’t know what.

touch listener:

 function(event, params) if(event.phase == "began") then print("began") display.getCurrentStage( ):setFocus(event.target, event.id) elseif(event.phase == "moving") then print("moved") elseif(event.phase == "ended") then print("ended") display.getCurrentStage( ):setFocus( nil ) end return true end

widget.newScrollView( { left = options.left, top = options.top, width = options.width, height = options.height, horizontalScrollDisabled = not options.horizontalScrollEnabled, verticalScrollDisabled = not options.verticalScrollEnabled, hideScrollBar = true, listener = function(event) if event.phase == "began" then print("began") elseif event.phase == "moved" then print("moved") elseif event.phase == "ended" then print("ended") else print(event.phase) end return true end } )

My test output is: began(from touch listener), began (scrollview), moved++, ended(scrollView)

Greetings Dominik

Hi Dominik,

Can you post your code where you place (at least) one button, along with its listener addition and so forth?

Thanks,

Brent

Of course. I have did a little more testing and the touch listener is only called once: The first time clicking(touch begin) on the sprite. Then is never called again. Even if just clicked normal again. So a output looks like: “test0, test1, began, moved…, ended,began,moved…,ended” for clicking on the sprite. Moved may be printed 0 times also.

I’m to thank you for your effort.

The listener which i create in another module.

 function(event, self, params)       print("test0") if(event.phase == "began") then print("test1") -- handle touch corrext display.getCurrentStage( ):setFocus(event.target, event.id) play\_touch\_id = event.id elseif(event.phase == "moving") then print("test2") elseif(event.phase == "ended") then print("test3") display.getCurrentStage( ):setFocus( nil ) sfx.play(sfx.button\_click) composer.gotoScene(params.scene, {effect = "slideDown", time=250}) end return true end

The creation of the sprites.

[...] local cells = {} for \_y=1, line\_count do cells[\_y] = {} for \_x = 1, cell\_per\_line do if (\_y-1)\*cell\_per\_line+\_x \<= level\_count then cells[\_y][\_x] = display.newSprite( spriteSheet, sequenceData ) local \_cell = cells[\_y][\_x] class:insert( \_cell ) \_cell.x, \_cell.y = \_x \* cellSize, \_y \* cellSize local \_level = levelData[(\_y-1)\*cell\_per\_line+\_x] \_cell.level = \_level if \_level.status == 0 then \_cell:setFrame("1") elseif \_level.status == 1 then \_cell:setFrame("2") elseif \_level.status == 2 then \_cell:setFrame("3" ) end --add touch listener \<-- here the listener --\> \_cell:addEventListener( "touch", function(event) if \_cell.level.status ~= 0 then \_onTouch(event, \_cell, \_level.params) end end ) if debug then print("level\_select.lua::factory:new()-createLevelGrid()#DEBUG=\<Cell created\>#ID=\<"..tostring((\_y-1)\*cell\_per\_line+\_x).."\>".."#STATUS=\<"..tostring(\_level.status).."\>") end else if debug then print("level\_select.lua::factory:new()-createLevelGrid()#DEBUG=\<No cell because of level count\>") end end end end [...]

Hi Dominik,

What is the name of your listener function? It doesn’t appear that you’ve named it (which should throw an error). Also, you pass three parameters to it, but it accepts only one (event). There may also be an issue in your internal listener function and conditional logic… I suggest you try to work backwards and simplify this a bit, and test to see that you’re getting the proper responses. I know that’s not a great deal of help, but this is probably going to require some hands-on debugging.

Best regards,

Brent

Seems like I should post more of the code, as I’m not naming the function because I just pass it to my level_select module.

I added a print command to my event listener to see if that’s called properly, but also just on first began phase once called. Not after that. So something seems to be wrong at _cell:addEventListener(…), but I can’t figure out what.

local factory = {} --[[----------------------------- level\_data= { {level = "data.level0", title = "Tutorial", status = 1}, {level = "data.level1", title = "Level 1", status = 2}, {level = "data.level2", title = "Level 2", status = 0} } 0 = locked, 1 = unlocked, 2 = done spriteSheetImage = "images/level\_view.png" options = { left = , top = , width = , height = , horizontalScrollDisabled = , verticalScrollDisabled = , cellSize = , } --]]----------------------------- ------------------------------------------------------------ -------- ALWAYS SQUARE SO suitable for most purposes ------ ------------------------------------------------------------ function factory:new(\_onTouch,\_levelData, \_spriteSheetImage, \_options, \_debug) local debug = \_debug local widget = require("widget") if not \_options then print("level\_select.lua::factory:new()#MALFUNCTION=\<options is nil\>") end local options = \_options local class = widget.newScrollView( { left = options.left, top = options.top, width = options.width, height = options.height, horizontalScrollDisabled = not options.horizontalScrollEnabled, verticalScrollDisabled = not options.verticalScrollEnabled, hideScrollBar = true, listener = function(event) if event.phase == "began" then print("began") elseif event.phase == "moved" then print("moved") elseif event.phase == "ended" then print("ended") else print(event.phase) end return true end } ) local levelData = \_levelData local spriteSheetImage = \_spriteSheetImage local spriteSheet = graphics.newImageSheet( spriteSheetImage, { width = options.cellSize, height = options.cellSize, numFrames = 3, sheetContentWidth = options.cellSize \* 3, sheetContentHeight = options.cellSize } ) local sequenceData = { name="cell\_view", start = 1, count = 3} local function createLevelGrid() local level\_count = #levelData if(options.horizontalScrollEnabled and options.verticalScrollEnabled) then -- scroll uncontrollable in every direction UAAAH -- not elseif(options.verticalScrollEnabled and options.width) then -- vertical local maxSize = options.width local cellSize = options.cellSize local cell\_per\_line = math.floor(maxSize / cellSize) local line\_count = math.ceil(level\_count / cell\_per\_line) local cells = {} if debug then print("level\_select.lua::factory:new()-createLevelGrid()#DEBUG=\<\>#LEVEL\_COUNT=\<"..tostring(level\_count).."\>") end for \_y=1, line\_count do cells[\_y] = {} for \_x = 1, cell\_per\_line do if (\_y-1)\*cell\_per\_line+\_x \<= level\_count then cells[\_y][\_x] = display.newSprite( spriteSheet, sequenceData ) local \_cell = cells[\_y][\_x] class:insert( \_cell ) \_cell.x, \_cell.y = \_x \* cellSize, \_y \* cellSize local \_level = levelData[(\_y-1)\*cell\_per\_line+\_x] \_cell.level = \_level if \_level.status == 0 then \_cell:setFrame("1") elseif \_level.status == 1 then \_cell:setFrame("2") elseif \_level.status == 2 then \_cell:setFrame("3" ) end --add touch listener lol, set things \<-- print --\> \_cell:addEventListener( "touch", function(event) print("All right here") if \_cell.level.status ~= 0 then \_onTouch(event, \_cell, \_level.params) end end ) if debug then print("level\_select.lua::factory:new()-createLevelGrid()#DEBUG=\<Cell created\>#ID=\<"..tostring((\_y-1)\*cell\_per\_line+\_x).."\>".."#STATUS=\<"..tostring(\_level.status).."\>") end else if debug then print("level\_select.lua::factory:new()-createLevelGrid()#DEBUG=\<No cell because of level count\>") end end end end elseif(options.horizontalScrollEnabled and options.height) then -- horizontal local maxSize = options.height else print("level\_select.lua::factory:new():createLevelGrid#MALFUNCTION=\<No scroll enabled. Options may be malformatted.\>") end end local function updateLevelGrid() end function class:initialize( ) createLevelGrid() end function class:onDestroy() self:removeSelf( ) end class.x, class.y = \_x,\_y return class end return factory

And here is the scene from which I call the level_select module:

function scene:create( event, params ) local sceneGroup = self.view -- Initialize the scene here. -- Example: add display objects to "sceneGroup", add touch listeners, etc. level\_select = level\_select\_factory:new( function(event, self, params) print("test0") if(event.phase == "began") then print("test1") -- handle touch corrext display.getCurrentStage( ):setFocus(event.target, event.id) play\_touch\_id = event.id elseif(event.phase == "moving") then print("test2") elseif(event.phase == "ended") then print("test3") display.getCurrentStage( ):setFocus( nil ) sfx.play(sfx.button\_click) composer.gotoScene(params.scene, {effect = "slideDown", time=250}) end return true end, { {status = 1, params = { scene = "data.endless"}}, {status = 2}, {status = 0}, {status = 0}, {status = 0}, {status = 0}, {status = 0}, {status = 0}, {status = 0}, {status = 0}, {status = 0}, {status = 0}, {status = 0}, {status = 0}, {status = 0}, {status = 0}, {status = 0} }, "images/level\_status.png", { left = 0, top = 0, width = 1080-128, height = 1080-128, horizontalScrollEnabled = false, verticalScrollEnabled = true, cellSize = 256, }, false) level\_select:initialize( ) level\_select.x, level\_select.y = display.contentCenterX, display.contentCenterY sceneGroup:insert(level\_select) end

I tried to move all stuff into the function directly at _cell:addEventListener():

 \_cell:addEventListener( "touch", function(event) print("listener") if(event.phase == "began") then display.getCurrentStage( ):setFocus( event.target , event.id ) print("began") elseif(event.phase == "moved") then print("moved") elseif(event.phase == "ended" or event.phase == "cancelled") then display.getCurrentStage( ):setFocus( nil , event.id ) print("ended") end end)

But my output then is (after ignoring the output from the scrollView listener): “listener, began”. So didn’t work either.

Greetings Dominik

PS: I’m passing three parameters to it because I’m calling it from another listener function passing the event and additional specific parameters. I tried to test out the logic directly in the listener function which I create in _cell:addEventListener, but this function is just called exactly once.

Did you try to return true at the end of the listener? The touch may be passing through to the scrollView, which then takes focus away from the _cell object.

Brent

Thank you, that was the problem, makes pretty much sense. Thank you very much!

Hi Dominik,

Can you post your code where you place (at least) one button, along with its listener addition and so forth?

Thanks,

Brent

Of course. I have did a little more testing and the touch listener is only called once: The first time clicking(touch begin) on the sprite. Then is never called again. Even if just clicked normal again. So a output looks like: “test0, test1, began, moved…, ended,began,moved…,ended” for clicking on the sprite. Moved may be printed 0 times also.

I’m to thank you for your effort.

The listener which i create in another module.

 function(event, self, params) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print("test0") if(event.phase == "began") then print("test1") -- handle touch corrext display.getCurrentStage( ):setFocus(event.target, event.id) play\_touch\_id = event.id elseif(event.phase == "moving") then print("test2") elseif(event.phase == "ended") then print("test3") display.getCurrentStage( ):setFocus( nil ) sfx.play(sfx.button\_click) composer.gotoScene(params.scene, {effect = "slideDown", time=250}) end return true end

The creation of the sprites.

[...] local cells = {} for \_y=1, line\_count do cells[\_y] = {} for \_x = 1, cell\_per\_line do if (\_y-1)\*cell\_per\_line+\_x \<= level\_count then cells[\_y][\_x] = display.newSprite( spriteSheet, sequenceData ) local \_cell = cells[\_y][\_x] class:insert( \_cell ) \_cell.x, \_cell.y = \_x \* cellSize, \_y \* cellSize local \_level = levelData[(\_y-1)\*cell\_per\_line+\_x] \_cell.level = \_level if \_level.status == 0 then \_cell:setFrame("1") elseif \_level.status == 1 then \_cell:setFrame("2") elseif \_level.status == 2 then \_cell:setFrame("3" ) end --add touch listener \<-- here the listener --\> \_cell:addEventListener( "touch", function(event) if \_cell.level.status ~= 0 then \_onTouch(event, \_cell, \_level.params) end end ) if debug then print("level\_select.lua::factory:new()-createLevelGrid()#DEBUG=\<Cell created\>#ID=\<"..tostring((\_y-1)\*cell\_per\_line+\_x).."\>".."#STATUS=\<"..tostring(\_level.status).."\>") end else if debug then print("level\_select.lua::factory:new()-createLevelGrid()#DEBUG=\<No cell because of level count\>") end end end end [...]

Hi Dominik,

What is the name of your listener function? It doesn’t appear that you’ve named it (which should throw an error). Also, you pass three parameters to it, but it accepts only one (event). There may also be an issue in your internal listener function and conditional logic… I suggest you try to work backwards and simplify this a bit, and test to see that you’re getting the proper responses. I know that’s not a great deal of help, but this is probably going to require some hands-on debugging.

Best regards,

Brent

Seems like I should post more of the code, as I’m not naming the function because I just pass it to my level_select module.

I added a print command to my event listener to see if that’s called properly, but also just on first began phase once called. Not after that. So something seems to be wrong at _cell:addEventListener(…), but I can’t figure out what.

local factory = {} --[[----------------------------- level\_data= { {level = "data.level0", title = "Tutorial", status = 1}, {level = "data.level1", title = "Level 1", status = 2}, {level = "data.level2", title = "Level 2", status = 0} } 0 = locked, 1 = unlocked, 2 = done spriteSheetImage = "images/level\_view.png" options = { left = , top = , width = , height = , horizontalScrollDisabled = , verticalScrollDisabled = , cellSize = , } --]]----------------------------- ------------------------------------------------------------ -------- ALWAYS SQUARE SO suitable for most purposes ------ ------------------------------------------------------------ function factory:new(\_onTouch,\_levelData, \_spriteSheetImage, \_options, \_debug) local debug = \_debug local widget = require("widget") if not \_options then print("level\_select.lua::factory:new()#MALFUNCTION=\<options is nil\>") end local options = \_options local class = widget.newScrollView( { left = options.left, top = options.top, width = options.width, height = options.height, horizontalScrollDisabled = not options.horizontalScrollEnabled, verticalScrollDisabled = not options.verticalScrollEnabled, hideScrollBar = true, listener = function(event) if event.phase == "began" then print("began") elseif event.phase == "moved" then print("moved") elseif event.phase == "ended" then print("ended") else print(event.phase) end return true end } ) local levelData = \_levelData local spriteSheetImage = \_spriteSheetImage local spriteSheet = graphics.newImageSheet( spriteSheetImage, { width = options.cellSize, height = options.cellSize, numFrames = 3, sheetContentWidth = options.cellSize \* 3, sheetContentHeight = options.cellSize } ) local sequenceData = { name="cell\_view", start = 1, count = 3} local function createLevelGrid() local level\_count = #levelData if(options.horizontalScrollEnabled and options.verticalScrollEnabled) then -- scroll uncontrollable in every direction UAAAH -- not elseif(options.verticalScrollEnabled and options.width) then -- vertical local maxSize = options.width local cellSize = options.cellSize local cell\_per\_line = math.floor(maxSize / cellSize) local line\_count = math.ceil(level\_count / cell\_per\_line) local cells = {} if debug then print("level\_select.lua::factory:new()-createLevelGrid()#DEBUG=\<\>#LEVEL\_COUNT=\<"..tostring(level\_count).."\>") end for \_y=1, line\_count do cells[\_y] = {} for \_x = 1, cell\_per\_line do if (\_y-1)\*cell\_per\_line+\_x \<= level\_count then cells[\_y][\_x] = display.newSprite( spriteSheet, sequenceData ) local \_cell = cells[\_y][\_x] class:insert( \_cell ) \_cell.x, \_cell.y = \_x \* cellSize, \_y \* cellSize local \_level = levelData[(\_y-1)\*cell\_per\_line+\_x] \_cell.level = \_level if \_level.status == 0 then \_cell:setFrame("1") elseif \_level.status == 1 then \_cell:setFrame("2") elseif \_level.status == 2 then \_cell:setFrame("3" ) end --add touch listener lol, set things \<-- print --\> \_cell:addEventListener( "touch", function(event) print("All right here") if \_cell.level.status ~= 0 then \_onTouch(event, \_cell, \_level.params) end end ) if debug then print("level\_select.lua::factory:new()-createLevelGrid()#DEBUG=\<Cell created\>#ID=\<"..tostring((\_y-1)\*cell\_per\_line+\_x).."\>".."#STATUS=\<"..tostring(\_level.status).."\>") end else if debug then print("level\_select.lua::factory:new()-createLevelGrid()#DEBUG=\<No cell because of level count\>") end end end end elseif(options.horizontalScrollEnabled and options.height) then -- horizontal local maxSize = options.height else print("level\_select.lua::factory:new():createLevelGrid#MALFUNCTION=\<No scroll enabled. Options may be malformatted.\>") end end local function updateLevelGrid() end function class:initialize( ) createLevelGrid() end function class:onDestroy() self:removeSelf( ) end class.x, class.y = \_x,\_y return class end return factory

And here is the scene from which I call the level_select module:

function scene:create( event, params ) local sceneGroup = self.view -- Initialize the scene here. -- Example: add display objects to "sceneGroup", add touch listeners, etc. level\_select = level\_select\_factory:new( function(event, self, params) print("test0") if(event.phase == "began") then print("test1") -- handle touch corrext display.getCurrentStage( ):setFocus(event.target, event.id) play\_touch\_id = event.id elseif(event.phase == "moving") then print("test2") elseif(event.phase == "ended") then print("test3") display.getCurrentStage( ):setFocus( nil ) sfx.play(sfx.button\_click) composer.gotoScene(params.scene, {effect = "slideDown", time=250}) end return true end, { {status = 1, params = { scene = "data.endless"}}, {status = 2}, {status = 0}, {status = 0}, {status = 0}, {status = 0}, {status = 0}, {status = 0}, {status = 0}, {status = 0}, {status = 0}, {status = 0}, {status = 0}, {status = 0}, {status = 0}, {status = 0}, {status = 0} }, "images/level\_status.png", { left = 0, top = 0, width = 1080-128, height = 1080-128, horizontalScrollEnabled = false, verticalScrollEnabled = true, cellSize = 256, }, false) level\_select:initialize( ) level\_select.x, level\_select.y = display.contentCenterX, display.contentCenterY sceneGroup:insert(level\_select) end

I tried to move all stuff into the function directly at _cell:addEventListener():

 \_cell:addEventListener( "touch", function(event) print("listener") if(event.phase == "began") then display.getCurrentStage( ):setFocus( event.target , event.id ) print("began") elseif(event.phase == "moved") then print("moved") elseif(event.phase == "ended" or event.phase == "cancelled") then display.getCurrentStage( ):setFocus( nil , event.id ) print("ended") end end)

But my output then is (after ignoring the output from the scrollView listener): “listener, began”. So didn’t work either.

Greetings Dominik

PS: I’m passing three parameters to it because I’m calling it from another listener function passing the event and additional specific parameters. I tried to test out the logic directly in the listener function which I create in _cell:addEventListener, but this function is just called exactly once.

Did you try to return true at the end of the listener? The touch may be passing through to the scrollView, which then takes focus away from the _cell object.

Brent

Thank you, that was the problem, makes pretty much sense. Thank you very much!