Asynchronous image loading - help needed

Hi,

I have an application that downloads images synchronously, but the initial loading time is too long, so I am trying to load some images asynchronously, and display them inside a Lua tableView. Is this possible and a good approach?

I am using network.download() like this:

function ImgnetworkListener( event )  
 if ( event.isError ) then  
 print("Network error: image download failed.")  
 else  
 local myImage = display.newImage(event.response, system.DocumentsDirectory, 0, 0)  
 print("Downloading product image: " .. event.response)  
 end  
end  
  
network.download(  
 "http://www.mysite.com/images/" .. jSon\_result.data.image1[i],  
 "GET",  
 ImgnetworkListener,  
 jSon\_result.data.image1[i],  
 system.DocumentsDirectory  
)  

I request all of the images at the beginning in a loop (my data is in a JSON-formatted file, which I read in to an array and de-serialise).

I can see all of the images in the ~/Documents directory, so the download has worked. The problem I am having is with is the callback, and knowing how to update the images in each tableView row, once an image has been downloaded.

An image placeholder is loaded initially so that the user can see an image will be loading:

myList = tableView.newList {  
 data=data,   
 default="listItemBg.png",  
 over="listItemBg\_over.png",  
 onRelease=listButtonRelease,  
 top=topBoundary,  
 bottom=bottomBoundary,  
 callback = function(row)  
  
 local g = display.newGroup()  
 local img = display.newImage("loading.jpg", system.DocumentsDirectory, 0, 0)  
 g:insert(img)  
 img.x = 10  
 img.y = 10  
 ...  
 etc  
 ...  
 end  
}  

How can I reference the row in the tableView and push the newly downloaded image into it, replacing the placeholder image? Or is their a better way of tackling this?

I think I have to loop over the tableView and update the image, but the exact syntax eludes me.

Thanks for any pointers. [import]uid: 116752 topic_id: 20267 reply_id: 320267[/import]

I have finished my newbie asynchronous image loader, and the code in its entirety is shown below for anyone who’s interested. This is an amateur attempt, so I’m not sure if this code is fully optimised etc.

--  
-- Asynchronous image loader into a widget.tableView with 'image loading' placeholder and detection of images already cached  
--  
local http = require("socket.http")  
local json = require("json")  
local ui = require("ui")  
local widget = require("widget")  
  
local isCategory = false  
local rowHeight = 80  
local rowColor = { 255, 255, 255, 255 }  
local lineColor = {0,0,0,255}  
   
-- Get the products from TDS (JSON formatted):  
response\_body\_JSon = http.request("http://www.mysite.com/somescript.asp")  
jSon\_result = json.decode(response\_body\_JSon)  
  
local listOptions = {  
 top = display.statusBarHeight,  
 height = 450,  
 maskFile = "mask320x410.png"  
}  
   
local list = widget.newTableView( listOptions )  
  
-- onEvent listener for the tableView  
local function onRowTouch( event )  
 -- whatever, swipes etc  
end  
  
local function file\_exists( name )   
 local path = system.pathForFile( name, system.DocumentsDirectory )  
 local f=io.open(path,"r")   
 if f~=nil then   
 io.close(f)   
 return true   
 else   
  
 return false   
 end   
end   
   
-- onRender listener for the tableView  
local function onRowRender( event )  
 local row = event.target  
 local rowGroup = event.view  
  
 -- The next line is critical, without it the images still arriving will not render immediately  
 row.reRender = true   
  
 if file\_exists( jSon\_result.data.image1[event.index] ) then  
 local img = display.newImage( jSon\_result.data.image1[event.index], system.DocumentsDirectory, 0, 0 )  
 img:setReferencePoint( display.CenterLeftReferencePoint )  
 img.y = row.height \* 0.5  
 img.x = 15  
 img:scale(0.4, 0.4)  
 rowGroup:insert( img )  
 else  
 -- The image has not yet downloaded, or was not found so show a placeholder for now:  
 local img = display.newImage( "loader.gif", system.ResourcesDirectory, 0, 0 )  
 img:setReferencePoint( display.CenterLeftReferencePoint )  
 img.y = row.height \* 0.5  
 img.x = 15  
 img:scale(0.4, 0.4)  
 rowGroup:insert( img )  
 print("Downloading image: " .. jSon\_result.data.image1[event.index] )  
 end  
  
 local text = display.newRetinaText( jSon\_result.data.name[event.index], 200, 20, "Georgia", 9 )  
 text:setReferencePoint( display.CenterLeftReferencePoint )  
 text.y = row.height \* 0.5  
 text.x = 80  
 text:setTextColor( 0 )  
 rowGroup:insert( text )  
end  
  
local function img\_networkListener( event )  
 if (event.isError) then  
 print("Error loading image: " .. event.response)  
 else  
 -- Image has downloaded, so show it:  
 local rowHeight, rowColor, lineColor, isCategory  
 list:insertRow {  
 onEvent=onRowTouch,  
 onRender=onRowRender,  
 height=rowHeight,  
 isCategory=isCategory,  
 rowColor=rowColor,  
 lineColor=lineColor  
 }  
 end  
end  
   
-- Download the images, and load table data  
for i=1,jSon\_result.recordcount do  
 -- Get the images:  
 if file\_exists( jSon\_result.data.image1[i] ) then  
 -- This file is already cached, so just display the row:  
 print(jSon\_result.data.image1[i] .. " already downloaded")  
 local rowHeight, rowColor, lineColor, isCategory  
 list:insertRow {  
 onEvent=onRowTouch,  
 onRender=onRowRender,  
 height=rowHeight,  
 isCategory=isCategory,  
 rowColor=rowColor,  
 lineColor=lineColor  
 }  
 else  
 network.download(   
 "http://www.mysite.com/images/" .. jSon\_result.data.image1[i],  
 "GET",   
 img\_networkListener,   
 jSon\_result.data.image1[i],   
 system.DocumentsDirectory  
 )  
 end  
end  
  
local function refreshRows()  
 for x=1,jSon\_result.recordcount do  
 list.content.rows[x].reRender = true  
 local pos = list:getScrollPosition()+400  
 end  
end  

This code will detect if an image is already cached (in the system.DocumentsDirectory folder) and will not download the image again - this helps speed things up. A placeholder in the system.ResourcesDirectory (loader.gif) loads if an image is not available; this is later replaced by the image itself. If you’re quick enough (or have a slow server) you can see the transition from loading to loaded.

The row.reRender = true is the one bit of crucial code that allows the transition from loading to loaded to occur; without it the images that arrive later do not update (however they will if you click or scroll).

This code loads a JSON data source, which looks like this (one row of data shown here):

{"recordcount":1,"columnlist":"firstname,image1,lastname,name,orderdt,orderstatus\_id,order\_id,unitprice","data":{"firstname":["John"],"image1":["image1.jpg"],"lastname":["Smith"],"name":["Whatever product name"],"orderdt":["Jan 12 2012 7:44PM"],"orderstatus\_id":[1],"order\_id":[123],"unitprice":["$100.00"]}}   

[import]uid: 116752 topic_id: 20267 reply_id: 79503[/import]

Hi! thanks for your code!!
Please tell me when you call refreshRows function?
thanks very much! [import]uid: 10056 topic_id: 20267 reply_id: 83256[/import]

Sorry, my bad. That function was not required in the end, and it can be removed. In fact, the existing line:

row.reRender = true

does the trick. Without that line, I find that the rows do not render as data is loaded asynchronously.

Corona does not seem able to load animated GIFs either (the placeholder image is a animated ajax-style loading image). [import]uid: 116752 topic_id: 20267 reply_id: 83310[/import]