More tableview woes:

I have been having a lot of trouble getting my tableview to work, Below is a short code sequence that exhibits a couple of problems. The expected behaviour is:

  1. The user hits the button, is presented with a textbox 

  2. On entering the text (and hitting return), the text appears in the tableview table and the textbox is dismissed

  3. The user repeats the process

  4. if the number of rows entered overflows the visible table, it scrolls to make the row visible.

Actual behaviour:

  1. The native text box sometimes not dismissed. This is worrying since it indicates a bug somewhere and may account for other problems that I have been having, but not illustrated by this example.

  2. The 4th row to be entered is not visible, but the function getRowAtIndex insist that it is. Not until 6 rows have been entered does the expected scrolling appear.

Is there a problem in calling nested event handlers (e.g. onRowRender from inside the nativeTextField handler from inside the button handler)?

[lua]


– main.lua


widget = require “widget”

_CW = display.contentWidth

_CH = display.contentHeight

local rowHeight = 60

local searchTable

----------------- function onRowRender ------------------

local function onRowRender (event)

local row = event.row

print("About to render row, index = ",row.index)

local rowText = display.newText(row, row.params.Name, 0, 0, nil, 14)

rowText:setFillColor( 0, 0, 0 )

rowText.x = _CW/2

rowText.y = rowHeight/2

return true

end – end onRowRender

local rowAtIndex

local numRows

local searchListener = function(event)

if (event.phase == “submitted”) then

searchText = event.target.text

searchTable:insertRow( {

rowHeight=rowHeight, params = {Name = event.target.text}

} )

– is last row visible, if not scroll to it

numRows = searchTable:getNumRows()

rowAtIndex = searchTable:getRowAtIndex(numRows)

if not rowAtIndex then

print(“entry off screen”)

searchTable:scrollToIndex(numRows)

else

print(“entry visible”)

end

native.setKeyboardFocus(nil)

event.target:removeSelf()

end – if submitted

return true

end – searchListener

-------------------------- button release listener -------------------

local addButtonRelease = function(event)

local searchField = native.newTextField(

_CW/2, _CH/2, _CW*0.8, 30)

searchField.font = native.newFont(nil,14)

searchField.placeholder = “enter name to search for”

native.setKeyboardFocus(searchField)

searchField:addEventListener(“userInput”, searchListener)

end – addButtonRelease

------------------------ generic button params ---------------

buttonParams = {

onRelease = addButtonRelease,

shape = “roundedRect”,

width = 100,

height = 30,

cornerRadius = 5,

fillColor = { default={1,1,0,1}, over={1,0.1,0.7,0.4} },

strokeColor = { default={1,0.4,0,1}, over={0.8,0.8,1,1} },

strokeWidth = 0,

fontSize = 10,

}

---------------------- add search button -------------------

local addButton = widget.newButton(buttonParams)

addButton.id = “addButton”

addButton.y = _CH - 10

addButton:setLabel( “add to search” )

------------ create table of search requests --------------

searchTable = widget.newTableView {

top = 30 ,

height = _CH/3,

onRowRender = onRowRender,

backgroundColor = {1, 1, 1, 0.5}

}

[/lua]

Your code is hard to read but I think I know what’s going on. You’re making an assumption that if a row isn’t on the screen that it doesn’t exist. I think we even render a row or two off screen for performance reasons. You can still scroll to index, but you need to keep track of the number of rows and just scroll to the last one you believe should be there.

Rob

Thanks for the reply, but I haven’t made myself clear. I know that if I insert more rows than the tableview widget can display they do exist (and using the debugger, I do note that rows get a call to onRowRender even when offscreen). What the code was supposed to do was to add rows to the bottom of the table until a row went outside the visible window. I was assuming that when that happened getRowAtIndex would return nil, and I would then scroll to that row, but that appears not to happen.

​Actually, its not clear what scrolling to a row should mean - should the row then appear at the top, middle or bottom of the view?

​BTW, Any tips on the coding style since you observed that it was hard to read?

Tips on code formatting: https://coronalabs.com/blog/2015/06/09/tutorial-the-value-of-well-formatted-code/

as for what you’re trying to accomplish, it looks like you’re trying to simulate a message window where the latest message comes in at the bottom and older messages are scrolled off the screen but the user can scroll back to see older messages.

While you can make a tableView do this, perhaps a better solution is to just use a scrollView and keep tacking the message on the end

Rob

Rob,

thanks again for the reply. You are correct win interpreting my aim. However. what I really wanted to do was to have a stack-like list of inputs with the latest at the top - I just couldn’t figure out how to do that with the available tableView calls. What I didn’t want was the latest input to disappear out of view!

I now keep a shadow table of inputs from textfield and insert each one at the top of the shadow table and then cycle through each entry in this shadow table writing the entry to the tableView. Since I am waiting on user input and there are only going to be a small number of entries anticipated, the inefficiency in this method can be disregarded.

I do think however there is a bug in getRowAtIndex in that rows close the the edge of the tableView display are reported as visible when they are (plainly!) not. This might be due to the offscreen rendering that you mentioned that you do for efficiency reasons.

PS. I have just realised why you complained about the readability of the code. For some reason, all the tabbed indentations have been removed. Next time I post code,  I will check to see that it hasn’t lost its formatting

I’m not sure a tableView is the right thing for you. I think a scrollView will work better for you. Just keep adding  your display object above the last one inserted (you will have things positioned with negative numbers but that’s okay) and just scroll the scrollView to its top.

Perhaps:

local latestText = display.newText(....) table.insert( myStack, 1, latestText ) -- prepend an open entry in the stack table. latestText.y = myStack[2].y - latestText.height - padding scrollView:insert( latestText) scrollView:scrollTo("top")

or something like that.

Cheers, I never thought about using a scrollView object. I misunderstood their functionality.

Your code is hard to read but I think I know what’s going on. You’re making an assumption that if a row isn’t on the screen that it doesn’t exist. I think we even render a row or two off screen for performance reasons. You can still scroll to index, but you need to keep track of the number of rows and just scroll to the last one you believe should be there.

Rob

Thanks for the reply, but I haven’t made myself clear. I know that if I insert more rows than the tableview widget can display they do exist (and using the debugger, I do note that rows get a call to onRowRender even when offscreen). What the code was supposed to do was to add rows to the bottom of the table until a row went outside the visible window. I was assuming that when that happened getRowAtIndex would return nil, and I would then scroll to that row, but that appears not to happen.

​Actually, its not clear what scrolling to a row should mean - should the row then appear at the top, middle or bottom of the view?

​BTW, Any tips on the coding style since you observed that it was hard to read?

Tips on code formatting: https://coronalabs.com/blog/2015/06/09/tutorial-the-value-of-well-formatted-code/

as for what you’re trying to accomplish, it looks like you’re trying to simulate a message window where the latest message comes in at the bottom and older messages are scrolled off the screen but the user can scroll back to see older messages.

While you can make a tableView do this, perhaps a better solution is to just use a scrollView and keep tacking the message on the end

Rob

Rob,

thanks again for the reply. You are correct win interpreting my aim. However. what I really wanted to do was to have a stack-like list of inputs with the latest at the top - I just couldn’t figure out how to do that with the available tableView calls. What I didn’t want was the latest input to disappear out of view!

I now keep a shadow table of inputs from textfield and insert each one at the top of the shadow table and then cycle through each entry in this shadow table writing the entry to the tableView. Since I am waiting on user input and there are only going to be a small number of entries anticipated, the inefficiency in this method can be disregarded.

I do think however there is a bug in getRowAtIndex in that rows close the the edge of the tableView display are reported as visible when they are (plainly!) not. This might be due to the offscreen rendering that you mentioned that you do for efficiency reasons.

PS. I have just realised why you complained about the readability of the code. For some reason, all the tabbed indentations have been removed. Next time I post code,  I will check to see that it hasn’t lost its formatting

I’m not sure a tableView is the right thing for you. I think a scrollView will work better for you. Just keep adding  your display object above the last one inserted (you will have things positioned with negative numbers but that’s okay) and just scroll the scrollView to its top.

Perhaps:

local latestText = display.newText(....) table.insert( myStack, 1, latestText ) -- prepend an open entry in the stack table. latestText.y = myStack[2].y - latestText.height - padding scrollView:insert( latestText) scrollView:scrollTo("top")

or something like that.

Cheers, I never thought about using a scrollView object. I misunderstood their functionality.