• 0
Евгений Корепов

ListView с картинкой - не работает под Android

Вопросы

Господа и товарищи, помогите тупому мне! Столкнулся с странным. Под windows все отлично работает, а под android не могу добиться загрузки картинок. Мозг уже сломал.

Собрал тестовый проект - в ListView (DynamicAppearance) добавляем 4 ListViewItem, в ListViewUpdatingObjects все создаем и грузим картинки из инета (потоки и прочее убрал для упрощения). Картанка слева, текст (URL) справа, проще некуда. Прилагаю к сообщению архив проекта и код.

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  FMX.ListView.Types, FMX.ListView.Appearances, FMX.ListView.Adapters.Base,
  FMX.ListView, System.Net.HTTPClient, FMX.Objects;

type
  TFormMain = class(TForm)
    ListView: TListView;
    procedure ListViewUpdatingObjects(const Sender: TObject;
      const AItem: TListViewItem; var AHandled: Boolean);
    procedure FormShow(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    ListViewUpdate : Boolean;    
    procedure MyListViewUpdateObjects(const AListView: TListView; const AItem: TListViewItem);
    procedure InitListView(AListView : TListView);
    function LoadImageFromURL(AURL : String) : TBitmap;
  end;

var
  FormMain: TFormMain;

implementation

{$R *.fmx}

procedure TFormMain.FormCreate(Sender: TObject);
begin
  ListViewUpdate:=False;  
end;

procedure TFormMain.FormShow(Sender: TObject);
begin
  InitListView(ListView);
end;

procedure TFormMain.InitListView(AListView : TListView);
Var AListViewItem : TListViewItem;
    AImageURL : String;
begin
  AImageURL:='http://kayfolom.ru/images/test/';

  ListViewUpdate:=True;
  AListViewItem:=AListView.Items.Add;
  AListViewItem.Data['ImageURL']:=AImageURL + 'logo.png';
  ListViewUpdate:=False;
  AListViewItem.Adapter.ResetView(AListViewItem);

  ListViewUpdate:=True;
  AListViewItem:=AListView.Items.Add;
  AListViewItem.Data['ImageURL']:=AImageURL + '000487806d3a2ab98aeb2c47b810fc8b.jpg';
  ListViewUpdate:=False;
  AListViewItem.Adapter.ResetView(AListViewItem);

  ListViewUpdate:=True;
  AListViewItem:=AListView.Items.Add;
  AListViewItem.Data['ImageURL']:=AImageURL + '0012ef6cb42e95268a4cd1d832a2b93a.jpg';
  ListViewUpdate:=False;
  AListViewItem.Adapter.ResetView(AListViewItem);

  ListViewUpdate:=True;
  AListViewItem:=AListView.Items.Add;
  AListViewItem.Data['ImageURL']:=AImageURL + '0022454ccb4f81a701cb3a3c89d52d2f.jpg';
  ListViewUpdate:=False;
  AListViewItem.Adapter.ResetView(AListViewItem);
end;

procedure TFormMain.ListViewUpdatingObjects(const Sender: TObject;
  const AItem: TListViewItem; var AHandled: Boolean);
begin
  if Not ListViewUpdate then
  begin
    MyListViewUpdateObjects(Sender as TListView, AItem);
    AHandled:=True;
  end;
end;

procedure TFormMain.MyListViewUpdateObjects(const AListView: TListView; const AItem: TListViewItem);
Var AName : TListItemText;
    AImage : TListItemImage;
    AvailableWidth, ImageWidth, ImageHeight : single;
  function SetupTextObject(const AName, AText : String; AFontSize : Single; AFontStyles : TFontStyles;
  AWidth, AHeight, X , Y : Single;
      AAlign, AVertAlign: TListItemAlign; ATextAlign, ATextVertAlign: TTextAlign) : TListItemText;
  begin
    Result:=TListItemText(AItem.View.FindDrawable(AName));
    if Result=Nil then
      Result:=TListItemText.Create(AItem);
    Result.Name:=AName;
    Result.Width:=AWidth;
    Result.WordWrap:=True;
    Result.Font.Size:=AFontSize;
    Result.Font.Style:=Result.Font.Style + AFontStyles;
    Result.Trimming:=TTextTrimming.None;
    Result.Text:=AText;
    Result.PlaceOffset.X:=X;
    Result.PlaceOffset.Y:=Y;
    Result.Align:=AAlign;
    Result.VertAlign:=AVertAlign;
    Result.TextAlign:=ATextAlign;
    Result.TextVertAlign:=ATextVertAlign;
    Result.Height:=AHeight;
  end;
  function SetupImageObject(const AName : String; AWidth, AHeight, X , Y : Single;
      AAlign, AVertAlign: TListItemAlign) : TListItemImage;
  Var AImageURL : String;
  begin
    Result:=TListItemImage(AItem.View.FindDrawable(AName));
    if Result=Nil then
    begin
      Result:=TListItemImage.Create(AItem);
      AImageURL:=AItem.Data['ImageURL'].AsString;
      Result.Bitmap:=LoadImageFromURL(AImageURL);
    end;
    Result.Name:=AName;
    Result.Width:=AWidth;
    Result.Height:=AHeight;
    Result.PlaceOffset.X:=X;
    Result.PlaceOffset.Y:=Y;
    Result.Align:=AAlign;
    Result.VertAlign:=AVertAlign;
    Result.ScalingMode:=TImageScalingMode.StretchWithAspect;
  end;
begin
  AvailableWidth:=AListView.Width - AListView.ItemSpaces.Left - AListView.ItemSpaces.Right;

  // Изображение размещаем слева
  ImageWidth:=AvailableWidth / 3;
  ImageHeight:=AvailableWidth / 3;
  AImage:=SetupImageObject('Image', ImageWidth, ImageHeight, 0, 0,
    TListItemAlign.Leading, TListItemAlign.Leading);

  // Текст справа
  AName:=SetupTextObject('Name', AItem.Data['ImageURL'].AsString, 14, [],
    AvailableWidth - ImageWidth, ImageHeight,
    ImageWidth, 0,
    TListItemAlign.Leading, TListItemAlign.Leading, TTextAlign.Center, TTextAlign.Center);

  AItem.Height:=Round(ImageHeight + AListView.ItemSpaces.Top + AListView.ItemSpaces.Bottom);
end;

function TFormMain.LoadImageFromURL(AURL : String) : TBitmap;
Var AHTTPClient : THTTPClient;
    AStream : TMemoryStream;
    HTTPResponse : IHTTPResponse;
begin
  Result:=Nil;
  AHTTPClient:=THTTPClient.Create;
  AStream:=TMemoryStream.Create;
  try
    HTTPResponse:=AHTTPClient.Get(AURL, AStream);
  finally
    if HTTPResponse.StatusCode=200 then
      Result:=TBitmap.CreateFromStream(AStream);
  end;
end;

end.

 

test092 ListView with Image.7z

Изменено пользователем Евгений Корепов

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

5 ответов на этот вопрос

  • 1

Блин! У меня нет слов! Проблема решилась многочасовым гугленьем. Самое смешное  - решение нашел на этом форуме в сообщении http://fire-monkey.ru/topic/3014-ne-otobrazhaetsya-tlistitemimage/?do=findComment&comment=18113 , спасибо огромное @DimArt за решение!

Вся проблема была в недокументированном свойстве OwnsBitmap, достаточно после загрузки изображения в ListItemImage.Bitmap добавить строчку ListItemImage.OwnsBitmap:=True; И все начинает отлично работать под Android.

Документация ембаркадеро говорит нам о этом свойстве следущее "Embarcadero Technologies does not currently have any additional information." (http://docwiki.embarcadero.com/Libraries/Tokyo/en/FMX.ListView.Types.TListItemImage.OwnsBitmap). Б*%дь, ну как так то? У меня одни маты, два дня просраны вхолостую...

Итоговый, работоспособный код функции SetupImageObject ниже

  function SetupImageObject(const AName : String; AWidth, AHeight, X , Y : Single;
      AAlign, AVertAlign: TListItemAlign) : TListItemImage;
  Var AImageURL : String;
  begin
    Result:=TListItemImage(AItem.View.FindDrawable(AName));
    if Result=Nil then
    begin
      Result:=TListItemImage.Create(AItem);
      AImageURL:=AItem.Data['ImageURL'].AsString;
      Result.Bitmap:=LoadImageFromURL(AImageURL);
      Result.OwnsBitmap:=True;
    end;
    Result.Name:=AName;
    Result.Width:=AWidth;
    Result.Height:=AHeight;
    Result.PlaceOffset.X:=X;
    Result.PlaceOffset.Y:=Y;
    Result.Align:=AAlign;
    Result.VertAlign:=AVertAlign;
    Result.ScalingMode:=TImageScalingMode.StretchWithAspect;
  end;

 

Изменено пользователем Евгений Корепов

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Кстати, если кто объяснит что делает строчка ListItemImage.OwnsBitmap:=True, буду безмерно благодарен. Не люблю копипастить, без понимания что именно делает код. А лезть в исходники пока некогда.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
В 09.04.2017 в 22:51, Евгений Корепов сказал:

Кстати, если кто объяснит что делает строчка ListItemImage.OwnsBitmap:=True, буду безмерно благодарен. Не люблю копипастить, без понимания что именно делает код. А лезть в исходники пока некогда.

судя по исходникам, при установленном свойстве картинка считается собственностью ListItemImage и при ListItemImage.Destroy уничтожается. То есть для того, чтобы не было проблем с памятью и сегфолтами надо битмап копировать

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
1 час назад, marsden сказал:

судя по исходникам, при установленном свойстве картинка считается собственностью ListItemImage и при ListItemImage.Destroy уничтожается. То есть для того, чтобы не было проблем с памятью и сегфолтами надо битмап копировать

О, спасибо! Значит  ListItemImage.OwnsBitmap управляет типом ссылки на Bitmap - False - слабая ссылка, True нормальная ссылка. Вот почему в документации это не указать?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
15 часов назад, Евгений Корепов сказал:

О, спасибо! Значит  ListItemImage.OwnsBitmap управляет типом ссылки на Bitmap - False - слабая ссылка, True нормальная ссылка. Вот почему в документации это не указать?

потому что сделано через жжж. Зажми Ctrl и щелкни по OwnsBitmap, затем по SetOwnsBitmap - меня увиденное повергло в ступор и я понял, что ходить по этому канату надо со страховкой, иначе acces violation & invalid pointer обеспечен

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Для публикации сообщений создайте учётную запись или авторизуйтесь

Вы должны быть пользователем, чтобы оставить комментарий

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти

  • Похожий контент

    • От Вадим Смоленский
      В декабре я задавал здесь вопрос о борьбе с перехватами нажатий клавиш компонентом TWebBrowser. Продвинутый пользователь Kami посоветовал тогда, раз уж меня интересует только Windows, поставить хук на клавиатуру. Поделился полезной ссылкой. Добавил, что можно еще много чего нагуглить. Что-то действительно нагуглилось - но не в том объеме, чтобы я смог четко понять, как это следует делать. Вопросов много. Куда именно должна быть воткнута функция KeyboardProc? Что в ней должно содержаться, чтобы управление передавалось уже написанному обработчику события FormKeyDown? Многие также упоминают о возникающих проблемах с юникодом, и хорошо было бы понять, как уберечься от них.
      Буду очень признателен, если кто-нибудь осветит эту темную для меня материю.
    • От Вадим Смоленский
      Как преобразовать тип HString в обычную строку?
      Нагуглил упоминания о функции TWindowsString.HStringToString, которая должна находиться в System.WinrtHelpers. Но такого юнита в поставке Delphi не наблюдаю. Может, его можно где-нибудь раздобыть? Или существуют иные способы?
    • От gutalin79
      Почему при добавлении MapView в пример Android Service, приложение перестает работать?
      AndroidSimpleService.zip
       
       
       
      Пример делал по этому видео: 
       
    • От Вадим Смоленский
      Пытаясь разобраться с проблемой размещения файлов в UWP-приложениях, пришел к необходимости создать в своем коде объект класса ApplicationData. Этот класс описан на соответствующей странице майкрософтовской документации, где в самом начале обозначено следующее:
      Namespace:  Windows.Storage
      Assemblies:  Windows.Storage.dll, Windows.dll
      Юнита с именем Windows.Storage или Winapi.Windows.Storage я в поставке Delphi не наблюдаю. Неудивительно, что попытки вставить соответствующие идентификаторы в раздел uses ни к чему не ведут. Как в таких случаях поступают? Откуда берут необходимое?
    • От Вадим Смоленский
      Хочу еще раз обратиться к коллективному разуму в надежде все-таки разобраться с тем, как должен быть устроен пакет appx для размещения в Microsoft Store. А именно - как организовать размещение настроек и пользовательских файлов в специально отведенных для этого папках, а не в установочном каталоге, что запрещено.
      Вся информация, которую я смог к сегодняшнему дню накопать, размещена на этой странице майкрософтовской документации и сводится к тому, что при инсталляции пакета appx автоматически создается хранилище пользовательских данных из трех папок. Цитирую: one for local files, one for roaming files, and one for temporary files. В общем-то, это удобно - по крайней мере, не нужно заботиться о деинсталляции: ровно эти же папки при удалении программы и сотрутся.
      Вопрос в том, как к этому хранилищу обратиться. В идеале, конечно же, хотелось бы иметь возможность уже в Deployment Manager обозначить для некоторых файлов, что они должны быть положены в это хранилище, а не в установочный каталог. Но как это можно сделать и можно ли сделать вообще, мне понять не удалось. Списка констант для параметра Remote Path я найти не смог, а отдельные упоминаемые тут и там константы ('res', 'assets', 'classes', 'library') явно относятся к мобильным платформам, а не к Windows.
      Если так поступить нельзя, остается класть всё в установочный каталог, а при первом запуске приложения переносить в нужное место. Но опять же: как приложению заполучить полный адрес этого места? Ведь это вовсе не привычные нам CSIDL_APPDATA и не CSIDL_COMMON_APPDATA, это нечто новое, доселе невиданное, в документации называемое "local app data store". На той же странице есть пример соответствующего кода с использованием класса ApplicationData, но он написан на незнакомом мне C#. Там есть также ссылки на описание класса ApplicationData, но по ссылкам тоже C# и C++. Был бы очень признателен, если бы кто-нибудь показал мне, как заполучить адрес local app data store средствами Delphi.
    • От Вадим Смоленский
      Целый месяц бодаюсь с Microsoft Store, пытаясь разместить у них свое UWP-приложение в виде пакета appx. Получаю отлуп за отлупом. Проблема: приложение должно располагать папкой для пользователя, отличной от установочного каталога, где он мог бы сохранять файлы. Там же должны храниться файлы настроек. Раньше, создавая дистрибутив в InnoSetup, я всегда предусматривал создание такой папки по адресу <user>\AppData\Roaming\MyApp. Теперь всё полагается указывать в разделе Deployment - но как там указать такой адрес? Вроде бы, есть графа "Remote path", и логично существовать каким-то макросам для нее, вроде AppData - но никакой информации на эту тему я найти не смог. Видел лишь упоминания таких макросов, как res, assets, classes, library - однако все они, как я понял, относятся к мобильным платформам.
      Попытки обходных маневров не удались. Сначала я решил всё класть в установочный каталог, а при первом запуске создавать нужную папку и перебрасывать в нее несколько файлов. На моем компьютере это работает, но при тестировании в Microsoft Store отчего-то валится (присылают скриншот с сообщением "Access is denied"). То ли нельзя стирать файлы в установочном каталоге, то ли нельзя в таком режиме создавать новый каталог. Потом я прочитал где-то, что UWP-приложениям всё равно, где лежат файлы, они могут их менять даже в установочном каталоге. Попробовал всё валить в одну кучу и так работать. Увы, опять отрапортовали о падении ("The app tries to create a file under WindowsApps folder").
      Получается, что все-таки нужно как-то заставить appx-дистрибутив создавать при установке папку по адресу <user>\AppData\Roaming\MyApp. Но как?
      Спрашивал на experts-exchange, там знатоков не нашлось. Если и здесь нет, может, кто-нибудь хотя бы подскажет, в каких местах есть смысл спросить?
    • От Astghik
      Hello !!!
      I want onButtonClick create popup. I use TPopup component. All good, but on android "Back button" click closing forma. But I want close popup (when popup is shown).

       
      //---------------------------------------------------------------------------------
      void __fastcall  btn3PointsClick(TObject *Sender)
      {
          PopUpSettings->IsOpen = true;
          PopUpSettings->PlacementTarget = btn3Points;
          PopUpSettings->BringToFront();
      }
      //-------------------------------------------------------------------------------------
      void __fastcall FormCloseQuery(TObject *Sender, bool &CanClose)
      {
          try {
              if (PopUpSettings->IsOpen == true) {
                  CanClose = false;
              }
              else {
                  CanClose = true;
              }
          } __finally {
              PopUpSettings->IsOpen = false;
          }
      }
      //-------------------------------------------------------------------
       
    • От Вадим Смоленский
      Проект для Windows, автономная БД SQLite, никаких клиент-серверных дел, компоненты и операции самые простые: SQL-запрос в TFDQuery, вызов метода Open. Всё работает нормально, но отдельные пользователи жалуются на эпизодические непредсказуемые падения при поиске. Интересно, что после каждого такого падения всё опять функционирует нормально, но потом приложение не удается нормально закрыть, приходится вызывать диспетчер задач.
      Стабильно воспроизвести не могут ни пользователи, ни я сам. Мне удалось это считанные несколько раз - я лишь смог убедиться через отладчик, что проблема при закрытии связана именно с базой данных: вставлял в обработчик FormCloseQuery оператор TFDConnection.Close - и программа пару раз упала именно на этом операторе.
      Найти корень зла пока не удалось. Показалось только, что проблема возникает лишь тогда, когда поиск приводит к слишком большому (несколько тысяч) числу записей в TFDQuery.
      Может, стоит поменять какие-нибудь установки в TFDConnection или TFDQuery ?
    • От Rokweb
      Таймер с интервалом 1мс заметно подтормаживает во время выполнения анимации TFloatAnimation в Tokyo. У всех так или только у меня?
    • От Rokweb
      Здравствуйте.
      Речь пойдёт об Android.
      Использовал в Berlin данный unit для проигрывания звуков (TMediaPlayer не подходит) и все отлично работало. Сейчас перешел на Tokyo и происходит зависание в цикле:
       
      while not GLoaded do begin Sleep(10); Application.ProcessMessages; end;  
      Модуль прикрепил в сообщении.
       
      Так же интересует - возможно ли, использовать стиль, созданный в процессе разработки Android приложения - в iOS и если да - то как это правильно реализовать (почти каждый контрол имеет сейчас свой стиль)?
       
      Прошу помощи.
      GameAudioManager.zip
  • Последние посетители   0 пользователей онлайн

    Ни одного зарегистрированного пользователя не просматривает данную страницу