Ok. Will get back to you later tonight unless someone else beats me to it. Running out the door right now I’m afraid.
No worries!
For anyone watching this… ksan found that the line: ‘transition.to( group, {time = 1000, x = 0, onComplete = testSQL})’ was causing the Categories not sticking issue. Bug report posted. A big thank you to ksan for helping out with this.
Regards,
Trent
It was great fun tracking this one down. Making a cutdown version of your project so we can submit the bug report.
Bug # 25367 - transition.to causes tableView Category sticky at the top behavior to malfunction - logged. Thanks much discovering this one.
Some great code in there. I think your issue is in the for loop where you allocate the results of your sql select into a single variable set. OnRender first time works because it gets each row’s data one at a time through the variable set but when time comes to reRender due to moving up & down the for loop does not get all the records again. Here’s what I would do…
Create a table to hold all the records read from the sql statement.
Keep your current for loop but modify it to look somewhat like this :
local evacRecs = {} – watch out for scoping. Possibly needs to be at the top of your code.
local idx = 0 – counter used to insert rows into your table holding the evac records.
for b in evacdb:nrows(“SELECT * FROM Visitor ORDER BY PersonType, Status, GivenName, FamilyName”) do
idx = idx + 1
evacRecs[idx].sysID = b.SysID
evacRecs[idx].givenname = b.GivenName
evacRecs[idx].familyname = b.FamilyName
…
Then modify your onRowRender to look somewhat like this
if cd ~= nil then
rowCD = display.newText( row, evacRecs[row.index].cd, 0, 0, nil, 12 )
This should pull the right record out of your table for a given row (using row.index as index on your table).
Hope this helps.
Hi ksan,
Thank you very much, that got it working. I had tried putting the results into a table before but didn’t realise I needed to use row.index so I was getting the same results.
One last thing is that my Categories are still not sticking to the top of the window when scrolling. I’ve attached an image that shows this. The ‘Personnel’ Category should be showing at the top of the window in the image but it just scrolls away like a normal row. How can I get the Categories to stick?
Regards,
Trent
Glad to hear you got it working. The categories not sticking issue… I think there was a bug causing this at some point which was fixed somewhere along the way. Maybe its not in the most recent public build. Can’t recall. Did you try building with the open source version of the widgets library? See stickied post at the top of this sub-forum.
Great thank you ksan!
You’re most welcome. Hope that gets all working for you. Let us know how it goes! Best of luck.
Hmmm… the widgets sample works with the latest open source build, but in my app the Categories still don’t stick! I’ve tested that row.isCategory == true in the rowRender listener and the isCategory value is being set correctly.
Any ideas?
Regards,
Trent
I think I know what’s going on. Will try to review your code again but you can look for this as well. Does the second category appear at all? My guess is first category is drawn on screen and then flows out of view when its time to bring in the second category and lock it up at the top of the screen. Review your onRowRender to see how your code works when row.isCategory == true. Hope this helps.
Edit
You have the following in your ORR
if event.row.isCategory == true then
rowTitle.x = display.contentCenterX
rowTitle.y = row.contentHeight * 0.5
end --if
rowTitle:setTextColor( 0, 0, 0 )
print("rowtitle.x = " …rowTitle.x)
I think you need to pull the code that puts the category text on screen into that if then end box. Can’t see where that is. See if you can figure it out and if not please post an updated copy of your code.
Yes the second Cateogry does appear - image attached of how it looks when first loaded. Sorry I’m very new at Corona so I wouldn’t even know what to look for…
Regards,
Trent
EDIT - Didn;t realise you had posted just before - I’ll check my code with what you have suggested and will get back to you.
Sorry ksan, I’m not sure what to be looking for. Here is my updated code - really appreciate the help.
local widget = require( “widgetTX” )
local storyboard = require( “storyboard” )
local scene = storyboard.newScene()
– Our scene
function scene:createScene( event )
local group = self.view
local testSQLText = display.newText( “”, 0, 0, native.systemFont, 12)
local serverPath
local myResponse
local evacpath = system.pathForFile( “evaclist.sqlite”, system.TemporaryDirectory )
local evacTableView = nil
local evacRecs = {}
local idx = 0
----!!! Listeners !!!----
– Listen for evacTableView events
local function evacTableViewListener( event )
local phase = event.phase
print( “Event.phase is:”, event.phase )
end – function
– Handle row rendering
local function onRowRender( event )
local phase = event.phase
local row = event.row
rowTitle = display.newText( row, evacRecs[row.index].givenname…" "…evacRecs[row.index].familyname, 0, 0, nil, 16 )
if row.isCategory == true then
rowTitle.x = display.contentCenterX
rowTitle.y = row.contentHeight * 0.5
else
rowTitle.x = row.x - ( row.contentWidth * 0.5 ) + ( rowTitle.contentWidth * 0.5 ) + 20
rowTitle.y = row.contentHeight * 0.25
end --if
rowTitle:setTextColor( 0, 0, 0 )
print("rowtitle.x = " …rowTitle.x)
if evacRecs[row.index].cd ~= nil then
rowCD = display.newText( row, evacRecs[row.index].cd, 0, 0, nil, 12 )
rowCD.x = row.x - ( row.contentWidth * 0.5 ) + ( rowCD.contentWidth * 0.5 ) + 20
rowCD.y = rowTitle.y + 15
rowCD:setTextColor( 0, 0, 0 )
print("rowCD.x = " …rowCD.x)
end --if
if evacRecs[row.index].mobileno ~= nil then
rowMobile = display.newText( row, evacRecs[row.index].mobileno, 0, 0, nil, 12 )
rowMobile.x = row.x - ( row.contentWidth * 0.5 ) + ( rowMobile.contentWidth * 0.5 ) + 20
rowMobile.y = rowTitle.y + 30
rowMobile:setTextColor( 0, 0, 0 )
print("rowmobile.x = " …rowMobile.x)
end --if
end – function
– Handle touches on the row
local function onRowTouch( event )
local phase = event.phase
local row = event.target
if “press” == event.phase then
print( “Touched row:”, event.target.index )
end – if
end – function
– Handles row update
local function onRowUpdate( event )
local phase = event.phase
local row = event.row
end
local function networkEvacListener( event )
if ( event.isError ) then
testSQLText.text = “Network Error 2!”
elseif event.phase == “began” then
testSQLText.text = “Downloading…”
elseif event.phase == “ended” then
print(“Received file: evaclist.sqlite”)
testSQLText.text = “Download Complete!”
loadSQLList()
end --if
end – function
local function networkListener( event )
if ( event.isError ) then
testSQLText.text = “Network Error 1!”
else
myResponse = event.response
if tostring(myResponse) == “1” then
testSQLText.text = “Device not recognised.”
elseif tostring(myResponse) == “2” then
testSQLText.text = “Device not registered.”
else
testSQLText.text = “No errors received!”
print(myResponse)
local params = {}
params.progress = true
network.download( “http://”…serverPath…"/SendEvacuationList?name=" …myResponse, “GET”, networkEvacListener, params, “evaclist.sqlite”, system.TemporaryDirectory)
end – if
end – if
end – function
----!!! Functions !!!----
function backToMenu()
transition.to( group, { time = 1000, x = 0 - display.contentWidth, onComplete = function() storyboard.gotoScene( “menu” ); end })
end – function
function testSQL()
deviceID = system.getInfo( “deviceID” )
db = sqlite3.open( path )
for a in db:nrows(“SELECT * FROM settings”) do
serverPath = a.ServerPath
end – for
db:close()
if serverPath == nil then
testSQLText.text = “No Server URL found. Please enter the Server URL in Settings.”
else
testSQLText.text = “Connecting… please wait.”
end – if
testSQLText.x = display.contentCenterX
testSQLText.y = display.contentHeight - (testSQLText.contentHeight / 2)
testSQLText:setTextColor(0,0,0)
--delete these rows for production
deviceID = 1
serverPath = “localhost:85”
– do it or else
if serverPath ~= nil then
network.request( “http://”…serverPath…"/EvacuationListMobile?did=" …deviceID, “GET”, networkListener)
end – if
end
function loadSQLList()
evacdb = sqlite3.open( evacpath )
local function onSystemEvent( event )
if event.type == “applicationExit” then
evacdb:close()
end – if
end – function
for b in evacdb:nrows(“SELECT * FROM Visitor ORDER BY PersonType, Status, GivenName, FamilyName”) do
idx = idx + 1
evacRecs[idx] = {}
evacRecs[idx].sysID = b.SysID
evacRecs[idx].givenname = b.GivenName
evacRecs[idx].familyname = b.FamilyName
evacRecs[idx].cd = b.CD
evacRecs[idx].mobileno = b.MobileNo
evacRecs[idx].host = b.Host
evacRecs[idx].status = b.Status
evacRecs[idx].persontype = b.PersonType
local isCategory = false
local rowHeight = 75
local rowColor =
{
default = { 255, 255, 255 },
over = { 0, 174, 239 },
}
local lineColor = { 220, 220, 220 }
if (evacRecs[idx].givenname == “Visitors” and evacRecs[idx].familyname == “On Site”) or (evacRecs[idx].givenname == “Personnel” and evacRecs[idx].familyname == “On Site”) then
isCategory = true
rowHeight = 50
rowColor =
{
default = {212, 212, 212, 200},
}
end --if
evacTableView:insertRow
{
isCategory = isCategory,
rowHeight = rowHeight,
rowColor = rowColor,
lineColor = lineColor,
}
end – for
evacdb:close()
end – function
----!!! Controls !!!----
– add back button
local backButton = widget.newButton
{
left = 0,
top = 0,
width = 50,
height = 30,
default = { 255, 255, 255, 90 },
over = { 120, 53, 128, 255 },
fontSize = 12,
label = “Back”,
onRelease = backToMenu,
}
backButton.x = display.contentWidth - (backButton.contentWidth / 2)
evacTableView = widget.newTableView
{
top = 32,
width = 320,
height = 400,
listener = evacTableViewListener,
onRowRender = onRowRender,
onRowTouch = onRowTouch,
onRowUpdate = onRowUpdate,
}
group:insert(evacTableView)
group:insert(testSQLText)
group.x = display.contentWidth * 1.5
transition.to( group, {time = 1000, x = 0, onComplete = testSQL})
end – function
scene:addEventListener( “createScene” )
function scene:destroyScene( event )
print( “Called when scene is unloaded.” )
end
scene:addEventListener( “destroyScene” )
return scene
I think I’ve got it. I think your code is working as it is expected and I think your data is the problem. I might be totally off but I think the following tells me
if (evacRecs[idx].givenname == “Visitors” and evacRecs[idx].familyname == “On Site”) or (evacRecs[idx].givenname == “Personnel” and evacRecs[idx].familyname == “On Site”) then
that you are creating a fake record with givenname = “Visitors” and all records following this record are expected to be shown as Visitors until you have another fake record where givenname = “Personnel”. Is this right?
If my hunch is correct I’m afraid you need to fix your data. Each row of people should have a field where their type is stored (ie are they each a Visitor or Personnel). At first I thought your personType field would contain precisely this information but the more I look at your code the more I got suspicious of how you are determining the categories with the if then statement I copied above.
Let me know if I’m on the right track and we can discuss how you can fix this.
Regards,
Kerem
Yes that’s exactly right. I did it that way because I couldn’t think of another way at the time to figure out how to insert the Categories.
Would it better to use two sql tables - 1 for visitors (person type 1) and 1 for personnel (person type 2), then do a for loop on both tables?
Ok. We’re making progress. I’m happy to see we are getting to the bottom of this.
What info do you hold in the field called personType?
You definitely want one table. Basic database rule. Each type of data element should be in one table only to be differentiated with a field such as your personType. So all data about people should be in one table.
Ideally you would normalize the nature of person (ie Visitor or Personnel) and keep a personType code in your person table. Say 1 = Personnel and 2 = Visitor. In this setup you would keep your possible personTypes in a separate table where the keyfield would be the 1, 2 etc matching whats in your first table and then a personTypeText field would contain your long text (ie Visitor, Personnel etc)… The reason its done this way is to make each record conserve less memory or diskspace and also have faster queries. Select * from xyz where personType = 1 etc works much faster than Select * from xyz where personType = “Visitor” etc…
But in a mobile app with a small number of records you probably would be better off keeping the actual text in the main person table.
So to summarize, you want to use either your existing personType field or a new field to mark each person record with either Visitor or Personnel. You want to get rid of those fake records. Then you will need to revise your code a little to pick the changes in person type and call a specific insertRow with isCategory = true when you detect the category change. There is a trick there as you will most likely loose one record each time the category changes but we can discuss that next.
Now you have some homework! Have fun!
Ok no worries! PersonType is already set to either 1 or 2 depending, so it’s just figuring out how to insert the Category rows which is where I was stuck before doing it the above way.
What’s this trick you mention???
Regards,
Trent
Your data set needs to be ordered by your categories and I see that it is so thats good.
What you need is a little variable to track the current personType. You need to get rid of :
if (evacRecs[idx].givenname == “Visitors” and evacRecs[idx].familyname == “On Site”) or (evacRecs[idx].givenname == “Personnel” and evacRecs[idx].familyname == “On Site”) then
and something like the following there :
if thisPersonType ~= evacRecs[idx].persontype then – declare local thisPersonType somewhere up above.
thisPersonType ~= evacRecs[idx].persontype – This is just to keep track of current personType and call insertRow each time personType change.
--now insert the category
tableView:insertRow {
isCategory = true,
rowHeight = rowCategoryHeight,
rowColor = rowCategoryColor,
lineColor = lineColor,
}
end
this will go and insert the category. The trick is to call
tableView:insertRow {
isCategory = false,
rowHeight = rowHeight,
rowColor = rowColor,
lineColor = lineColor,
}
again while still in the same loop cycle so that the record itself also gets inserted into a new row in the tableView. Otherwise you will miss one person each time personType changes.
You have one more problem now. row.index used on your ORR will be out of sync with the data table since the tableView now has extra rows for the category lines. You can deal with this by maintaining another counter in the for loop that calls the insertRows. I’ll let you figure that one out as a Lua learning challenge!
Good luck. Have fun.
Ok cool, thank you ksan. I’ll let you know how I go
Regards,
Trent