[ListView]load image

This is the forum for miscellaneous technical/programming questions.

Moderator: 2ffat

[ListView]load image

Postby Lena » Tue Oct 17, 2017 11:22 am

Hi.
Code: Select all
//cycle fragment
TBitmap * PictureFood = new TBitmap();
#ifdef __ANDROID__
String path = System::Ioutils::TPath::Combine(System::Ioutils::TPath::GetSharedDownloadsPath(), image);
#elif _Windows
String path = System::Ioutils::TPath::Combine(GetCurrentDir(), image); //image = 1.jpg, 2.jpg...
#endif
PictureFood->LoadFromFile(path);
//MyImage the name of the object in ListView
reinterpret_cast<TListItemImage *>(item->Objects->FindDrawable(L"MyImage"))->Bitmap = PictureFood;


How correctly to load from an external file a picture in ListView (DynamicAppearance)? The code above shows the downloaded pictures in Windows, everything is fine. But in the android instead of the picture is emptiness. :(
Lena
BCBJ Master
BCBJ Master
 
Posts: 525
Joined: Sun Feb 06, 2011 1:28 pm

Re: [ListView]load image

Postby rlebeau » Tue Oct 17, 2017 6:01 pm

Lena wrote:The code above shows the downloaded pictures in Windows, everything is fine.


Not quite. You shouldn't be relying on GetCurrentDir() to find the files. A process's CWD is dynamic and can/does change value during the process's lifetime. If the files are located in the same folder as the app, use ExtractFilePath(ParamStr(0)) instead.

Lena wrote:But in the android instead of the picture is emptiness. :(


Did you verify that "path" is correct to begin with, and that LoadFromFile() is successful? FireMonkey is not very good about throwing exceptions on file load errors. Have you tried saving the loaded TBitmap to another file to check the image?
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1457
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: [ListView]load image

Postby Lena » Wed Oct 18, 2017 2:43 am

Did you verify that "path" is correct to begin with, and that LoadFromFile() is successful?


I checked. It's all right. No message ShowMessage(L"No file");
1. Copy 1.jpg 2.jpg 3.jpg 4.jpg in Android folder Dowloads
2. Copy dibocca.ini in Android folder Dowloads.
dibocca.ini:
Code: Select all
[1]
name=Антипасто Итальяно
price=275
text=Антипасто Итальяно ....................... 280 г .......... 275 грн.
footer=(ассорти из прошутто, салями, сыра Дор Блу, сыра Пармезан, оливок, томатов и соуса Бальзамик)
image=1.jpg

[2]
name=Капрезе с соусом Песто
price=115
text=Капрезе с соусом Песто ................ 300 г .......... 115 грн.
footer=(сыр Моцарела, свежие томаты, оливковое масло, соус Песто)
image=2.jpg

[3]
name=Сырная палитра
price= 275
text=Сырная палитра ................................ 280 г .......... 275 грн.
footer=(ассорти из сыров Италии, мед/джем, финики, виноград, орехи)
image=3.jpg

[4]
name=Тартар из телятины
price=110
text=Тартар из телятины ......................... 260 г .......... 110 грн.
footer=(рубленое мясо, корнишоны, гренки, каперсы, желток)
image=4.jpg


3. Code:
Code: Select all
void __fastcall TForm1::FormShow(TObject *Sender)
{
   #ifdef __ANDROID__
   String path = System::Ioutils::TPath::Combine(System::Ioutils::TPath::GetSharedDownloadsPath(), L"dibocca.ini");
   #elif _Windows
   String path = System::Ioutils::TPath::Combine(GetCurrentDir(), L"dibocca.ini");
   #endif

   if(FileExists(path))
    {
      std::unique_ptr<TIniFile> FileINI(new TIniFile(path));
      std::unique_ptr<TStringList> AlliniSection(new TStringList());
      FileINI->ReadSections(AlliniSection.get());

      String text = "";
      String footer = "";
      String image = "";
      String name = "";
      String price = "";


       for (int i = 0; i < AlliniSection->Count; i++)
       {
         text = FileINI->ReadString(AlliniSection->Strings[i], L"text", "");
         footer = FileINI->ReadString(AlliniSection->Strings[i], L"footer", "");
         name = FileINI->ReadString(AlliniSection->Strings[i], L"name", "");
         price = FileINI->ReadString(AlliniSection->Strings[i], L"price", "");
         image = FileINI->ReadString(AlliniSection->Strings[i], L"image", "");

         TListViewItem * item = ListView1->Items->Add();
         item->Data[L"FullName"] = TValue::From<UnicodeString>(text);
         item->Data[L"FotterName"] = TValue::From<UnicodeString>(footer);
         item->Data[L"TextCount"] = TValue::From<UnicodeString>(L"1");
         item->Data[L"name"] = TValue::From<UnicodeString>(name);
         item->Data[L"price"] = TValue::From<UnicodeString>(price);

         TBitmap * PictureFood = new TBitmap();
         #ifdef __ANDROID__
         String path = System::Ioutils::TPath::Combine(System::Ioutils::TPath::GetSharedDownloadsPath(), image);
         #elif _Windows
         String path = System::Ioutils::TPath::Combine(GetCurrentDir(), image);
         #endif
         if(FileExists(path))
         {
          PictureFood->LoadFromFile(path);
          reinterpret_cast<TListItemImage *>(item->Objects->FindDrawable(L"MyImage"))->Bitmap = PictureFood;
         }
         else
            {
             ShowMessage(L"No file");
            }

         item->Data[L"min"] = TValue::From<int>(0);
         item->Data[L"add"] = TValue::From<int>(1);

       }

    }
    else
      {
        ShowMessage(L"No file dibocca.ini");
        }
}
Attachments
winand.jpg
winand.jpg (31.13 KiB) Viewed 885 times
Last edited by Lena on Wed Oct 18, 2017 2:55 am, edited 2 times in total.
Lena
BCBJ Master
BCBJ Master
 
Posts: 525
Joined: Sun Feb 06, 2011 1:28 pm

Re: [ListView]load image

Postby Lena » Wed Oct 18, 2017 2:51 am

Have you tried saving the loaded TBitmap to another file to check the image?

Such an attempt:
Image1 on Form have name ImageRAD (visible=false).
Code: Select all
//***
if(FileExists(path))
         {
          PictureFood->LoadFromFile(path);
          ImageRAD->Bitmap = NULL;
          ImageRAD->Bitmap = PictureFood;
          reinterpret_cast<TListItemImage *>(item->Objects->FindDrawable(L"MyImage"))->Bitmap = ImageRAD->Bitmap;
         }
         else
            {
             ShowMessage(L"No file");
            }

Only one picture 4.jpg is loaded in all rows. :(
Attachments
one.jpg
one.jpg (23.71 KiB) Viewed 884 times
Lena
BCBJ Master
BCBJ Master
 
Posts: 525
Joined: Sun Feb 06, 2011 1:28 pm

Re: [ListView]load image

Postby rlebeau » Wed Oct 18, 2017 10:23 am

Lena wrote:I checked. It's all right. No message ShowMessage(L"No file");


Why are you checking for a file's existence before trying to load it? Just load it, and let it fail if the file can't be opened.

Lena wrote:
Code: Select all
String path = System::Ioutils::TPath::Combine(GetCurrentDir(), L"dibocca.ini");
...
String path = System::Ioutils::TPath::Combine(GetCurrentDir(), image);



You are still using the wrong folder path on Windows. DO NOT use GetCurrentDir()!

Lena wrote:
Code: Select all
reinterpret_cast<TListItemImage *>(item->Objects->FindDrawable(L"MyImage"))->Bitmap = PictureFood;



Instead of reinterpret_cast, you should be using static_cast, or even dynamic_cast. And checking for nullptr before accessing the Bitmap.
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1457
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: [ListView]load image

Postby Lena » Wed Oct 18, 2017 11:56 am

You are still using the wrong folder path on Windows.


I use Windows only for the test during development. The main goal is Android.

Instead of reinterpret_cast, you should be using static_cast, or even dynamic_cast.


I try static_cast and dynamic_cast. But but I can not see images in Android.
Put breakpoint on line if(FileExists(path)) and press F8

Code: Select all
TBitmap * PictureFood = new TBitmap();
         if(FileExists(path))
         {
          PictureFood->LoadFromFile(path);//I am here. All fine but I can njt see images in Android
          dynamic_cast<TListItemImage *>(item->Objects->FindDrawable(L"MyImage"))->Bitmap = PictureFood;
          if(dynamic_cast<TListItemImage *>(item->Objects->FindDrawable(L"MyImage"))->Bitmap == NULL)
            {
             ShowMessage(L"Problem!");//I do not see this message
            }
         }
         else
            {
             //image by default
             dynamic_cast<TListItemImage *>(item->Objects->FindDrawable(L"MyImage"))->Bitmap = ImageRAD->Bitmap;
            }
Lena
BCBJ Master
BCBJ Master
 
Posts: 525
Joined: Sun Feb 06, 2011 1:28 pm

Re: [ListView]load image

Postby Lena » Wed Oct 18, 2017 12:11 pm

If I make a global variable;
TBitmap * PictureFood;
Code: Select all
PictureFood = new TBitmap();
         if(FileExists(path))
         {
          PictureFood->LoadFromFile(path);
          dynamic_cast<TListItemImage *>(item->Objects->FindDrawable(L"MyImage"))->Bitmap = PictureFood;
          if(dynamic_cast<TListItemImage *>(item->Objects->FindDrawable(L"MyImage"))->Bitmap == NULL)
            {
            ShowMessage(L"Problem!");//I do not see this message
            }
         }

I see only last image in last item of ListView in Android. :(
Lena
BCBJ Master
BCBJ Master
 
Posts: 525
Joined: Sun Feb 06, 2011 1:28 pm

Re: [ListView]load image

Postby rlebeau » Wed Oct 18, 2017 2:31 pm

Lena wrote:
Have you tried saving the loaded TBitmap to another file to check the image?

Such an attempt:
Image1 on Form have name ImageRAD (visible=false).


I meant, saving the image to file using TBitmap::SaveToFile(). If a TBitmap fails to load a file, it is not going to display anything in any UI you try to show it in, and saving it back to a file will either produce a blank image or an error. But at least you would have the new files to compare to the originals.
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1457
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: [ListView]load image

Postby Lena » Thu Oct 19, 2017 6:59 am

BINGO! I see images in Windows and Android!

Code: Select all
void __fastcall TForm1::FormShow(TObject *Sender)
{

   #ifdef __ANDROID__
   String path = System::Ioutils::TPath::Combine(System::Ioutils::TPath::GetSharedDownloadsPath(), L"dibocca.ini");
   #elif _Windows
//only for test
   String path = System::Ioutils::TPath::Combine(GetCurrentDir(), L"dibocca.ini");
   #endif

   if(FileExists(path))
    {
      std::unique_ptr<TIniFile> FileINI(new TIniFile(path));
      std::unique_ptr<TStringList> AlliniSection(new TStringList());
      FileINI->ReadSections(AlliniSection.get());

      String text = "";
      String footer = "";
      String image = "";
      String name = "";
      String price = "";

      //ListView1->BeginUpdate();
       for (int i = 0; i < AlliniSection->Count; i++)
       {
         text = FileINI->ReadString(AlliniSection->Strings[i], L"text", "");
         footer = FileINI->ReadString(AlliniSection->Strings[i], L"footer", "");
         name = FileINI->ReadString(AlliniSection->Strings[i], L"name", "");
         price = FileINI->ReadString(AlliniSection->Strings[i], L"price", "");
         image = FileINI->ReadString(AlliniSection->Strings[i], L"image", "");

         TListViewItem * item = ListView1->Items->Add();
         item->Data[L"FullName"] = TValue::From<UnicodeString>(text);
         item->Data[L"FotterName"] = TValue::From<UnicodeString>(footer);
         item->Data[L"TextCount"] = TValue::From<UnicodeString>(L"1");
         item->Data[L"name"] = TValue::From<UnicodeString>(name);
         item->Data[L"price"] = TValue::From<UnicodeString>(price);


         #ifdef __ANDROID__
         String path = System::Ioutils::TPath::Combine(System::Ioutils::TPath::GetSharedDownloadsPath(), image);
         #elif _Windows
         String path = System::Ioutils::TPath::Combine(GetCurrentDir(), image);
         #endif

         TBitmap * PictureFood = new TBitmap();
         if(FileExists(path))
         {
          PictureFood->LoadFromFile(path);
          TListItemImage * img = dynamic_cast<TListItemImage *>(item->Objects->FindDrawable(L"MyImage"));
          img->Bitmap = PictureFood;
          img->OwnsBitmap = true;//BINGO!
          if(dynamic_cast<TListItemImage *>(item->Objects->FindDrawable(L"MyImage"))->Bitmap == NULL)
            {
            ShowMessage(L"Problem!");
            }
         }
         else
            {
             //image by default
             dynamic_cast<TListItemImage *>(item->Objects->FindDrawable(L"MyImage"))->Bitmap = ImageRAD->Bitmap;
            }


         item->Data[L"min"] = TValue::From<int>(0);
         item->Data[L"add"] = TValue::From<int>(1);

       }
      //ListView1->EndUpdate();
    }
    else
      {
        ShowMessage(L"Не найден файл меню dibocca.ini");
        }

}



Additional questions:

1. Do I need in FormShow line:
ListView1->BeginUpdate();
ListView1->EndUpdate();
?
2. I use TBitmap * PictureFood = new TBitmap();
How to remove the memory leak because there is no delete?

Maybe for the second question I need something like this global ad?:
std::vector<std::unique_ptr<Graphics::TBitmap> > PictureFood;
But I do not understand how to correctly add objects. :(
Lena
BCBJ Master
BCBJ Master
 
Posts: 525
Joined: Sun Feb 06, 2011 1:28 pm

Re: [ListView]load image

Postby rlebeau » Thu Oct 19, 2017 10:18 am

Lena wrote:1. Do I need in FormShow line:
ListView1->BeginUpdate();
ListView1->EndUpdate();
?


You should always use (Begin|End)Update() when making multiple changes to a control. It delays repainting until after you are done making changes.

Lena wrote:2. I use TBitmap * PictureFood = new TBitmap();
How to remove the memory leak because there is no delete?


There is no memory leak, because you are setting img->OwnsBitmap=true, so the TBitmap will be freed when the TListItemImage object is freed.

If you want to manage the lifetime of the TBitmap yourself, you have to set img->OwnsBitmap=false instead, and do not free the TBitmap until after the TListItemImage is freed.

Lena wrote:Maybe for the second question I need something like this global ad?:
std::vector<std::unique_ptr<Graphics::TBitmap> > PictureFood;


Only if you want to manage the TBitmap lifetimes yourself. For example:

Code: Select all
private:
   std::vector<std::unique_ptr<Graphics::TBitmap>> PictureFoods;

...

struct DelayUpdates
{
   TControl *m_ctrl;
   DelayUpdates(TControl *ctrl) : m_ctrl(ctrl) { m_ctrl->BeginUpdate(); }
   ~DelayUpdates() { m_ctrl->EndUpdate(); }
};

void __fastcall TForm1::FormShow(TObject *Sender)
{
   String inifolder, imgfolder;

   #if defined(__ANDROID__)
   inifolder = System::Ioutils::TPath::GetSharedDownloadsPath();
   imgfolder = System::Ioutils::TPath::GetSharedDownloadsPath();
   #elif defined(_Windows)
   inifolder = GetCurrentDir();//ExtractFilePath(ParamStr(0));
   imgfolder = GetCurrentDir();//ExtractFilePath(ParamStr(0));
   #endif

   String path = System::Ioutils::TPath::Combine(inifolder, _D("dibocca.ini"));
   if (!FileExists(path))
   {
      ShowMessage(L"Не найден файл меню dibocca.ini");
      return;
   }

   std::unique_ptr<TIniFile> FileINI(new TIniFile(path));
   std::unique_ptr<TStringList> AlliniSection(new TStringList());
   FileINI->ReadSections(AlliniSection.get());

   String text, footer, image, name, price;

   DelayUpdates du(ListView1);

   for (int i = 0; i < AlliniSection->Count; ++i)
   {
      text = FileINI->ReadString(AlliniSection->Strings[i], _D("text"), _D(""));
      footer = FileINI->ReadString(AlliniSection->Strings[i], _D("footer"), _D("");
      name = FileINI->ReadString(AlliniSection->Strings[i], _D("name"), _D("");
      price = FileINI->ReadString(AlliniSection->Strings[i], _D("price"), _D("");
      image = FileINI->ReadString(AlliniSection->Strings[i], _D("image"), _D("");

      TListViewItem * item = ListView1->Items->Add();
      item->Data[_D("FullName")] = TValue::From<String>(text);
      item->Data[_D("FotterName")] = TValue::From<String>(footer);
      item->Data[_D("TextCount")] = TValue::From<String>(_D("1"));
      item->Data[_D("name")] = TValue::From<String>(name);
      item->Data[_D("price")] = TValue::From<String>(price);
      item->Data[_D("min")] = TValue::From<int>(0);
      item->Data[_D("add")] = TValue::From<int>(1);

      TListItemImage * img = static_cast<TListItemImage *>(item->Objects->FindDrawable(L"MyImage"));
      if (img)
      {
            String path = System::Ioutils::TPath::Combine(imgfolder, image);
            std::unique_ptr<Graphics::TBitmap> PictureFood(new Graphics::TBitmap(path));
            if (!PictureFood->IsEmpty())
            {
               PictureFoods.push_back(std::move(PictureFood));
               img->Bitmap = PictureFoods.back().get();
            }
            else
            {
               //image by default
               img->Bitmap = ImageRAD->Bitmap;
            }
            img->OwnsBitmap = false;
         }
      }
   }
}


Otherwise, get rid of the std::vector and do this instead:

Code: Select all
std::unique_ptr<Graphics::TBitmap> PictureFood(new Graphics::TBitmap(path));
if (!PictureFood->IsEmpty())
{
   img->Bitmap = PictureFood.get();
   img->OwnsBitmap = true;
   PictureFood.release();
}
else
{
   //image by default
   img->Bitmap = ImageRAD->Bitmap;
   img->OwnsBitmap = false;
}
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1457
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: [ListView]load image

Postby Lena » Thu Oct 19, 2017 11:39 pm

Thank You very much!
Lena
BCBJ Master
BCBJ Master
 
Posts: 525
Joined: Sun Feb 06, 2011 1:28 pm


Return to Technical

Who is online

Users browsing this forum: No registered users and 13 guests