Евгений Корепов
-
Постов
738 -
Зарегистрирован
-
Посещение
-
Победитель дней
100
Активность репутации
-
Евгений Корепов отреагировална Barbanel в Выполнение операции после скрытия меню
Процедура Invalidate не перерисовывает форму немедленно, она лишь добавляет сообщение на перерисовку в очередь сообщений.
Соотв. форма перерисуется когда не будет других событий в очереди, в данном случае - после всех длительных операций или после вызова .ProcessMessages
-
Евгений Корепов отреагировална krapotkin в Выполнение операции после скрытия меню
есть более интересный и по-мне, более правильный способ
по нажатию в меню не сразу выполняете что-то, а ЗАПИСЫВАЕТЕ действие, которое ВЫПОЛНИТСЯ по событию закрытия мультивью
тогда реакция наступает визуально правильная и дальше крутите колеса, запускайте потоки и все такое
-
-
Евгений Корепов получил реакцию от Wolfone в Работа с камерой - Rad 10.3
Вот кусок кода из боевого приложения, достаточно разрешения на доступ к камере, больше никаких разрешений не надо. Надеюсь вам поможет.
FScanCamera : TCameraComponent;
FScanManager - не обращайте внимания, это распознавание QR кодов.
ImageCamera : TImage; - картинка на форме где отображаются кадры с камеры
Работа начинается с ScanStart()
{$IFDEF MOBILE} // *************************************************************** // ****** Сканирование QR кода ****** // *************************************************************** procedure TFormMain.ScanStart(); var AppEventSvc: IFMXApplicationEventService; APermissionCamera : String; begin if TPlatformServices.Current.SupportsPlatformService(IFMXApplicationEventService, IInterface(AppEventSvc)) then AppEventSvc.SetApplicationEventHandler(AppEvent); FScanFrameTake := 0; CButtonScan.Text:='Отменить сканирование'; {$IFDEF IOS} ScanStarting(); {$ENDIF IOS} {$IFDEF ANDROID} APermissionCamera := JStringToString(TJManifest_permission.JavaClass.CAMERA); PermissionsService.RequestPermissions([APermissionCamera], CameraPermissionRequestResult, ExplainReason); {$ENDIF ANDROID} end; procedure TFormMain.ScanStop(); begin if Assigned(FScanCamera) then begin if FScanCamera.Active then FScanCamera.Active:=False; FScanCamera.Free; end; { if Assigned(FScanManager) then begin FScanManager.Free; end; } FScanInProgress := false; LayoutCamera.Height:=0; CButtonScan.Text:='Сканировать QR-код'; end; {$IFDEF ANDROID} procedure TFormMain.CameraPermissionRequestResult(Sender: TObject; const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>); begin if (Length(AGrantResults) = 1) and (AGrantResults[0] = TPermissionStatus.Granted) then begin ScanStarting(); end else TDialogService.ShowMessage('Сканирование QR-кода не возможно, требуемое разрешение не было дано') end; procedure TFormMain.ExplainReason(Sender: TObject; const APermissions: TArray<string>; const APostRationaleProc: TProc); begin TDialogService.ShowMessage('Приложению нужен доступ к камере для сканирования QR-кода ...', procedure(const AResult: TModalResult) begin APostRationaleProc; end) end; {$ENDIF ANDROID} procedure TFormMain.ScanStarting(); begin FScanInProgress := false; if Not Assigned(FScanManager) then FScanManager:= TScanManager.Create(TBarcodeFormat.QR_CODE, nil); if Not Assigned(FScanCamera) then FScanCamera:=TCameraComponent.Create(Self); FScanCamera.OnSampleBufferReady:=ScanCameraSampleBufferReady; FScanCamera.Quality := FMX.Media.TVideoCaptureQuality.MediumQuality; FScanCamera.Active := false; FScanCamera.Kind := FMX.Media.TCameraKind.BackCamera; FScanCamera.FocusMode := FMX.Media.TFocusMode.ContinuousAutoFocus; FScanCamera.Active := True; LayoutCamera.Height:=LayoutCamera.Width; end; { procedure TFormMain.btnStopCameraClick(Sender: TObject); begin end; } procedure TFormMain.ScanCameraSampleBufferReady(Sender: TObject; const ATime: TMediaTime); begin TThread.Synchronize(TThread.CurrentThread, GetImageCamera); end; procedure TFormMain.GetImageCamera; var scanBitmap: TBitmap; ReadResult: TReadResult; begin FScanCamera.SampleBufferToBitmap(ImageCamera.Bitmap, True); if (FScanInProgress) then exit; { This code will take every 4 frame. } Inc(FScanFrameTake); if (FScanFrameTake mod 4 <> 0) then exit; scanBitmap := TBitmap.Create(); scanBitmap.Assign(ImageCamera.Bitmap); ReadResult := nil; // There is bug in Delphi Berlin 10.1 update 2 which causes the TTask and // the TThread.Synchronize to cause exceptions. // See: https://quality.embarcadero.com/browse/RSP-16377?jql=project%20%3D%20RSP%20AND%20issuetype%20%3D%20Bug%20AND%20affectedVersion%20%3D%20%2210.1%20Berlin%20Update%202%22%20AND%20status%20%3D%20Open%20ORDER%20BY%20priority%20DESC TTask.Run( procedure begin try FScanInProgress := True; try ReadResult := FScanManager.Scan(scanBitmap); except on E: Exception do begin TThread.Synchronize(nil, procedure begin LabelAPIKey.Text := 'Ключ доступа : ' + E.Message; end); exit; end; end; TThread.Synchronize(nil, procedure begin if (ReadResult <> nil) then begin if ProcessingAPIKeyHex(ReadResult.Text) then begin ScanStop(); end; end; end); finally ReadResult.Free; scanBitmap.Free; FScanInProgress := false; end; end); end; { Make sure the camera is released if you're going away. } function TFormMain.AppEvent(AAppEvent: TApplicationEvent; AContext: TObject): Boolean; begin case AAppEvent of TApplicationEvent.WillBecomeInactive, TApplicationEvent.EnteredBackground, TApplicationEvent.WillTerminate: if Assigned(FScanCamera) then FScanCamera.Active := false; end; Result:=True; end;
-
Евгений Корепов отреагировална krapotkin в TBitmapData
Считалось, что до RIO TBitmap НЕ потокобезопасен. В RIO рапортовали, что справились с этим. Не помню, проверял кто или нет
Но TBitmapSurface был потокобезопасен уже тогда. Возможно, стоит готовить данные там, а потом просто отдавать в Bitmap
https://stackoverflow.com/questions/37602538/delphi-tbitmap-to-string-via-tbitmapsurface-and-back-to-tbitmap
https://stackoverflow.com/questions/51523321/how-to-draw-fmx-surface-tbitmapsurface-on-fmx-graphics-tbitmap/51526855
-
Евгений Корепов получил реакцию от Ingalime в [РЕШЕНО]: Как отловить кнопки пульта ДУ
Друзья, вы будете смеяться - Эмбаркадера наконец то обратила внимание на заявку по этой проблеме. Правда в стиле моя-твоя непонимай )))
https://quality.embarcadero.com/browse/RSP-16910
-
Евгений Корепов отреагировална CyberStorm в Умирает ли делфи? (С хабра)
Странно, что никто не обратил внимание на важную для всех делфистов статью на хабре:
https://habr.com/ru/post/481534/
-
Евгений Корепов получил реакцию от Barbanel в Логика авторизации по отпечатку пальца
В телефоне нет никаких гарантированных аппаратных данных - или по причине китайщины, или по тому что их можно изменить. Если уже вам так хочется "привязаться" к чему то неизменному, то привязывайтесь к телефонному номеру. Хотя и его может не быть.
Палец не панацея - у вас нет никаких гарантий что палец принадлежит владельцу персональных данных. 100% дактилоскопических данных пользователя у вас нет, а на этапе "привязки" пальца, телефон уже может быть в руках злоумышленника.
У вас не получится сделать идеальную систему с абсолютной системой от дурака. Поэтому я и предложил исходить из того что приложение на телефоне запускает легальных пользователь. Все иное должно описываться в политике конфиденциальности, типа мы чтим закон о ПД, но если вы долбоклюй и отдали телефон и пинкод постороннему, то вы сами пролюбили свою медкарту.
И пинкод никак не позволит войти на другом устройстве - пин код сохраняется локально на устройстве, в папке приложения. И если пользователь ввел его верно, то запросы на сервер авторизуются с заранее сохраненного на устройстве токена (guid, куки или что вам больше нравится).
Ситуации "А если кто взломает телефон и перенесет какой то файл на другое устройство" вас не должны волновать абсолютно. Вы предоставили пользователю приложение, а задача пользователя - не пролюбить телефон и свои персональные данные. Дополнительно вы прикрываете свою задницу политикой конфиденциальности, где описываете что не обязаны охранять пользователя и его телефон круглые сутки.
-
Евгений Корепов получил реакцию от Ingalime в Логика авторизации по отпечатку пальца
В телефоне нет никаких гарантированных аппаратных данных - или по причине китайщины, или по тому что их можно изменить. Если уже вам так хочется "привязаться" к чему то неизменному, то привязывайтесь к телефонному номеру. Хотя и его может не быть.
Палец не панацея - у вас нет никаких гарантий что палец принадлежит владельцу персональных данных. 100% дактилоскопических данных пользователя у вас нет, а на этапе "привязки" пальца, телефон уже может быть в руках злоумышленника.
У вас не получится сделать идеальную систему с абсолютной системой от дурака. Поэтому я и предложил исходить из того что приложение на телефоне запускает легальных пользователь. Все иное должно описываться в политике конфиденциальности, типа мы чтим закон о ПД, но если вы долбоклюй и отдали телефон и пинкод постороннему, то вы сами пролюбили свою медкарту.
И пинкод никак не позволит войти на другом устройстве - пин код сохраняется локально на устройстве, в папке приложения. И если пользователь ввел его верно, то запросы на сервер авторизуются с заранее сохраненного на устройстве токена (guid, куки или что вам больше нравится).
Ситуации "А если кто взломает телефон и перенесет какой то файл на другое устройство" вас не должны волновать абсолютно. Вы предоставили пользователю приложение, а задача пользователя - не пролюбить телефон и свои персональные данные. Дополнительно вы прикрываете свою задницу политикой конфиденциальности, где описываете что не обязаны охранять пользователя и его телефон круглые сутки.
-
Евгений Корепов отреагировална Slym в Передача параметров (с русскими буквами) в UTF8 в NetHTTPClient.Post
HTTPAnalyzer показывает:
POST / HTTP/1.1 Connection: Keep-Alive Content-Type: application/x-www-form-urlencoded; charset=utf-8 User-Agent: Embarcadero URI Client/1.0 Host: www.yandex.ru Content-Length: 148 nOwnerOrganizMode=1&cRegNumber=%D1%81-5%2F1&cBegRegDate=22.05.2029&cFIO=%D1%81%D0%B5%D1%80%D0%B3&cDocContent=%D0%B9%D1%86%D1%83qwe%D0%B9%D1%86%D1%83 Обилие %D0 - показывают что уходит UNICODE, а кодирование цифр и английского одним символом указывает что это UTF8... и заголовки этому соответствуют
Проблема на клиенте отсутствует... копай сервис
-
Евгений Корепов отреагировална krapotkin в Свой APK updater. Использование Fileprovider
полезные ссылки
https://android-tools.ru/coding/delimsya-fajlami-v-android-s-pomoshhyu-fileprovider/
https://www.delphiworlds.com/2018/06/targeting-android-8-and-higher-continued/
-
Евгений Корепов отреагировална krapotkin в Свой APK updater. Использование Fileprovider
От программиста никаких действий не требуется. Система любезно сообщает, что откуда попало не ставит, но вот вам переход прямо на нужную настройку. Переходим, включаем, нажимаем back и продолжаем установку...
-
Евгений Корепов отреагировална krapotkin в Свой APK updater. Использование Fileprovider
У меня в работе два приложения, и оба они не предназначены для Play market, так как имеют ограниченный круг использования, по сути, чисто внутрикорпоративные. Так что нежелательно и выкладывание их и на альтернативные магазины приложений.
Автоматически возникает вопрос обновления. Если в первый раз мы можем установить приложение сами при помощи админов, то обновлять их не так просто. А контингент пользователей не справится с "скачайте APK по ссылке, найдите, куда его скачал браузер, и запустите вручную именно последний скачанный, а не какой попало"...
Простейший способ - дать приложению скачать свежую копию с сайта и натравить на полученный файл системный инсталлер.
Вот только свежие Andoird делать это напрямик запрещают. Нужен filepropvider. Целый день шуровал по мануалам и YT,
Вот то что получилось в результате.
Если у вас 10.3.3 как у меня, уже можно не вносить <provider>...</provider> в манифест и свой файл file_paths.xml (или как вам его советуют назвать в интернетах) в деплой.
Теперь все это делается хоть несколько странно и однобоко, но автоматически, путем установки галочки Secure File Sharing
после этого в манифесте автоматически пропишется один из вариантов размещения файлов, которые вы можете найти в интернете. Используется алиас external-path
файл, показанный на рисунке, создается автоматически самой делфи.
теперь остается отгадать, какой путь реально подставится вместо "."
Как показала практика, все пути выглядят не так, как кажется, если исходить из простого здравого смысла. Целый день использования GetHomeDir и других полезных методов TPath завел меня совсем в тупик.
Оказалось все проще (?)
st:TMemoryStream; OutputDir: JFile; ApkFile: JFile; ApkUri: Jnet_Uri; path, filename: string; ... OutputDir := TAndroidHelper.Context.getExternalCacheDir(); path := JStringToString(OutputDir.getAbsolutePath); filename := path+'/ASDroid2.apk'; ApkFile := TJfile.JavaClass.init( StringToJstring(filename)); FApkUri := TAndroidHelper.JFileToJURI(ApkFile); st.Position := 0; st.SaveToFile(filename); обратите внимание, в provider_paths мы задаем external-paths, а в коде ищем ExternalCacheDir.!!! (For.Unbelievably.Creative.Knowers!)
Потом все просто. FApkUri передаем в интент и запускаем
итоговый код примерно таков. (скачивание в потоке с использованием небольшого собственного API, но там ничего важного, можно не обращать внимания)
procedure TasdSettingsFrame.bDownloadClick(Sender: TObject); begin {$IFDEF ANDROID} bDownload.Enabled := False; DownloadAndRun(); {$ENDIF} end; {$IFDEF ANDROID} procedure TasdSettingsFrame.DownloadAndRun(); begin ttask.Run(procedure var aapi:TasdAPI; st:TMemoryStream; OutputDir: JFile; ApkFile: JFile; ApkUri: Jnet_Uri; path, filename: string; begin st := TMemoryStream.Create; aapi := TasdAPI.Clone(_API); try aapi.OnReceiveData := OnReceiveData; aapi.getApk(st); if aapi.Err.Code=0 then begin OutputDir := TAndroidHelper.Context.getExternalCacheDir(); path := JStringToString(OutputDir.getAbsolutePath); filename := path+'/ASDroid2.apk'; ApkFile := TJfile.JavaClass.init( StringToJstring(filename)); FApkUri := TAndroidHelper.JFileToJURI(ApkFile); st.Position := 0; st.SaveToFile(filename); TThread.Synchronize(nil,procedure begin bDownload.Enabled := true; StartActivity(FApkUri); end); end; finally st.Free; aapi.Free; end; end); end; procedure StartActivity(ApkUri: Jnet_Uri); var Intent: JIntent; begin Intent := TJIntent.Create(); Intent.setAction(TJIntent.JavaClass.ACTION_VIEW); Intent.addFlags(TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK or TJIntent.JavaClass.FLAG_ACTIVITY_CLEAR_TOP or TJIntent.JavaClass.FLAG_GRANT_WRITE_URI_PERMISSION or TJIntent.JavaClass.FLAG_GRANT_READ_URI_PERMISSION); Intent.setDataAndType(apkuri, StringToJString('application/vnd.android.package-archive')); TAndroidHelper.Activity.startActivity(Intent); end; procedure TasdSettingsFrame.OnReceiveData(const Sender: TObject; AContentLength: Int64; AReadCount: Int64; var Abort: Boolean); begin tthread.Synchronize(nil, procedure begin pb1.Max := AContentLength; pb1.Value := AReadCount; end); end; {$ENDIF} Вопросы остались конечно, почему так странно с каталогами, но выяснять пока нет желания. Работает - не трожь.
Всем удачи.
UPD.
Для того, чтобы системный инсталлер запускался, нужно не забыть отметить еще одну галочку
-
Евгений Корепов отреагировална krapotkin в Ошибка java.lang.IllegalArgumentException: Unable to load native library... libProject1.so
В процессе разработки столкнулся с очень странной ситуацией, корни которой до сих пор неясны, но решение нашлось.
Итак. Делфи 10.3.3. Чистое приложение - пустая форма и кнопка. Отлично работает на разных устройствах кроме Samsung Galaxy Tab A (2016). Android 5.1.1
Программа падает, не успев даже загрузиться. Любая. При этом она же, скомпилированная на другом компе, запускается нормально. И даже после изменения SDK/NDK на нем, ничего не падает.
LogCat дает крайне странную картину
java.lang.IllegalArgumentException: Unable to load native library: /data/app-lib/com.embarcadero.Project1-1/libProject1.so
Начинаем экспериментировать с SDK, NDK и т.д. Поставил также хотфиксы для 10.3.3.
Сразу скажу, SDK/NDK ставились отдельно через Android studio, не скачивались вместе с Delphi. Не торопитесь бросать чтение на этом месте!)) На другом компе, где все работает, все ставилось точно так же.
Короче, очень много комбинаций версий SDK/NDK проверено.
Результат такой. При постепенном понижении версии NDK все взлетело при NDK v22.
При этом версия SDK видимо такого большого влияния не имеет, потому что этот вариант нормально работает
Повторюсь. На других устройствах все работало и на более новых Ndk. Xiaomi Mi Pad4 , Samsung Galaxy Tab A (2019), Xiaomi Redmi 5 и 5Plus
Спасибо за участие и помощь в разборе завалов @Andrey Efimov
-
Евгений Корепов получил реакцию от Сергей Сергеев в Логика авторизации по отпечатку пальца
В телефоне нет никаких гарантированных аппаратных данных - или по причине китайщины, или по тому что их можно изменить. Если уже вам так хочется "привязаться" к чему то неизменному, то привязывайтесь к телефонному номеру. Хотя и его может не быть.
Палец не панацея - у вас нет никаких гарантий что палец принадлежит владельцу персональных данных. 100% дактилоскопических данных пользователя у вас нет, а на этапе "привязки" пальца, телефон уже может быть в руках злоумышленника.
У вас не получится сделать идеальную систему с абсолютной системой от дурака. Поэтому я и предложил исходить из того что приложение на телефоне запускает легальных пользователь. Все иное должно описываться в политике конфиденциальности, типа мы чтим закон о ПД, но если вы долбоклюй и отдали телефон и пинкод постороннему, то вы сами пролюбили свою медкарту.
И пинкод никак не позволит войти на другом устройстве - пин код сохраняется локально на устройстве, в папке приложения. И если пользователь ввел его верно, то запросы на сервер авторизуются с заранее сохраненного на устройстве токена (guid, куки или что вам больше нравится).
Ситуации "А если кто взломает телефон и перенесет какой то файл на другое устройство" вас не должны волновать абсолютно. Вы предоставили пользователю приложение, а задача пользователя - не пролюбить телефон и свои персональные данные. Дополнительно вы прикрываете свою задницу политикой конфиденциальности, где описываете что не обязаны охранять пользователя и его телефон круглые сутки.
-
Евгений Корепов получил реакцию от Ingalime в Прочитать DNS запрос в idUDPServer
Все правильно получаете, кодировка тут не при чем. Согласно спецификации "DNS Packet Structure", вы получаете не строку, а пакет который нужно разобрать. К примеру если запрос будет "nslookup www.google.ru 127.0.0.1", то там где вы получаете имя хоста будет строка "''#3'www'#6'google'#2'ru'#0#0#1#0#1".
Парсинг простейший:
#3 - означает что далее идут 3 символа хоста 'www' - вот ожидаемые 3 символа #6 - далее идут еще 6 символов хоста 'google' - ага, вот они #2 - ну и еще 2 символа 'ru' - ура, они здесь #0 - конец имени хоста, складываем в кучу, перемежая точками и получаем www.google.ru #0 - дальше у нас служебная информация... #1 #0 #1 Вот как то так.
P.S. А зачем вам на таком низком уровне работать? Может использовать IdDNSServer : TIdDNSServer ?
P.P.S. Правильно Memo1.Lines.Add(BytesToString(AData,12)); //(12, а не 13)
-
Евгений Корепов отреагировална Yarpda в [DELPHI FMX Android] [6.0] Не удается загрузить SSL библиотеку
Спасибо! Действительно многое открылось. )) ThttpClient заработал как и хотелось и без танцев с бубном над библиотеками SSL.
-
Евгений Корепов получил реакцию от Yarpda в [DELPHI FMX Android] [6.0] Не удается загрузить SSL библиотеку
Вам не нужен TNETHTTPClient, это всего лишь обертка для THTTPClient. Просто сделайте поиск по данному форуму по "THTTPClient" и вам откроется многое.
-
Евгений Корепов получил реакцию от Ingalime в [DELPHI FMX Android] [6.0] Не удается загрузить SSL библиотеку
Вам не нужен TNETHTTPClient, это всего лишь обертка для THTTPClient. Просто сделайте поиск по данному форуму по "THTTPClient" и вам откроется многое.
-
Евгений Корепов получил реакцию от Tumaso в [DELPHI FMX Android] [6.0] Не удается загрузить SSL библиотеку
В примере у вас получение данных по HTTPS. В рабочем проекте тоже самое? Может тогда не мучатся с Indy и его сторонними библиотеками, а использовать родной механизм андроида - THTTPClient?
-
Евгений Корепов получил реакцию от #WAMACO в [DELPHI FMX Android] [6.0] Не удается загрузить SSL библиотеку
В примере у вас получение данных по HTTPS. В рабочем проекте тоже самое? Может тогда не мучатся с Indy и его сторонними библиотеками, а использовать родной механизм андроида - THTTPClient?
-
Евгений Корепов отреагировална slav_z в Положение скролла.
function IsEndScroll(ScrollBox: TCustomScrollBox): Boolean; begin Result:=ScrollBox.ViewportPosition.Y>=ScrollBox.ContentBounds.Height-ScrollBox.ClientHeight; end;
-
Евгений Корепов получил реакцию от Anatoliy в Умный дом
К сожалению тема никакого отношения в Умному дому не имеет. В телеграмме я поднимал тему о реальных технологиях умного дома - датчиках (задымление, протечка, газ, открытие двери/окна, движение), протоколы и технологии соединения всего этого, к примеру ZigBee, ну и работа со всем этим хозяйством.
Тема прикольная и интересная. Написал на delphi класс работы в ZigBee шлюзом, нормально опрашиваю все датчики, но запись реализовать не удалось из за отсутствия в delphi библиотек шифрования "из коробки". Наработок куча, но все они времен Delph 7 - абсолютно все используют короткие ANSI строки, и в современных версиях среды результат шифрования/дешифровки может быть какой угодно, но не правильный.
Пока все что нужно по быстрому написал на php, там хоть с шифрованием порядок, не надо изобретать велосипед или адаптировать код двадцатилетней давности.
Так что теперь могу включать/выключать свет с помощью умной розетки, включать светильник на основном хабе, выбирая яркость и палитру, но на php ))))
-
Евгений Корепов отреагировална krapotkin в Фреймворк для стандартного ListView
еще один пример раскладки из реального проекта
{ "Columns":[], "Variables": [ {"Y_UNIT":"12"}, {"GAP":"8"}, {"ITEM_H":"2*GAP+5*Y_UNIT"}, {"IMG_H":"76"}, {"IMG_W":"76"}, {"STOCK_SIZE":"48"}, {"PRICE_COLW":"53"} ], "Objects": [ {"Name":"ID","Kind":"data"}, , {"Name":"img","Kind":"image", "Place":{"X":"0","Y":"0","W":"IMG_W","H":"IMG_H"}, } , {"Name":"Text","Kind":"text","TextHAlign":"leading","TextVAlign":"leading", "Place":{"X":"img.right","Y":"GAP","W":"itemwidth - IMG_W - GAP - 2*PRICE_COLW - GAP","H":"52"}, "WordWrap":true, "Color":"black", "Font":{"Size":12} } , {"Name":"earlier","Kind":"text","TextHAlign":"leading","TextVAlign":"leading", "Place":{"X":"text.left","Y":"ITEM_H-2*GAP+2","W":"itemwidth - 2*PRICE_COLW","H":"Y_UNIT"}, "WordWrap":false, "Color":"green", "Font":{"Size":10} } , {"Name":"price1","Kind":"text","TextHAlign":"trailing","TextVAlign":"trailing", "Place":{"X":"itemwidth-GAP-PRICE_COLW-W","Y":"GAP+2*Y_UNIT","W":"auto","H":"Y_UNIT*1.5", "priority":"WXYH"}, "WordWrap":false, "Color":"black", "Font":{"Size":15} } , {"Name":"imgstrike","Kind":"image", "Place":{"X":"price1.x-5","Y":"price1.y","W":"price1.w+8","H":"price1.h"}, } , {"Name":"price2","Kind":"text","TextHAlign":"trailing","TextVAlign":"trailing", "Place":{"X":"price1.right-W","Y":"ITEM_H-GAP-Y_UNIT*1.5","W":"auto","H":"Y_UNIT*1.5", "priority":"WXYH"}, "WordWrap":false, "Color":"black", "Font":{"Size":15, "style":"bold"} } , {"Name":"qty","Kind":"text","TextHAlign":"trailing","TextVAlign":"center", "Place":{"X":"itemwidth-PRICE_COLW-GAP","Y":"GAP","W":"PRICE_COLW","H":"2*Y_UNIT"}, "WordWrap":false, "Color":"black", "Font":{"Size":27, "style":"bold"} } , {"Name":"imgstock","Kind":"image", "Place":{"X":"itemwidth-STOCK_SIZE-3","Y":"ITEM_H-STOCK_SIZE-4","W":"STOCK_SIZE","H":"STOCK_SIZE"}, } ], "ItemHeight":"ITEM_H", "ItemSpaces": {"X":"0","Y":"0","W":"0","H":""}, "SideSpace":"0" }
-
Евгений Корепов получил реакцию от qz5 в Где указать ownerBitmap := true (ImageListItemBottomDetail)
Смотрите, вот ваш код:
procedure TfmMain.LoadBitmapFromURL(const AURL: string; const AItem: TListViewItem; aBitmap: TBitmap; aSourceBmp: TRectangle); var K: Integer; // Анонимная процедура захватывает локальную переменную, а не обращается к AItem, которой уже может не быть в момент _окончания_ скачивания фотки FHTTPClient : THTTPClient; ResourceStream: TResourceStream; begin Вы используете асинхронный вызов FHTTPClient.BeginGet , начинает выполнятся HTTP запрос, одновременно процедура LoadBitmapFromURL завершает работу и компилятор уничтожает локальные переменные, включая FHTTPClient. Асинхронные вызовы с получением результат происходят уже после уничтожения FHTTPClient и естественно ничего не работает. Вод виндой работает случайно - из за другой модели управления памятью.
У вас несколько путей:
1. Сделать FHTTPClient глобальным - лучший и простой путь.
2. Создавать поток самостоятельно, использовать в нем FHTTPClient.Get (не асинхронный)
3. И еще куча вариантов....
Посмотрите как реализовано у меня (по первому варианту)