1

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:

  1. Send a GET request
  2. Fetch an XML content
  3. Make a list from the XML content
  4. Downloading around 30 images from a web site via path of the each photo on the server
  5. 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();
}
Yasu
  • 13
  • 4
  • Minor suggestion to get help even faster: Place the code that does X near where you tak about X (e.g. `loadXML` directly below "2. Fetch an xml document"). Helps any answeres to more quickly see what relates to what. – Poohl Mar 19 '21 at 21:22
  • Thank you for your comment, @Poohl. I have arranged the tasks in the code. – Yasu Mar 20 '21 at 20:03

0 Answers0