我应该如何从另一个线程更新 GUI? RAD 工作室 10.4

问题描述

我一直在使用 RAD Studio 10.4 开发移动应用程序。我想在“Run”开始时创建并显示餐厅的菜单。我正在 FormCreate() 中尝试在另一个线程中执行以下操作:

  1. 发送 GET 请求
  2. 获取 XML 内容
  3. 根据 XML 内容制作列表
  4. 通过服务器上每张照片的路径从网站下载大约 30 张图像
  5. 图片添加到列表中

我不想让用户等待很长时间,所以我想在另一个线程中执行这些任务,以便先显示菜单,然后在下载食物图像时将它们添加菜单上。我几乎做到了,除了我经常看到一些带有空白或白线的图像

如何在菜单显示它们而不留空白?

代码如下:

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,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();
}

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)