I've been working on a mobile app with RAD Studio 10.4. I want to create and show a restaurant's menu at the beginning of "Run". I'm trying to do in the FormCreate() the following things in another thread:
- Send a GET request
- Fetch an XML content
- Make a list from the XML content
- Downloading around 30 images from a web site via path of the each photo on the server
- Add the images on the list
I don't want to have user wait for long time, so I want to do these tasks in another thread in order to show the menu first and then add the images of food on the menu as they are downloaded. I made it almost except that I have seen very often some images with a blank or white line on it.
How can I show them on the menu with no blank?
Here is the code:
void __fastcall TForm1::FormCreate(TObject *Sender)
{
_di_ITask task = TTask::Create([&](){
showMenu();
});
task->Start();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::showMenu()
{
try
{
String content = fetchMenu();
loadXML(content);
}
catch(...)
{
//ShowMessage("Something wrong is happening...");
}
vecImgStream = loadImages();
addImgToList(vecImgStream, vectItemOfListView, m_ListView);
attachItemClickListener();
isIndicatorVisible(false);
//delete the vector of pointers
for(TMemoryStream* pMStream : vecImgStream)
{
delete pMStream;
}
vecImgStream.clear();
}
//---------------------------------------------------------------------------
String __fastcall TForm1::fetchMenu()
{
String content;
try
{
AnsiString url = "https://www.test.com/API/MenuGET.php";
RESTClient1->BaseURL = url;
RESTRequest1->AddParameter("BannerID", "24");
RESTRequest1->AddParameter("SuccursaleID", "1");
RESTRequest1->AddParameter("Password", "password");
RESTRequest1->Method = TRESTRequestMethod(rmGET);
//GET
RESTRequest1->Execute();
//Retrieve the content
content = RESTResponse1->Content;
}
catch(std::exception&)
{
content = "An error occured!";
}
return content;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::loadXML(String content)
{
//creat main list with category name
_di_IXMLDocument menuXML = strToXML(content);
createMainList(menuXML);
}
//---------------------------------------------------------------------------
_di_IXMLDocument __fastcall TForm1::strToXML(String content)
{
//XML
XMLDocument1->Active = true;
_di_IXMLDocument xml = interface_cast<Xmlintf::IXMLDocument>(new TXMLDocument(NULL));
//Convert: String -> XML
xml->LoadFromXML(content);
return xml;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::createMainList(_di_IXMLDocument menuXML)
{
//Clear up the list
m_ListView->Items->Clear();
m_ListView->BeginUpdate();
//Position myself at <Menu>
Menu = menuXML->DocumentElement;
//Position myself at <Categories>
_di_IXMLNode Categories = Menu->ChildNodes->FindNode("Categories");
for (int categ = 0; categ < Categories->ChildNodes->Count; categ++)
{
//Position myself at each <Item>
_di_IXMLNode item = Categories->ChildNodes->GetNode(categ);
//Retrieve and show <NameFR>'s value under <Item>
AnsiString nameFR = item->ChildNodes->Nodes[WideString("NameFR")]->Text;
//Retrieve <Image>'s value (photo's path) under each <Item>
AnsiString imagePath = item->ChildNodes->Nodes[WideString("Image")]->Text;
//Bind the name and the photo's path of each item
//menuImage[nameFR] = imagePath;
int categNumber = (item->ChildNodes->GetNode("ID")->Text).ToInt();
mapCategory[nameFR] = categNumber;
TListViewItem* itemOfListView = m_ListView->Items->Add();
//Add the item's name to each row of the list
itemOfListView->Text = nameFR;
//*** Prep to download images later ***
vectItemOfListView.push_back(itemOfListView);
if(!imagePath.IsEmpty())
{
vectorPhotoPath.push_back(imagePath);
}
else
{
vectorPhotoPath.push_back("");
}
}
m_ListView->EndUpdate();
m_ListView->Enabled = true;
}
//---------------------------------------------------------------------------
std::vector<TMemoryStream*> __fastcall TForm1::loadImages()
{
/*************************
For the first time use:
1.Download images
2.Save them in cache
3.Add and show them onto the list
From the second time use:
1.Load images
2.Add and show them onto the list
**************************/
std::vector<TMemoryStream*> vecImgStream;
vecImgStream = loadImageFromFile();
if(vecImgStream.empty())
{
vecImgStream = getImageStreams();
saveImageToFile(vecImgStream);
}
else
{
//ShowMessage("Images found");
}
return vecImgStream;
}
//---------------------------------------------------------------------------
std::vector<TMemoryStream*> __fastcall TForm1::loadImageFromFile()
{
std::vector<TMemoryStream*> vectPhotoStream;
int counter = 0;
UnicodeString fileName;
do{
//fileName = file path + Integer value(between 0 and 33) +".jpeg"
//Ex. u"/data/user/0/com.embarcadero.Project1/files/0.jpeg"
fileName =
System::Ioutils::TPath::Combine(System::Ioutils::TPath::GetDocumentsPath(),
IntToStr(counter) + ".jpeg");
if(FileExists(fileName))
{
//text = "Image found";
vectPhotoStream.push_back(new TMemoryStream);
vectPhotoStream[counter]->LoadFromFile(fileName);
}
counter++;
}
while(FileExists(fileName));
return vectPhotoStream;
}
std::vector<TMemoryStream*> TForm1::getImageStreams()
{
std::vector<TMemoryStream*> vecImgStream;
vecImgStream = downloadImage(vectorPhotoPath);
return vecImgStream;
}
//---------------------------------------------------------------------------
std::vector<TMemoryStream*> __fastcall TForm1::downloadImage(std::vector<String> &vectorPhotoPath)
{
std::vector<TMemoryStream*> vecImgStream;
for(int i=0; i<vectorPhotoPath.size(); i++)
{
String imagePath = vectorPhotoPath[i];
if(!imagePath.IsEmpty())
{
//1. download an image and keep it as a stream
TMemoryStream* pStream = loadDefaultImage(imagePath);
//2. Put it into the vector
vecImgStream.push_back(pStream);
}
else
{
//Need to put something empty in the vector.
static char Buffer[2048];
memset(Buffer,0,2048);
sprintf(Buffer,"%s","");
int Len = strlen(Buffer);
TMemoryStream *pMyStream = new TMemoryStream();
pMyStream->Write(Buffer,Len);
//Put it into the vector
vecImgStream.push_back(pMyStream);
}
}
return vecImgStream;
}
//---------------------------------------------------------------------------
TMemoryStream* __fastcall TForm1::loadDefaultImage(String imagePath)
{
UnicodeString URLphoto = "http://www.test.com/" + imagePath;
TMemoryStream* photoStream;
THTTPClient* HTTPclient;
try {
photoStream = new TMemoryStream();
photoStream->Position = 0;
HTTPclient = THTTPClient::Create();
//GET request
HTTPclient->Get(URLphoto, photoStream);
}
catch (Exception & e)
{
ShowMessage (e.Message);
}
delete HTTPclient;
return photoStream;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::addImgToList(
std::vector<TMemoryStream*> &vecImgStream,
std::vector<TListViewItem*> &vectItemOfListView,
TListView* m_ListView
){
m_ListView->BeginUpdate();
for(int i=0; i < vecImgStream.size(); i++)
{
if(!vecImgStream[i]->Size == 0)
{
TMemoryStream* pStream = vecImgStream[i];
//put the photo onto the list
vectItemOfListView[i]->Bitmap->LoadFromStream(pStream);
}
}
m_ListView->EndUpdate();
}