Евгений Корепов
-
Постов
738 -
Зарегистрирован
-
Посещение
-
Победитель дней
100
Активность репутации
-
Евгений Корепов получил реакцию от Sergionn в Helper для TBitmap - асинхронная загрузка картинки из URL
Окончательный вариант. Долго бился с изменением размера картинки внутри потока (к примеру что бы в ListView не грузить картинки больше чем нужно). Средствами TBitmap это оказалось сделать невозможно (именно в потоке), чтение форумов, issue Эмбаркадеро, привело к туманному выводу что проблема в архитектуре FMX. В Токио, TBitmap стал потокобезопасным - это означает что никогда не пытайтесь использовать Bitmap в потоке, рано или поздно получите артефакты и глюки.
Я решил проблему отказавшись от работы с TBitmap в потоке, и использовав для этого TBitmapSurface (загрузка из stream, изменение размеров).
Что умеет хелпер:
1. Загрузка картинки в Bitmap и подгонкой размера (размер можно и не менять - не передавайте параметр ASize)
procedure LoadFromURLAsync(const AURL : String; const ASize : TControlSize = nil); overload;
2. Загрузка картинки в TListItemImage ListView. После Окончания загрузки хелпер выполнит AListItemImage.Invalidate в основном потоке приложения для отрисовки картинки.
procedure LoadFromURLAsync(const AURL : String; const AListItemImage : TListItemImage); overload;
3. Создание картинки. Тоже самое что и предыдущие, но можно сэкономить строчку кода ABitmap:=TBitmap.Create ?
constructor CreateFromUrlAsync(const AURL : String; const AListItemImage : TListItemImage = nil);
По картинкам в ListView - можете на свой страх и риск грузить 100500 картинок, но лучше использовать загрузку только для видимой части (+- еще сколько то итемов). Хелпер тупо грузит картинки и не обеспечивает механизм оптимальной загрузки.
Протестировано на Windows и Android.
Ответы на вопросы которые мне задавали:
Почему не создавать отдельный поток для каждой картинки, в нем создавать THTTPClient и делать запрос - пробовал этот вариант, он медленнее на порядок, даже под виндой это чертовски медленно. AHTTPClient.BeginGet и так создает отдельный поток на каждый запрос. Почему AHTTPClient глобальный для юнита - в хелпере нельзя вводить свои переменные, а создавать AHTTPClient внутри функции не выйдет - он убьется до завершения потока. И текущий вариант быстрее. Код хелпера и архив с тестовым проектом:
unit BitmapAsyncLoader; interface uses System.Net.HttpClient, System.Net.URLClient, System.SysUtils, System.Types, System.Classes, FMX.Graphics, FMX.Surfaces, FMX.Types, FMX.ListView.Types; type TBitmapAsyncLoader = class helper for TBitmap private function ResizeBitmapSurface(const ABitmapSurface : TBitmapSurface; const AWidth, AHeight : Integer) : TBitmapSurface; procedure SynchronizeAssignFromBitmapSurface(const ABitmapSurface : TBitmapSurface; const AListItemImage : TListItemImage = nil); procedure StartHTTPThread(const AURL : String; const AWidth, AHeight : Integer; const AListItemImage : TListItemImage = nil); public procedure LoadFromURLAsync(const AURL : String; const ASize : TControlSize = nil); overload; procedure LoadFromURLAsync(const AURL : String; const AListItemImage : TListItemImage); overload; constructor CreateFromUrlAsync(const AURL : String; const AListItemImage : TListItemImage = nil); end; implementation type THTTPClientListener = class class procedure HTTPClientValidateServerCertificate(const Sender: TObject; const ARequest: TURLRequest; const Certificate: TCertificate; var Accepted: Boolean); end; var AHTTPClient : THTTPClient; constructor TBitmapAsyncLoader.CreateFromUrlAsync(const AURL : String; const AListItemImage : TListItemImage = nil); begin Create; LoadFromURLAsync(AURL, AListItemImage); end; procedure TBitmapAsyncLoader.LoadFromURLAsync(const AURL : String; const AListItemImage : TListItemImage); var AWidth, AHeight : Integer; begin if Assigned(AListItemImage) then begin AWidth:=Round(AListItemImage.Width); AHeight:=Round(AListItemImage.Height); end else begin AWidth:=-1; AHeight:=-1; end; StartHTTPThread(AURL, AWidth, AHeight, AListItemImage); end; procedure TBitmapAsyncLoader.LoadFromURLAsync(const AURL : String; const ASize : TControlSize = nil); var AWidth, AHeight : Integer; begin if Assigned(ASize) then begin AWidth:=Round(ASize.Width); AHeight:=Round(ASize.Height); end else begin AWidth:=-1; AHeight:=-1; end; StartHTTPThread(AURL, AWidth, AHeight); end; function TBitmapAsyncLoader.ResizeBitmapSurface(const ABitmapSurface : TBitmapSurface; const AWidth, AHeight : Integer) : TBitmapSurface; begin if (AWidth <> -1) and (AHeight <> -1) then begin try Result:=TBitmapSurface.Create; Result.StretchFrom(ABitmapSurface, AWidth, AHeight, ABitmapSurface.PixelFormat); finally ABitmapSurface.Free; end; end else Result:=ABitmapSurface; end; procedure TBitmapAsyncLoader.SynchronizeAssignFromBitmapSurface(const ABitmapSurface : TBitmapSurface; const AListItemImage : TListItemImage = nil); begin TThread.Synchronize(Nil, procedure begin Assign(ABitmapSurface); ABitmapSurface.Free; if Assigned(AListItemImage) then AListItemImage.Invalidate; end ); end; procedure TBitmapAsyncLoader.StartHTTPThread(const AURL : String; const AWidth, AHeight : Integer; const AListItemImage : TListItemImage = nil); begin AHTTPClient.BeginGet( procedure (const ASyncResult: IAsyncResult) var AHTTPResponse : IHTTPResponse; ABitmapSurface : TBitmapSurface; begin if Not ASyncResult.IsCompleted then exit; try AHTTPResponse:=THTTPClient.EndAsyncHTTP(ASyncResult); except exit; end; if Assigned(AHTTPResponse) and (AHTTPResponse.StatusCode = 200) then begin ABitmapSurface:=TBitmapSurface.Create; if TBitmapCodecManager.LoadFromStream(AHTTPResponse.ContentStream, ABitmapSurface, Self.CanvasClass.GetAttribute(TCanvasAttribute.MaxBitmapSize)) then begin ABitmapSurface:=ResizeBitmapSurface(ABitmapSurface, AWidth, AHeight); SynchronizeAssignFromBitmapSurface(ABitmapSurface, AListItemImage); end; end; end, AURL ); end; class procedure THTTPClientListener.HTTPClientValidateServerCertificate(const Sender: TObject; const ARequest: TURLRequest; const Certificate: TCertificate; var Accepted: Boolean); begin Accepted:=True; end; initialization AHTTPClient:=THTTPClient.Create; AHTTPClient.OnValidateServerCertificate:=THTTPClientListener.HTTPClientValidateServerCertificate; finalization if Assigned(AHTTPClient) then AHTTPClient.DisposeOf; end.
BitmapAsyncLoader.7z
-
Евгений Корепов получил реакцию от Barbanel в Helper для TBitmap - асинхронная загрузка картинки из URL
Окончательный вариант. Долго бился с изменением размера картинки внутри потока (к примеру что бы в ListView не грузить картинки больше чем нужно). Средствами TBitmap это оказалось сделать невозможно (именно в потоке), чтение форумов, issue Эмбаркадеро, привело к туманному выводу что проблема в архитектуре FMX. В Токио, TBitmap стал потокобезопасным - это означает что никогда не пытайтесь использовать Bitmap в потоке, рано или поздно получите артефакты и глюки.
Я решил проблему отказавшись от работы с TBitmap в потоке, и использовав для этого TBitmapSurface (загрузка из stream, изменение размеров).
Что умеет хелпер:
1. Загрузка картинки в Bitmap и подгонкой размера (размер можно и не менять - не передавайте параметр ASize)
procedure LoadFromURLAsync(const AURL : String; const ASize : TControlSize = nil); overload;
2. Загрузка картинки в TListItemImage ListView. После Окончания загрузки хелпер выполнит AListItemImage.Invalidate в основном потоке приложения для отрисовки картинки.
procedure LoadFromURLAsync(const AURL : String; const AListItemImage : TListItemImage); overload;
3. Создание картинки. Тоже самое что и предыдущие, но можно сэкономить строчку кода ABitmap:=TBitmap.Create ?
constructor CreateFromUrlAsync(const AURL : String; const AListItemImage : TListItemImage = nil);
По картинкам в ListView - можете на свой страх и риск грузить 100500 картинок, но лучше использовать загрузку только для видимой части (+- еще сколько то итемов). Хелпер тупо грузит картинки и не обеспечивает механизм оптимальной загрузки.
Протестировано на Windows и Android.
Ответы на вопросы которые мне задавали:
Почему не создавать отдельный поток для каждой картинки, в нем создавать THTTPClient и делать запрос - пробовал этот вариант, он медленнее на порядок, даже под виндой это чертовски медленно. AHTTPClient.BeginGet и так создает отдельный поток на каждый запрос. Почему AHTTPClient глобальный для юнита - в хелпере нельзя вводить свои переменные, а создавать AHTTPClient внутри функции не выйдет - он убьется до завершения потока. И текущий вариант быстрее. Код хелпера и архив с тестовым проектом:
unit BitmapAsyncLoader; interface uses System.Net.HttpClient, System.Net.URLClient, System.SysUtils, System.Types, System.Classes, FMX.Graphics, FMX.Surfaces, FMX.Types, FMX.ListView.Types; type TBitmapAsyncLoader = class helper for TBitmap private function ResizeBitmapSurface(const ABitmapSurface : TBitmapSurface; const AWidth, AHeight : Integer) : TBitmapSurface; procedure SynchronizeAssignFromBitmapSurface(const ABitmapSurface : TBitmapSurface; const AListItemImage : TListItemImage = nil); procedure StartHTTPThread(const AURL : String; const AWidth, AHeight : Integer; const AListItemImage : TListItemImage = nil); public procedure LoadFromURLAsync(const AURL : String; const ASize : TControlSize = nil); overload; procedure LoadFromURLAsync(const AURL : String; const AListItemImage : TListItemImage); overload; constructor CreateFromUrlAsync(const AURL : String; const AListItemImage : TListItemImage = nil); end; implementation type THTTPClientListener = class class procedure HTTPClientValidateServerCertificate(const Sender: TObject; const ARequest: TURLRequest; const Certificate: TCertificate; var Accepted: Boolean); end; var AHTTPClient : THTTPClient; constructor TBitmapAsyncLoader.CreateFromUrlAsync(const AURL : String; const AListItemImage : TListItemImage = nil); begin Create; LoadFromURLAsync(AURL, AListItemImage); end; procedure TBitmapAsyncLoader.LoadFromURLAsync(const AURL : String; const AListItemImage : TListItemImage); var AWidth, AHeight : Integer; begin if Assigned(AListItemImage) then begin AWidth:=Round(AListItemImage.Width); AHeight:=Round(AListItemImage.Height); end else begin AWidth:=-1; AHeight:=-1; end; StartHTTPThread(AURL, AWidth, AHeight, AListItemImage); end; procedure TBitmapAsyncLoader.LoadFromURLAsync(const AURL : String; const ASize : TControlSize = nil); var AWidth, AHeight : Integer; begin if Assigned(ASize) then begin AWidth:=Round(ASize.Width); AHeight:=Round(ASize.Height); end else begin AWidth:=-1; AHeight:=-1; end; StartHTTPThread(AURL, AWidth, AHeight); end; function TBitmapAsyncLoader.ResizeBitmapSurface(const ABitmapSurface : TBitmapSurface; const AWidth, AHeight : Integer) : TBitmapSurface; begin if (AWidth <> -1) and (AHeight <> -1) then begin try Result:=TBitmapSurface.Create; Result.StretchFrom(ABitmapSurface, AWidth, AHeight, ABitmapSurface.PixelFormat); finally ABitmapSurface.Free; end; end else Result:=ABitmapSurface; end; procedure TBitmapAsyncLoader.SynchronizeAssignFromBitmapSurface(const ABitmapSurface : TBitmapSurface; const AListItemImage : TListItemImage = nil); begin TThread.Synchronize(Nil, procedure begin Assign(ABitmapSurface); ABitmapSurface.Free; if Assigned(AListItemImage) then AListItemImage.Invalidate; end ); end; procedure TBitmapAsyncLoader.StartHTTPThread(const AURL : String; const AWidth, AHeight : Integer; const AListItemImage : TListItemImage = nil); begin AHTTPClient.BeginGet( procedure (const ASyncResult: IAsyncResult) var AHTTPResponse : IHTTPResponse; ABitmapSurface : TBitmapSurface; begin if Not ASyncResult.IsCompleted then exit; try AHTTPResponse:=THTTPClient.EndAsyncHTTP(ASyncResult); except exit; end; if Assigned(AHTTPResponse) and (AHTTPResponse.StatusCode = 200) then begin ABitmapSurface:=TBitmapSurface.Create; if TBitmapCodecManager.LoadFromStream(AHTTPResponse.ContentStream, ABitmapSurface, Self.CanvasClass.GetAttribute(TCanvasAttribute.MaxBitmapSize)) then begin ABitmapSurface:=ResizeBitmapSurface(ABitmapSurface, AWidth, AHeight); SynchronizeAssignFromBitmapSurface(ABitmapSurface, AListItemImage); end; end; end, AURL ); end; class procedure THTTPClientListener.HTTPClientValidateServerCertificate(const Sender: TObject; const ARequest: TURLRequest; const Certificate: TCertificate; var Accepted: Boolean); begin Accepted:=True; end; initialization AHTTPClient:=THTTPClient.Create; AHTTPClient.OnValidateServerCertificate:=THTTPClientListener.HTTPClientValidateServerCertificate; finalization if Assigned(AHTTPClient) then AHTTPClient.DisposeOf; end.
BitmapAsyncLoader.7z
-
Евгений Корепов получил реакцию от qz5 в Helper для TBitmap - асинхронная загрузка картинки из URL
Окончательный вариант. Долго бился с изменением размера картинки внутри потока (к примеру что бы в ListView не грузить картинки больше чем нужно). Средствами TBitmap это оказалось сделать невозможно (именно в потоке), чтение форумов, issue Эмбаркадеро, привело к туманному выводу что проблема в архитектуре FMX. В Токио, TBitmap стал потокобезопасным - это означает что никогда не пытайтесь использовать Bitmap в потоке, рано или поздно получите артефакты и глюки.
Я решил проблему отказавшись от работы с TBitmap в потоке, и использовав для этого TBitmapSurface (загрузка из stream, изменение размеров).
Что умеет хелпер:
1. Загрузка картинки в Bitmap и подгонкой размера (размер можно и не менять - не передавайте параметр ASize)
procedure LoadFromURLAsync(const AURL : String; const ASize : TControlSize = nil); overload;
2. Загрузка картинки в TListItemImage ListView. После Окончания загрузки хелпер выполнит AListItemImage.Invalidate в основном потоке приложения для отрисовки картинки.
procedure LoadFromURLAsync(const AURL : String; const AListItemImage : TListItemImage); overload;
3. Создание картинки. Тоже самое что и предыдущие, но можно сэкономить строчку кода ABitmap:=TBitmap.Create ?
constructor CreateFromUrlAsync(const AURL : String; const AListItemImage : TListItemImage = nil);
По картинкам в ListView - можете на свой страх и риск грузить 100500 картинок, но лучше использовать загрузку только для видимой части (+- еще сколько то итемов). Хелпер тупо грузит картинки и не обеспечивает механизм оптимальной загрузки.
Протестировано на Windows и Android.
Ответы на вопросы которые мне задавали:
Почему не создавать отдельный поток для каждой картинки, в нем создавать THTTPClient и делать запрос - пробовал этот вариант, он медленнее на порядок, даже под виндой это чертовски медленно. AHTTPClient.BeginGet и так создает отдельный поток на каждый запрос. Почему AHTTPClient глобальный для юнита - в хелпере нельзя вводить свои переменные, а создавать AHTTPClient внутри функции не выйдет - он убьется до завершения потока. И текущий вариант быстрее. Код хелпера и архив с тестовым проектом:
unit BitmapAsyncLoader; interface uses System.Net.HttpClient, System.Net.URLClient, System.SysUtils, System.Types, System.Classes, FMX.Graphics, FMX.Surfaces, FMX.Types, FMX.ListView.Types; type TBitmapAsyncLoader = class helper for TBitmap private function ResizeBitmapSurface(const ABitmapSurface : TBitmapSurface; const AWidth, AHeight : Integer) : TBitmapSurface; procedure SynchronizeAssignFromBitmapSurface(const ABitmapSurface : TBitmapSurface; const AListItemImage : TListItemImage = nil); procedure StartHTTPThread(const AURL : String; const AWidth, AHeight : Integer; const AListItemImage : TListItemImage = nil); public procedure LoadFromURLAsync(const AURL : String; const ASize : TControlSize = nil); overload; procedure LoadFromURLAsync(const AURL : String; const AListItemImage : TListItemImage); overload; constructor CreateFromUrlAsync(const AURL : String; const AListItemImage : TListItemImage = nil); end; implementation type THTTPClientListener = class class procedure HTTPClientValidateServerCertificate(const Sender: TObject; const ARequest: TURLRequest; const Certificate: TCertificate; var Accepted: Boolean); end; var AHTTPClient : THTTPClient; constructor TBitmapAsyncLoader.CreateFromUrlAsync(const AURL : String; const AListItemImage : TListItemImage = nil); begin Create; LoadFromURLAsync(AURL, AListItemImage); end; procedure TBitmapAsyncLoader.LoadFromURLAsync(const AURL : String; const AListItemImage : TListItemImage); var AWidth, AHeight : Integer; begin if Assigned(AListItemImage) then begin AWidth:=Round(AListItemImage.Width); AHeight:=Round(AListItemImage.Height); end else begin AWidth:=-1; AHeight:=-1; end; StartHTTPThread(AURL, AWidth, AHeight, AListItemImage); end; procedure TBitmapAsyncLoader.LoadFromURLAsync(const AURL : String; const ASize : TControlSize = nil); var AWidth, AHeight : Integer; begin if Assigned(ASize) then begin AWidth:=Round(ASize.Width); AHeight:=Round(ASize.Height); end else begin AWidth:=-1; AHeight:=-1; end; StartHTTPThread(AURL, AWidth, AHeight); end; function TBitmapAsyncLoader.ResizeBitmapSurface(const ABitmapSurface : TBitmapSurface; const AWidth, AHeight : Integer) : TBitmapSurface; begin if (AWidth <> -1) and (AHeight <> -1) then begin try Result:=TBitmapSurface.Create; Result.StretchFrom(ABitmapSurface, AWidth, AHeight, ABitmapSurface.PixelFormat); finally ABitmapSurface.Free; end; end else Result:=ABitmapSurface; end; procedure TBitmapAsyncLoader.SynchronizeAssignFromBitmapSurface(const ABitmapSurface : TBitmapSurface; const AListItemImage : TListItemImage = nil); begin TThread.Synchronize(Nil, procedure begin Assign(ABitmapSurface); ABitmapSurface.Free; if Assigned(AListItemImage) then AListItemImage.Invalidate; end ); end; procedure TBitmapAsyncLoader.StartHTTPThread(const AURL : String; const AWidth, AHeight : Integer; const AListItemImage : TListItemImage = nil); begin AHTTPClient.BeginGet( procedure (const ASyncResult: IAsyncResult) var AHTTPResponse : IHTTPResponse; ABitmapSurface : TBitmapSurface; begin if Not ASyncResult.IsCompleted then exit; try AHTTPResponse:=THTTPClient.EndAsyncHTTP(ASyncResult); except exit; end; if Assigned(AHTTPResponse) and (AHTTPResponse.StatusCode = 200) then begin ABitmapSurface:=TBitmapSurface.Create; if TBitmapCodecManager.LoadFromStream(AHTTPResponse.ContentStream, ABitmapSurface, Self.CanvasClass.GetAttribute(TCanvasAttribute.MaxBitmapSize)) then begin ABitmapSurface:=ResizeBitmapSurface(ABitmapSurface, AWidth, AHeight); SynchronizeAssignFromBitmapSurface(ABitmapSurface, AListItemImage); end; end; end, AURL ); end; class procedure THTTPClientListener.HTTPClientValidateServerCertificate(const Sender: TObject; const ARequest: TURLRequest; const Certificate: TCertificate; var Accepted: Boolean); begin Accepted:=True; end; initialization AHTTPClient:=THTTPClient.Create; AHTTPClient.OnValidateServerCertificate:=THTTPClientListener.HTTPClientValidateServerCertificate; finalization if Assigned(AHTTPClient) then AHTTPClient.DisposeOf; end.
BitmapAsyncLoader.7z
-
Евгений Корепов получил реакцию от Barbanel в Работа с камерой - Rad 10.3
Обратите внимание к какой версии документации вы обращаетесь:
http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Taking_Pictures_Using_FireMonkey_Interfaces
http://docwiki.embarcadero.com/RADStudio/XE5/en/Mobile_Tutorial:_Taking_and_Sharing_a_Picture_(iOS_and_Android)
Актуальная документация для 10.3
http://docwiki.embarcadero.com/RADStudio/Rio/en/Taking_Pictures_Using_FireMonkey_Interfaces
Разрешение на доступ Secure File Sharing возможно не понадобится если уберете галочку:
-
Евгений Корепов получил реакцию от PowerOwl в Helper для TBitmap - асинхронная загрузка картинки из URL
Пока тестировал хелпер в боевом проекте он потихоньку оброс исрпавлениями/улучшениями:
Загрузка из потока сделана через TBitmapSurface - это позволяет избежать множества глюков. LoadFromStream вынесен из Synchronize (основного потока) в поток HTTPClient - по результатам бенчмарка операция оказалась самая жручая. После исправления интерфейс перестал залипать совсем. Добавлен overload вариант с передачей в процедуру TListItemImage - для использования в TListView и корректной перерисовки подгруженных картинок через AListItemImage.Invalidate. unit BitmapAsyncLoader; interface uses FMX.Graphics, FMX.Surfaces, System.Net.HttpClient, System.Types, System.Classes, FMX.ListView.Types, FMX.ListView.Appearances; type TBitmapAsyncLoader = class helper for TBitmap procedure LoadFromURLAsync(const AUrl : String); overload; procedure LoadFromURLAsync(const AUrl : String; const AListItemImage : TListItemImage); overload; end; implementation var AHTTPClient : THTTPClient; procedure TBitmapAsyncLoader.LoadFromURLAsync(const AURL : String); begin try AHTTPClient.BeginGet( procedure (const ASyncResult: IAsyncResult) var AHTTPResponse : IHTTPResponse; ABitmapSurface : TBitmapSurface; begin if Not ASyncResult.IsCompleted then exit; try AHTTPResponse:=THTTPClient.EndAsyncHTTP(ASyncResult); except end; if Assigned(AHTTPResponse) and (AHTTPResponse.StatusCode = 200) then begin ABitmapSurface:=TBitmapSurface.Create; if TBitmapCodecManager.LoadFromStream(AHTTPResponse.ContentStream, ABitmapSurface, CanvasClass.GetAttribute(TCanvasAttribute.MaxBitmapSize)) then TThread.Synchronize(Nil, procedure begin if Assigned(Self)then Assign(ABitmapSurface); ABitmapSurface.Free; end ) else ABitmapSurface.Free; end; end, AURL ); except end; end; procedure TBitmapAsyncLoader.LoadFromURLAsync(const AURL : String; const AListItemImage : TListItemImage); begin try AHTTPClient.BeginGet( procedure (const ASyncResult: IAsyncResult) var AHTTPResponse : IHTTPResponse; ABitmapSurface : TBitmapSurface; begin if Not ASyncResult.IsCompleted then exit; try AHTTPResponse:=THTTPClient.EndAsyncHTTP(ASyncResult); except end; if Assigned(AHTTPResponse) and (AHTTPResponse.StatusCode = 200) then begin ABitmapSurface:=TBitmapSurface.Create; if TBitmapCodecManager.LoadFromStream(AHTTPResponse.ContentStream, ABitmapSurface, CanvasClass.GetAttribute(TCanvasAttribute.MaxBitmapSize)) then TThread.Synchronize(Nil, procedure begin if Assigned(Self) and Assigned(AListItemImage) then begin AListItemImage.BeginUpdate; Assign(ABitmapSurface); AListItemImage.Invalidate; AListItemImage.EndUpdate; end; ABitmapSurface.Free; end ) else ABitmapSurface.Free; end; end, AURL ); except end; end; initialization AHTTPClient:=THTTPClient.Create; finalization if Assigned(AHTTPClient) then AHTTPClient.DisposeOf; end. Тестовый проект, на этот раз с ListView (по кнопке добавляется 100 итемов) прилагаю.
BitmapAsyncLoaderListView.7z
-
Евгений Корепов получил реакцию от dnekrasov в Helper для TBitmap - асинхронная загрузка картинки из URL
Пока тестировал хелпер в боевом проекте он потихоньку оброс исрпавлениями/улучшениями:
Загрузка из потока сделана через TBitmapSurface - это позволяет избежать множества глюков. LoadFromStream вынесен из Synchronize (основного потока) в поток HTTPClient - по результатам бенчмарка операция оказалась самая жручая. После исправления интерфейс перестал залипать совсем. Добавлен overload вариант с передачей в процедуру TListItemImage - для использования в TListView и корректной перерисовки подгруженных картинок через AListItemImage.Invalidate. unit BitmapAsyncLoader; interface uses FMX.Graphics, FMX.Surfaces, System.Net.HttpClient, System.Types, System.Classes, FMX.ListView.Types, FMX.ListView.Appearances; type TBitmapAsyncLoader = class helper for TBitmap procedure LoadFromURLAsync(const AUrl : String); overload; procedure LoadFromURLAsync(const AUrl : String; const AListItemImage : TListItemImage); overload; end; implementation var AHTTPClient : THTTPClient; procedure TBitmapAsyncLoader.LoadFromURLAsync(const AURL : String); begin try AHTTPClient.BeginGet( procedure (const ASyncResult: IAsyncResult) var AHTTPResponse : IHTTPResponse; ABitmapSurface : TBitmapSurface; begin if Not ASyncResult.IsCompleted then exit; try AHTTPResponse:=THTTPClient.EndAsyncHTTP(ASyncResult); except end; if Assigned(AHTTPResponse) and (AHTTPResponse.StatusCode = 200) then begin ABitmapSurface:=TBitmapSurface.Create; if TBitmapCodecManager.LoadFromStream(AHTTPResponse.ContentStream, ABitmapSurface, CanvasClass.GetAttribute(TCanvasAttribute.MaxBitmapSize)) then TThread.Synchronize(Nil, procedure begin if Assigned(Self)then Assign(ABitmapSurface); ABitmapSurface.Free; end ) else ABitmapSurface.Free; end; end, AURL ); except end; end; procedure TBitmapAsyncLoader.LoadFromURLAsync(const AURL : String; const AListItemImage : TListItemImage); begin try AHTTPClient.BeginGet( procedure (const ASyncResult: IAsyncResult) var AHTTPResponse : IHTTPResponse; ABitmapSurface : TBitmapSurface; begin if Not ASyncResult.IsCompleted then exit; try AHTTPResponse:=THTTPClient.EndAsyncHTTP(ASyncResult); except end; if Assigned(AHTTPResponse) and (AHTTPResponse.StatusCode = 200) then begin ABitmapSurface:=TBitmapSurface.Create; if TBitmapCodecManager.LoadFromStream(AHTTPResponse.ContentStream, ABitmapSurface, CanvasClass.GetAttribute(TCanvasAttribute.MaxBitmapSize)) then TThread.Synchronize(Nil, procedure begin if Assigned(Self) and Assigned(AListItemImage) then begin AListItemImage.BeginUpdate; Assign(ABitmapSurface); AListItemImage.Invalidate; AListItemImage.EndUpdate; end; ABitmapSurface.Free; end ) else ABitmapSurface.Free; end; end, AURL ); except end; end; initialization AHTTPClient:=THTTPClient.Create; finalization if Assigned(AHTTPClient) then AHTTPClient.DisposeOf; end. Тестовый проект, на этот раз с ListView (по кнопке добавляется 100 итемов) прилагаю.
BitmapAsyncLoaderListView.7z
-
Евгений Корепов получил реакцию от Martifan в Helper для TBitmap - асинхронная загрузка картинки из URL
Пока тестировал хелпер в боевом проекте он потихоньку оброс исрпавлениями/улучшениями:
Загрузка из потока сделана через TBitmapSurface - это позволяет избежать множества глюков. LoadFromStream вынесен из Synchronize (основного потока) в поток HTTPClient - по результатам бенчмарка операция оказалась самая жручая. После исправления интерфейс перестал залипать совсем. Добавлен overload вариант с передачей в процедуру TListItemImage - для использования в TListView и корректной перерисовки подгруженных картинок через AListItemImage.Invalidate. unit BitmapAsyncLoader; interface uses FMX.Graphics, FMX.Surfaces, System.Net.HttpClient, System.Types, System.Classes, FMX.ListView.Types, FMX.ListView.Appearances; type TBitmapAsyncLoader = class helper for TBitmap procedure LoadFromURLAsync(const AUrl : String); overload; procedure LoadFromURLAsync(const AUrl : String; const AListItemImage : TListItemImage); overload; end; implementation var AHTTPClient : THTTPClient; procedure TBitmapAsyncLoader.LoadFromURLAsync(const AURL : String); begin try AHTTPClient.BeginGet( procedure (const ASyncResult: IAsyncResult) var AHTTPResponse : IHTTPResponse; ABitmapSurface : TBitmapSurface; begin if Not ASyncResult.IsCompleted then exit; try AHTTPResponse:=THTTPClient.EndAsyncHTTP(ASyncResult); except end; if Assigned(AHTTPResponse) and (AHTTPResponse.StatusCode = 200) then begin ABitmapSurface:=TBitmapSurface.Create; if TBitmapCodecManager.LoadFromStream(AHTTPResponse.ContentStream, ABitmapSurface, CanvasClass.GetAttribute(TCanvasAttribute.MaxBitmapSize)) then TThread.Synchronize(Nil, procedure begin if Assigned(Self)then Assign(ABitmapSurface); ABitmapSurface.Free; end ) else ABitmapSurface.Free; end; end, AURL ); except end; end; procedure TBitmapAsyncLoader.LoadFromURLAsync(const AURL : String; const AListItemImage : TListItemImage); begin try AHTTPClient.BeginGet( procedure (const ASyncResult: IAsyncResult) var AHTTPResponse : IHTTPResponse; ABitmapSurface : TBitmapSurface; begin if Not ASyncResult.IsCompleted then exit; try AHTTPResponse:=THTTPClient.EndAsyncHTTP(ASyncResult); except end; if Assigned(AHTTPResponse) and (AHTTPResponse.StatusCode = 200) then begin ABitmapSurface:=TBitmapSurface.Create; if TBitmapCodecManager.LoadFromStream(AHTTPResponse.ContentStream, ABitmapSurface, CanvasClass.GetAttribute(TCanvasAttribute.MaxBitmapSize)) then TThread.Synchronize(Nil, procedure begin if Assigned(Self) and Assigned(AListItemImage) then begin AListItemImage.BeginUpdate; Assign(ABitmapSurface); AListItemImage.Invalidate; AListItemImage.EndUpdate; end; ABitmapSurface.Free; end ) else ABitmapSurface.Free; end; end, AURL ); except end; end; initialization AHTTPClient:=THTTPClient.Create; finalization if Assigned(AHTTPClient) then AHTTPClient.DisposeOf; end. Тестовый проект, на этот раз с ListView (по кнопке добавляется 100 итемов) прилагаю.
BitmapAsyncLoaderListView.7z
-
Евгений Корепов получил реакцию от Barbanel в Helper для TBitmap - асинхронная загрузка картинки из URL
Пока тестировал хелпер в боевом проекте он потихоньку оброс исрпавлениями/улучшениями:
Загрузка из потока сделана через TBitmapSurface - это позволяет избежать множества глюков. LoadFromStream вынесен из Synchronize (основного потока) в поток HTTPClient - по результатам бенчмарка операция оказалась самая жручая. После исправления интерфейс перестал залипать совсем. Добавлен overload вариант с передачей в процедуру TListItemImage - для использования в TListView и корректной перерисовки подгруженных картинок через AListItemImage.Invalidate. unit BitmapAsyncLoader; interface uses FMX.Graphics, FMX.Surfaces, System.Net.HttpClient, System.Types, System.Classes, FMX.ListView.Types, FMX.ListView.Appearances; type TBitmapAsyncLoader = class helper for TBitmap procedure LoadFromURLAsync(const AUrl : String); overload; procedure LoadFromURLAsync(const AUrl : String; const AListItemImage : TListItemImage); overload; end; implementation var AHTTPClient : THTTPClient; procedure TBitmapAsyncLoader.LoadFromURLAsync(const AURL : String); begin try AHTTPClient.BeginGet( procedure (const ASyncResult: IAsyncResult) var AHTTPResponse : IHTTPResponse; ABitmapSurface : TBitmapSurface; begin if Not ASyncResult.IsCompleted then exit; try AHTTPResponse:=THTTPClient.EndAsyncHTTP(ASyncResult); except end; if Assigned(AHTTPResponse) and (AHTTPResponse.StatusCode = 200) then begin ABitmapSurface:=TBitmapSurface.Create; if TBitmapCodecManager.LoadFromStream(AHTTPResponse.ContentStream, ABitmapSurface, CanvasClass.GetAttribute(TCanvasAttribute.MaxBitmapSize)) then TThread.Synchronize(Nil, procedure begin if Assigned(Self)then Assign(ABitmapSurface); ABitmapSurface.Free; end ) else ABitmapSurface.Free; end; end, AURL ); except end; end; procedure TBitmapAsyncLoader.LoadFromURLAsync(const AURL : String; const AListItemImage : TListItemImage); begin try AHTTPClient.BeginGet( procedure (const ASyncResult: IAsyncResult) var AHTTPResponse : IHTTPResponse; ABitmapSurface : TBitmapSurface; begin if Not ASyncResult.IsCompleted then exit; try AHTTPResponse:=THTTPClient.EndAsyncHTTP(ASyncResult); except end; if Assigned(AHTTPResponse) and (AHTTPResponse.StatusCode = 200) then begin ABitmapSurface:=TBitmapSurface.Create; if TBitmapCodecManager.LoadFromStream(AHTTPResponse.ContentStream, ABitmapSurface, CanvasClass.GetAttribute(TCanvasAttribute.MaxBitmapSize)) then TThread.Synchronize(Nil, procedure begin if Assigned(Self) and Assigned(AListItemImage) then begin AListItemImage.BeginUpdate; Assign(ABitmapSurface); AListItemImage.Invalidate; AListItemImage.EndUpdate; end; ABitmapSurface.Free; end ) else ABitmapSurface.Free; end; end, AURL ); except end; end; initialization AHTTPClient:=THTTPClient.Create; finalization if Assigned(AHTTPClient) then AHTTPClient.DisposeOf; end. Тестовый проект, на этот раз с ListView (по кнопке добавляется 100 итемов) прилагаю.
BitmapAsyncLoaderListView.7z
-
Евгений Корепов получил реакцию от Anatoliy в Helper для TBitmap - асинхронная загрузка картинки из URL
Пока тестировал хелпер в боевом проекте он потихоньку оброс исрпавлениями/улучшениями:
Загрузка из потока сделана через TBitmapSurface - это позволяет избежать множества глюков. LoadFromStream вынесен из Synchronize (основного потока) в поток HTTPClient - по результатам бенчмарка операция оказалась самая жручая. После исправления интерфейс перестал залипать совсем. Добавлен overload вариант с передачей в процедуру TListItemImage - для использования в TListView и корректной перерисовки подгруженных картинок через AListItemImage.Invalidate. unit BitmapAsyncLoader; interface uses FMX.Graphics, FMX.Surfaces, System.Net.HttpClient, System.Types, System.Classes, FMX.ListView.Types, FMX.ListView.Appearances; type TBitmapAsyncLoader = class helper for TBitmap procedure LoadFromURLAsync(const AUrl : String); overload; procedure LoadFromURLAsync(const AUrl : String; const AListItemImage : TListItemImage); overload; end; implementation var AHTTPClient : THTTPClient; procedure TBitmapAsyncLoader.LoadFromURLAsync(const AURL : String); begin try AHTTPClient.BeginGet( procedure (const ASyncResult: IAsyncResult) var AHTTPResponse : IHTTPResponse; ABitmapSurface : TBitmapSurface; begin if Not ASyncResult.IsCompleted then exit; try AHTTPResponse:=THTTPClient.EndAsyncHTTP(ASyncResult); except end; if Assigned(AHTTPResponse) and (AHTTPResponse.StatusCode = 200) then begin ABitmapSurface:=TBitmapSurface.Create; if TBitmapCodecManager.LoadFromStream(AHTTPResponse.ContentStream, ABitmapSurface, CanvasClass.GetAttribute(TCanvasAttribute.MaxBitmapSize)) then TThread.Synchronize(Nil, procedure begin if Assigned(Self)then Assign(ABitmapSurface); ABitmapSurface.Free; end ) else ABitmapSurface.Free; end; end, AURL ); except end; end; procedure TBitmapAsyncLoader.LoadFromURLAsync(const AURL : String; const AListItemImage : TListItemImage); begin try AHTTPClient.BeginGet( procedure (const ASyncResult: IAsyncResult) var AHTTPResponse : IHTTPResponse; ABitmapSurface : TBitmapSurface; begin if Not ASyncResult.IsCompleted then exit; try AHTTPResponse:=THTTPClient.EndAsyncHTTP(ASyncResult); except end; if Assigned(AHTTPResponse) and (AHTTPResponse.StatusCode = 200) then begin ABitmapSurface:=TBitmapSurface.Create; if TBitmapCodecManager.LoadFromStream(AHTTPResponse.ContentStream, ABitmapSurface, CanvasClass.GetAttribute(TCanvasAttribute.MaxBitmapSize)) then TThread.Synchronize(Nil, procedure begin if Assigned(Self) and Assigned(AListItemImage) then begin AListItemImage.BeginUpdate; Assign(ABitmapSurface); AListItemImage.Invalidate; AListItemImage.EndUpdate; end; ABitmapSurface.Free; end ) else ABitmapSurface.Free; end; end, AURL ); except end; end; initialization AHTTPClient:=THTTPClient.Create; finalization if Assigned(AHTTPClient) then AHTTPClient.DisposeOf; end. Тестовый проект, на этот раз с ListView (по кнопке добавляется 100 итемов) прилагаю.
BitmapAsyncLoaderListView.7z
-
Евгений Корепов получил реакцию от PowerOwl в Helper для TBitmap - асинхронная загрузка картинки из URL
Для одного своего проекта сделал, поделюсь, вдруг кому пригодится. Тестировал под Windows и Android.
Для использования просто добавьте BitmapAsyncLoader в uses, а дальше все просто:
ImageControl.Bitmap.LoadFromURLAsync('https://bipbap.ru/wp-content/uploads/2017/10/0_8eb56_842bba74_XL-640x400.jpg'); Код юнита хелпера:
unit BitmapAsyncLoader; interface uses FMX.Graphics, System.Net.HttpClient, System.Types, System.Classes; type TBitmapAsyncLoader = class helper for TBitmap procedure LoadFromUrlAsync(const AUrl : String); end; implementation var AHTTPClient : THTTPClient; procedure TBitmapAsyncLoader.LoadFromURLAsync(const AURL : String); begin try AHTTPClient.BeginGet( procedure (const ASyncResult: IAsyncResult) var AHTTPResponse : IHTTPResponse; begin if Not ASyncResult.IsCompleted then exit; try AHTTPResponse:=THTTPClient.EndAsyncHTTP(ASyncResult); except end; if Assigned(AHTTPResponse) and (AHTTPResponse.StatusCode = 200) then TThread.Synchronize(Nil, procedure begin try Self.LoadFromStream(AHTTPResponse.ContentStream); except end; end ); end, AURL ); except end; end; initialization AHTTPClient:=THTTPClient.Create; finalization if Assigned(AHTTPClient) then begin AHTTPClient.DisposeOf; end; end. Архив с тестовым проектом прилагаю.
BitmapAsyncLoader.7z
-
Евгений Корепов получил реакцию от #WAMACO в Helper для TBitmap - асинхронная загрузка картинки из URL
Для одного своего проекта сделал, поделюсь, вдруг кому пригодится. Тестировал под Windows и Android.
Для использования просто добавьте BitmapAsyncLoader в uses, а дальше все просто:
ImageControl.Bitmap.LoadFromURLAsync('https://bipbap.ru/wp-content/uploads/2017/10/0_8eb56_842bba74_XL-640x400.jpg'); Код юнита хелпера:
unit BitmapAsyncLoader; interface uses FMX.Graphics, System.Net.HttpClient, System.Types, System.Classes; type TBitmapAsyncLoader = class helper for TBitmap procedure LoadFromUrlAsync(const AUrl : String); end; implementation var AHTTPClient : THTTPClient; procedure TBitmapAsyncLoader.LoadFromURLAsync(const AURL : String); begin try AHTTPClient.BeginGet( procedure (const ASyncResult: IAsyncResult) var AHTTPResponse : IHTTPResponse; begin if Not ASyncResult.IsCompleted then exit; try AHTTPResponse:=THTTPClient.EndAsyncHTTP(ASyncResult); except end; if Assigned(AHTTPResponse) and (AHTTPResponse.StatusCode = 200) then TThread.Synchronize(Nil, procedure begin try Self.LoadFromStream(AHTTPResponse.ContentStream); except end; end ); end, AURL ); except end; end; initialization AHTTPClient:=THTTPClient.Create; finalization if Assigned(AHTTPClient) then begin AHTTPClient.DisposeOf; end; end. Архив с тестовым проектом прилагаю.
BitmapAsyncLoader.7z
-
Евгений Корепов получил реакцию от Anatoliy в [РЕШЕНО]: Как отловить кнопки пульта ДУ
Спасибо! Посмотрел diff - как и предполагал, Эмбаркадера поленилась скопипастить все коды, видимо в буфер обмена у них влезла только часть ? Ну как так то?
Спасибо вам за проделанную работу!
-
Евгений Корепов отреагировална Dmitry_4501 в [РЕШЕНО]: Как отловить кнопки пульта ДУ
В общем удалось решить проблему с кнопками пульта. Теперь приложение распознает все кнопки с пульта. Если кому-то нужно, прикрепил архив. (Delphi 10.3.1 Rio)
fmx_androidkey_fix.zip
-
Евгений Корепов получил реакцию от Alex7wrt в Подключение Admob к iOS
Я еще тупее чем думал. После нажатия "Update Local File Cache" все фреймворки подсосались сами с Mac mini ))))))))))))))
-
Евгений Корепов получил реакцию от Barbanel в Подключение Admob к iOS
Я еще тупее чем думал. После нажатия "Update Local File Cache" все фреймворки подсосались сами с Mac mini ))))))))))))))
-
Евгений Корепов отреагировална Alex7wrt в Подключение Admob к iOS
Получилось, но только с подключением дополнительных фреймворков. Использовал JVEsuite.
-
Евгений Корепов получил реакцию от Barbanel в как очистить память клавиатуры
Нашел еще более изящный способ, без исчезновения/показа клавиатуры:
procedure TForm1.Button1Click(Sender: TObject); begin Memo1.SelectAll; Memo1.DeleteSelection; end;
-
Евгений Корепов получил реакцию от Barbanel в Версия андроид
К сожалению нисколько. Тут просто здравый смысл - вы приобретаете телефон хорошего качества, с нормальной гарантией и сервисом. Программа Android One гарантирует вам обновления (ежемесячные обновления и обновления OS) - это значит что устройство сможет помогать вам в разработке не один год. В последствии вы сможете продать устройство за приемлемые деньги, а не за 300 рублей (как раз по такой цене я распродал мусор Dexp купленный изначально, 4 года назад, по неопытности). Вот и все аргументы, никакого скрытого умысла.
P.S. Добавлю - один из телефонов Dexp взорвался у меня прямо на столе. Чудом не сгорел рабочий кабинет, но часть рабочих бумаг погорело, слава богу все само потухло. Меня на месте в этот момент не было, телефон был подключен к компу - до этого занимался отладкой одного из приложений. Телефон превратился в оплавленный комок пластмассы. ))
-
Евгений Корепов получил реакцию от Anatoliy в Версия андроид
К сожалению нисколько. Тут просто здравый смысл - вы приобретаете телефон хорошего качества, с нормальной гарантией и сервисом. Программа Android One гарантирует вам обновления (ежемесячные обновления и обновления OS) - это значит что устройство сможет помогать вам в разработке не один год. В последствии вы сможете продать устройство за приемлемые деньги, а не за 300 рублей (как раз по такой цене я распродал мусор Dexp купленный изначально, 4 года назад, по неопытности). Вот и все аргументы, никакого скрытого умысла.
P.S. Добавлю - один из телефонов Dexp взорвался у меня прямо на столе. Чудом не сгорел рабочий кабинет, но часть рабочих бумаг погорело, слава богу все само потухло. Меня на месте в этот момент не было, телефон был подключен к компу - до этого занимался отладкой одного из приложений. Телефон превратился в оплавленный комок пластмассы. ))
-
Евгений Корепов получил реакцию от Tumaso в как очистить память клавиатуры
Нашел еще более изящный способ, без исчезновения/показа клавиатуры:
procedure TForm1.Button1Click(Sender: TObject); begin Memo1.SelectAll; Memo1.DeleteSelection; end;
-
Евгений Корепов получил реакцию от Martifan в как очистить память клавиатуры
Нашел еще более изящный способ, без исчезновения/показа клавиатуры:
procedure TForm1.Button1Click(Sender: TObject); begin Memo1.SelectAll; Memo1.DeleteSelection; end;
-
Евгений Корепов получил реакцию от Anatoliy в как очистить память клавиатуры
Ага, теперь нормально воспроизводится.
Самый простой способ избежать этого, вот такой:
procedure TForm1.Button1Click(Sender: TObject); begin Memo1.Text:=EmptyStr; Memo1.ResetFocus; Memo1.SetFocus; end; Правда будет видно что виртуальная клавиатура исчезает, а затем опять появляется.
-
Евгений Корепов получил реакцию от Anatoliy в Wifi Multicast packets
Я использую для такого Indy, там все готовенькое, в том числе и TIdUDPServer со всем нужным вам функционалом (и потоками).
Вот в теме немного обсуждения
-
Евгений Корепов отреагировална Vitaldj в Версия андроид
Поосторожнее на поворотах. Тут спросили, Евгений ответил. Причём тут реклама?
-
Евгений Корепов отреагировална qz5 в Не работает JS в Android
или вот так
document.getElementById('PR_FIO').setAttribute('value','test');