• 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 обеспечен

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


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

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

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

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

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

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

Войти

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

Войти


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

    • От Вадим Смоленский
      В своем проекте (это японско-русский словарь) мне приходится выводить на TImage.Bitmap.Canvas хранящиеся в юникоде иероглифы, причем шрифт пользователь может выбрать сам. Столкнулся с неприятным явлением: разные шрифты располагают выводимый символ на разной высоте, в результате чего иероглиф часто не вписывается в предназначенную для него область. По умолчанию принят шрифт Tahoma, с ним всё хорошо:

      Но вот шрифт Yu Mincho:

      А вот, еще хлеще, Kozuka Gothic:

      Вопрос: что за параметр регулирует вертикальное смещение, можно ли его вынуть и с ним работать?
    • От zekelive
      Добрый день, товарищи. Если кто сталкивался, подскажите) в потоке создаются картинкив виде плиток и прочие компоненты и падают на scrollbox.  Но в runtime пролистывание лагает, да в целом вся программа подлагивает. Можно ли как то реализовать подгрузка в фоне без ущерба? Или может ещё какой способ есть?
    • От Вадим Смоленский
      Некоторые юзеры (видимо, из близоруких) прибегают к масштабированию экрана и требуют, чтобы ваше приложение тоже масштабировалось. Когда мой проект был в VCL, я смотрел на параметр Screen.PixelsPerInch и учитывал его значение в своем коде. В FireMonkey этого нет. Я пытался понять, как это делать теперь, и окончательно запутался. Microsoft на официальном сайте упоминает функцию SystemParametersInfo с параметром SPI_GETLOGICALDPIOVERRIDE, но делает оговорку, что такую конфигурацию лучше не использовать - дескать, в новых версиях Windows она не будет работать. Взамен рекомендуют функцию GetScaleFactorForDevice, но тут же заявляют, что под Windows 8 и она работать не будет, а будет работать только GetScaleFactorForMonitor. Голова кругом. Нет ли у кого, случаем, готового решения, которое работало бы на любых Windows - хотя бы от семерки до десятки?
      Еще один момент, который я не могу понять - в Windows 10 в настройках экрана, помимо масштаба в процентах, можно выбирать еще и разрешение в пикселях. По-моему, раньше такого разделения не было, хотя могу ошибаться. Связаны ли эти настройки друг с другом? И как они соотносятся со старым добрым DPI?
    • От x11
      ListView в режиме appearance.
      Добавил пару TListItemGlyphButton, указал в свойстве ButtonType - CheckBox. Но при работе приложения - пустота.
      Другие типы кнопок тоже не отображает.
      ЧЯДНТ.
    • От Вадим Смоленский
      Работая в Berlin, подправил стиль для одного компонента посредством вызова "Edit Custom Style" во всплывающем меню. Потом перешел на Tokyo; потом поменял компьютер и установил на него Tokyo заново. Сейчас вижу, что компонент отображается в стиле по умолчанию. Никаких сообщений по поводу пропавшего стиля не припомню. Где мне найти и как опять подключить мой отредактированный стиль?
    • От Вадим Смоленский
      В своем проекте (это японско-русский словарь) я часто вывожу текст на TImage методом TCanvas.FillText, и результат выглядит так:

      Один из тестеров, у которого установлена Windows XP, прислал скриншот того, что выводится у него:

      Ровно такую же безобразную картину я видел у себя, когда, экспериментируя с быстродействием, вставлял в файл dpr операторы  FMX.Types.GlobalUseDX:=False или FMX.Types.GlobalUseDirect2D:=False
      То ли в XP эти установки приняты по умолчанию, то ли так сконфигурирована система у тестера. Первое, что приходит в голову - вставить в код присвоение обеим упомянутым переменным значения True. Но прежде, чем предпринять такую попытку, хотелось бы услышать мнение экспертов. В чем здесь корень зла?
    • От Вадим Смоленский
      Записываю русский текст в файл:
      procedure WriteToFile; var F: TextFile; S: string; begin S:='Русский текст'; AssignFile(F,MyFile.txt'); ReWrite(F); WriteLn(F,S); CloseFile(F); end; Тип данных string - по идее, то же, что WideString. То бишь, в строку S записан юникод. Тем не менее, открывая потом файл, вижу, что русский текст сохранен в ANSI. Как добиться его сохранения в юникоде?
    • От x11
      По совету krapotkin`a
      http://fire-monkey.ru/topic/4595-listview-скрывает-повторяющийся-текст-заголовков/?do=findComment&comment=29209
       
      Забросил LiveBinding при заполнении TListView и начал заполнять ручками.
      Почти всё хорошо за исключением загрузки картинки из базы.
      При использовании LiveBinding картинка грузилась, а теперь просто пустота.
      На Windows картинки грузятся и отображаются, а на Андроиде не хотят.
       
      Вот часть кода загрузки
      Var stream: TBlobStream; ... begin .... lv.BeginUpdate; try lv.Items.Clear; qObjects2.close; qObjects2.Open; while not qObjects2.Eof do begin item := lv.Items.Add; item.Data[sItemId] := qObjects2id.AsString; item.Data[sItemPrice] := qObjects2price.AsString; item.Data[sItemType] := qObjects2type_name.Value; ... ... ... ListItemImage := Item.Objects.FindObjectT<TListItemImage>(sItemImg); stream := TBlobStream.Create(qObjects2img, bmRead); try if stream.Size <= 0 then ListItemImage.ImageIndex := 0 else ListItemImage.Bitmap := TBitmap.CreateFromStream(stream); finally stream.Free; end;  
      К ListView прикреплен список картинок, где есть картинка-пустышка, которая грузится, если у записи нет картинки.
      вот этой строкой ListItemImage.ImageIndex := 0
      картинки-пустышки нормально отображаются и на Андроид-устройстве в том числе.
      Ошибок нет, а просто пустой TListItemImage там, где должна быть картинка.
      Может, я какое-то свойство забыл включить у TListItemImage?
      При отладке видно, что картинка загружается из базы.
       
       
    • От x11
      Сделал такой эксперимент в событии OnUpdateObjects
      if AItem.Purpose in [TListItemPurpose.Header] then Memo1.Lines.Add(AItem.Text);  
      И в memo загрузилось в два раза больше строк, чем элементов списка.
      Это нормально, так и должно быть?
    • От Вадим Смоленский
      Писал на Delphi 10.2 Tokyo под Windows. У тестеров приложение валится при первой же попытке обращения к БД с сообщением "unable to open database file". У меня на компе всё нормально. Файл sqlite3.dll версии 3.21.0.0 в дистрибутив включен, кладется рядом с исполнимым файлом. Что может быть не так?
  • Последние посетители   0 пользователей онлайн

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