Widget 2.0 Tableview Performance

Making progress. In my onRowRender I go for something like : 

        if row.id ~= nil and row.id ~= “” then

            downloadFile(row.id, downloadComplete)  

        end

                       

        print(" – row.id == ", row.id)

I think I’m stuck at the last step you described as … 

  • populateImage listener a) loads image as picture, and  B) rifles through tableview rows until it finds the one with the mathcing url and does an insert() into the _view for that row

Any chance to get a glimpse at that populateImage listener and your call out to downloadFile in the onRowRender() ?

So sorry to be such a pest. I appreciate all your help. Thank you very much. 

These are full of my app specific stuff – dependent on my data I get from the server for my list… I chopped out a little flotsam and jetsom here and there… Caveat Emptor my friend.

From my image populate listener…

[lua]

– imagePopulateListener(event)    -called back when the image for the row onscreen is downloaded…


 local function imagePopulateListener(event)

    print(" – imagePopulateListener(event)  event.response ==", event.response)
    
    – Let’s see if messageList even exists still, user could have left screen before server replied…
    if( (this ~= nil) and (event ~= nil)) then       – Dont do it if user has left the screen…
        if( this.messageList ~= nil )   then           – Don’t do it if there’s no list of messages somehow
–            print(" – rows == “,  this.messageList._view._categoryGroup._rows)
            local rows = this.messageList._view._rows
            
            local maxRows = #rows#this.messageList.content.rows    
            if( maxRows == nil ) then
                maxRows = 0
            end            
–            print (” – maxRows == ", maxRows)

            if( (maxRows > 0) and (event.url ~= nil) ) then        
–                print(" – messageList.view.content.rows[1] == “, this.messageList.content.rows[1] )
                
                local localFilename = utils.getBaseFilename(event.url)     
–                print(”  – localFilename == ", localFilename)  
                
                – Search for the right list item for this image…
                local rowIndex = 0

                for i=1, maxRows do
        –            print(" – row #, view == “, i, this.messageList.content.rows[i].view)            
–                    print(” – fileloc, imageloc == “, rows[i].FILELOCATION, rows[i].imageLocation)
                    
                    if( rows[i].id ~= nil ) then
–                        print(” – Found one with an image… “, rows[i].id)
                    
                        if( utils.getBaseFilename(rows[i].id) == localFilename ) then
–                            print(” ******** Found the Image **********")
                            rowIndex = i    – Found the right rows index.
                            local myImage = getMessageThumbnail(localFilename)   – Routine that just loads up the image (thumbnail scale)
                          
                            myImage.x = 590                            
                            myImage.y = (rows[rowIndex]._height / 2) - 1    – Center it for now

                            rows[rowIndex]._view:insert(myImage)         – Jam it into the item.                                                        
                            myImage.isVisible = true                            
                            
–                           print("  ******** Added image to row: “, localFilename)                                        
                           refreshWG()                                 – refresh the popups if need be                         
                           break
                        end        
                    end
                end
            else
                print(” *** NO ROWS / no url")
            end
        else
            print(" – messageList == nil")
        end
    else
        print(" – this == nil")
    end
 
[/lua]

And this is the bit of my onRowRender that calls out to get it going if there is an image for the row…

[lua]

        if( utils.isEmpty(currMess.FILELOCATION) == false ) then    – Yes, we have an image for this row
    
            – Let’s see if we have the file in our download cache already…
            local localFilename = utils.getBaseFilename(currMess.FILELOCATION)
            
            if( utils.cacheFileExists(localFilename) == true ) then         – do we have an already downloaded and ready to go image with this message?            
                local myImage = getMessageThumbnail(localFilename)
                
                myImage.x = 588
                myImage.y = math.floor(row.height / 2)     – Center it for now                                                
                myImage.isVisible = true
                row:insert(myImage)       – add it to the row.
                
–                print("  ******** Added image to row: ", localFilename)                    
            else                    
               
                – Let’s call for the the file to be downloaded (it could already be cached, or in process of download too)…
                utils.downloadFile(currMess.FILELOCATION, imagePopulateListener)
                
            end
        end

[/lua]

I’ve had to hack it up a little, but I think it’s all there for reference anyways.

Thank you so very much. Don’t worry about the floatsam. I will clean it up for my application. You’re absolutely right. This is all reference and is most appreciated. I will report back once I can get the images to show. Thanks much once again!!!

OK, apparently you can see the code I just posted… It shows just one line in my browser… Ah well.

But ti sounds like you got it. There’s a bazillion print statements in there, make sure you use them to wade through to the goal line, good luck!

I see the code all messed up but I need to wade through it and clean it up for my application anyways so no worries about the format. 

Progress so far.

I get the first file downloaded and then when its existing I get a stack traceback on the 

pendingDownloadsevent.url

the message tells me I have : 

attempt to call field ‘?’ (a nil value)

Anyways, let me wade through the code above and see if I can make sense of it. 

Thanks much.

OK, the line of code you referenced it crashed on is filled in by the downloadFile() by the line:

pendingDownloads[fileLocation] = completionListener

It is setting an element in the table (pendingDownloads table) to the function pointer to call back. The key to the table (fileLocation) is the url… You should just check around these two lines of code to make sure the key matches. That would be my guess. No key match, no function pointer to call back.

@mpappas… you know what I’m going to ask next right… if you give a mouse a cookie… next he will ask for 

local myImage = getMessageThumbnail(localFilename)   :) 

Can I see inside that getMessageThumbNail() function?

I promise this will be the last thing I ask. I am so grateful for your help. Thank you so very very much!!!

[lua]

– getMessageThumbnail() – returns a thumbnail image for use on message screen.

local function getMessageThumbnail(localFilename)

local myImage = display.newImage( localFilename, system.TemporaryDirectory, 0, 0 )

    if( myImage ~= nil ) then
        myImage.width = 88               – Thumbnail size… It can be a little cropped if message is short… is ok…
        myImage.height = 132
        myImage.isVisible = false       – Lets make sure it doesn’t pop onscreen somewhere for now…
        myImage:removeSelf()           – Remove it from stage for now
    else
        print(" – error getting thumbnail - ", localFilename)
    end
    
    return myImage
end

[/lua]

Wohooo!!! I am sooo happy. Thank you so so very much!!!

My images download well without choking the tableview. 

If they are there when I bring the tableView up then they display well. 

If they were not there when I brought the tableView up they download nicely in the background. 

If I scroll down and then come back then the images show. Magic!!!

The only thing thats not working is for the image to show at the end of the download if I don’t move the tableView.

I think I need to fix the imagePopulateListener() code and I’ll be running all the way!!!

Thank you so very much. I could not have done this on my own ever!

Well done, congrats :slight_smile:

With all the lazy loading, caching and all going on - scrolling through potentially thousands of images (but only having the minimum handful of images in texture memory) in a list from a remote server… this tableView stuff scrolls pretty @#$%@#$ good, if ya ask me :slight_smile:

My caching idea and all is useful, but the lazy loading from the tableview is the real magic IMHO, without it memory would overflow from all the images being loaded in the list at once, so we’d have to write all our own lazy loading machinery. Instead, it’s built in, woohoo!

Regarding the first download not populating / being visible – I had some issues with making sure images didn’t populate *after* they scrolled out of view at first, and maybe something if off by default in there, But mine populate when downloaded without moving, just a matter of tracking it down I think. 

Its all your doing. I just “painted by numbers” and almost got Mona Lisa but not 100% yet!!!  :slight_smile:

I agree. the lazyloading is wonderful. And your event driven approach to getting the images to download and load when ready is a best in class implementation thats for sure. Thanks much for sharing this freely. 

I think I know the issue why my images are not downloading on first pass after completing the downloads. Its because I did not yet fix whats inside imagePopulateListener() where you look for a row containing the right file name etc… 

I am a little confused by the references to “this” in that function. I understand what it is doing but not sure how to use it yet. Searching CL docs for “this” is proving impossible. I’m referring to calls like the following : 

local maxRows = #rows – # this.messageList.content.rows

Almost there!!!

ah, the “this”. Thats my overall displaygroup for the entire screen.

this.messageList is where I keep my pointer to the tableView around / handy. I check “this” a lot at the beginning of functions (if this ~= nil) in case the user exits (and app destroys the screen), but a delayed call returns… Big no-no to add images when the screen (this) was destroyed.

So when I create the tableView, I insert it into the screen (this:insert(messageList), and I also set a pointer to it for use later (this.messageList = messageList). Kinda do that with a lot of graphic objects I might want to “throttle” a little bit later.

I think I get it!

So this is like “group” in the StoryBoard defaults from CL  i.e.

   local group = self.view

which you see in every storyBoard function. 

I see and understand what you do there. I just need to make it work for my scene. Almost there. 

Thank you so very much!!!

It works!!! I am so happy!!!

I spent a while adapting the insides of imagePopulateListener() and thought all was ok but it was still not working. imagePopulateListener() was running a about quarter of its code and then stopping.

I literally resorting to putting numbered print(“1”), print(“2”) etc after every line of code to see where the code is choking and guess what… I think I came across something funny in CL SDK. Might be even a bug. Not sure…

lines with the following reference 

     print(" – row #, view == ", i, tableView.content.rows[i].view)   

make code execution stop without any Corona errors. I did not see the rest of print()s I planted in there so I rem’med those suckers and guess what… it worked… so whatever was happening there it all goes away once you stop using 

tableView.content.rows[i].view

I am so very happy. Thank you very very much once again. I owe you big time. 

wtg ksan! w00t!!

@mpappas, Would it be ok to ask if you could share some code from your project? I’m trying to do similar things and not getting far. Thanks soooo much in advance if you can. 

Apologies for the off-topic request. 

In build 1135 anyways, I set the id on initialization of the row:

[lua]

        – Pre-calc row height in the loop inserting initial rows… then…

        this.messageList:insertRow{
                rowHeight=rowHeight,
                isCategory=isCategory,
                lineColor={96,96,96,255}, – myLineColor,
                rowColor={ default=finalRowColor, over={128,128,128,128} },
                id=tostring(idString),        – remote filename of thumbnail file (full url)
        }
[/lua]

Then later I read it:

[lua]

local function onRowRender( event )
    local row = event.row

    print(" – row.id == ", row.id)

end

[/lua]

Not much to it, my implementation anyways… In my case, I already know the content of the rows when I init the tableview (server responded with the list of data, so I know if there’s an image for each row at list creation time). You could probably stuff a table in the id field, dunno though, never tried.

Thank you so much! So you give the URL of the image to the tableView and it gets displayed how? Do you mean to say you download the images at each run time again & again or do you first download them to your local file system elsewhere? From your comment I think you are downloading them at runtime. 

There’s a lot of my apps code, so I can’t post it all, and trying to snip pieces and post something would be pretty error prone (not to mention take a while), but the overall process in my case is something like:

  • Request chat/blog list from server

  • Receive all the posts/messages

  • Init tableView

  • Create rows (storing any image url in the .id field)

  • onRowRender gets called for the half a dozen or so rows on screen

  • onRowRender checks to see if the filename in the id field (stripped of web url) exists in the system/temp folder

  • if file not exists in cache already, send request to server to download, otherwise, just display it from cache

  • custom listener receives callback when file downloads, calls back a special populateImage listener (Listener keeps table of callers/filenames from event.url returned from server call). File is stored in system/temp folder (cache)

  • populateImage listener a) loads image as picture, and B) rifles through tableview rows until it finds the one with the mathcing url and does an insert() into the _view for that row

Whew! It’s just that simple!

That sounds great! Its much clearer now. Sounds like almost exactly what I need to be doing. I’m simply trying to load some logo images for exhibitors in a festival but the concept is the same. Thanks much for your guidance.