Перейти к содержанию
Fire Monkey от А до Я
  • 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

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

Рекомендуемые сообщения

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

Ссылка на комментарий

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить на вопрос...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...