Перейти к содержанию
Fire Monkey от А до Я

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

Пользователи
  • Постов

    738
  • Зарегистрирован

  • Посещение

  • Победитель дней

    100

Активность репутации

  1. Like
    Евгений Корепов получил реакцию от Dev в Runtime permissions in Delphi 10.3 Rio   
    Для полноценной работы вам нужно добавить параметры в вызов (иначе вы не узнаете дал ли пользователь разрешение или нет)
    PermissionsService.RequestPermissions([FPermissionWrite, FPermissionRead], nil); Вот так:
    PermissionsService.RequestPermissions([FPermissionWrite, FPermissionRead], PermissionRequestResult, ExplainReason); PermissionRequestResult - это обработка ответа пользователя
    procedure TForm.PermissionRequestResult(Sender: TObject; const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>); begin if (Length(AGrantResults) = 2) and (AGrantResults[0] = TPermissionStatus.Granted) and (AGrantResults[1] = TPermissionStatus.Granted) then begin // Ура! Пользователь дал разрешение на оба наших запроса. Выставялем глобальные флаги (к примеру) которые сигнализируют что можно читать/писать карту памти end else TDialogService.ShowMessage('Не возможно продолжить работу, требуемые разрешения не получены') end; И ExplainReason - если пользователь сдуру не дал разрешение, то вам нужно объяснить ему что без этого приложение работать не будет.
    procedure TForm.ExplainReason(Sender: TObject; const APermissions: TArray<string>; const APostRationaleProc: TProc); begin TDialogService.ShowMessage('Приложению нужен доступ к карте памяти для таких то целей, иначе приложение не сможет работать. Зайдите в настроки Андроид и дайте разрешение на доступ', procedure(const AResult: TModalResult) begin APostRationaleProc; end) end;  
  2. Like
    Евгений Корепов получил реакцию от flydev в PUSH и Android8   
    Этот замкнутый круг разорвали в Rio, переходите на последнюю версию среды и пуши будут работать.
  3. Like
    Евгений Корепов получил реакцию от Brovin Yaroslav в Runtime permissions in Delphi 10.3 Rio   
    Для полноценной работы вам нужно добавить параметры в вызов (иначе вы не узнаете дал ли пользователь разрешение или нет)
    PermissionsService.RequestPermissions([FPermissionWrite, FPermissionRead], nil); Вот так:
    PermissionsService.RequestPermissions([FPermissionWrite, FPermissionRead], PermissionRequestResult, ExplainReason); PermissionRequestResult - это обработка ответа пользователя
    procedure TForm.PermissionRequestResult(Sender: TObject; const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>); begin if (Length(AGrantResults) = 2) and (AGrantResults[0] = TPermissionStatus.Granted) and (AGrantResults[1] = TPermissionStatus.Granted) then begin // Ура! Пользователь дал разрешение на оба наших запроса. Выставялем глобальные флаги (к примеру) которые сигнализируют что можно читать/писать карту памти end else TDialogService.ShowMessage('Не возможно продолжить работу, требуемые разрешения не получены') end; И ExplainReason - если пользователь сдуру не дал разрешение, то вам нужно объяснить ему что без этого приложение работать не будет.
    procedure TForm.ExplainReason(Sender: TObject; const APermissions: TArray<string>; const APostRationaleProc: TProc); begin TDialogService.ShowMessage('Приложению нужен доступ к карте памяти для таких то целей, иначе приложение не сможет работать. Зайдите в настроки Андроид и дайте разрешение на доступ', procedure(const AResult: TModalResult) begin APostRationaleProc; end) end;  
  4. Like
    Евгений Корепов получил реакцию от Barbanel в Runtime permissions in Delphi 10.3 Rio   
    Для полноценной работы вам нужно добавить параметры в вызов (иначе вы не узнаете дал ли пользователь разрешение или нет)
    PermissionsService.RequestPermissions([FPermissionWrite, FPermissionRead], nil); Вот так:
    PermissionsService.RequestPermissions([FPermissionWrite, FPermissionRead], PermissionRequestResult, ExplainReason); PermissionRequestResult - это обработка ответа пользователя
    procedure TForm.PermissionRequestResult(Sender: TObject; const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>); begin if (Length(AGrantResults) = 2) and (AGrantResults[0] = TPermissionStatus.Granted) and (AGrantResults[1] = TPermissionStatus.Granted) then begin // Ура! Пользователь дал разрешение на оба наших запроса. Выставялем глобальные флаги (к примеру) которые сигнализируют что можно читать/писать карту памти end else TDialogService.ShowMessage('Не возможно продолжить работу, требуемые разрешения не получены') end; И ExplainReason - если пользователь сдуру не дал разрешение, то вам нужно объяснить ему что без этого приложение работать не будет.
    procedure TForm.ExplainReason(Sender: TObject; const APermissions: TArray<string>; const APostRationaleProc: TProc); begin TDialogService.ShowMessage('Приложению нужен доступ к карте памяти для таких то целей, иначе приложение не сможет работать. Зайдите в настроки Андроид и дайте разрешение на доступ', procedure(const AResult: TModalResult) begin APostRationaleProc; end) end;  
  5. Like
    Евгений Корепов получил реакцию от Tumaso в Runtime permissions in Delphi 10.3 Rio   
    Для полноценной работы вам нужно добавить параметры в вызов (иначе вы не узнаете дал ли пользователь разрешение или нет)
    PermissionsService.RequestPermissions([FPermissionWrite, FPermissionRead], nil); Вот так:
    PermissionsService.RequestPermissions([FPermissionWrite, FPermissionRead], PermissionRequestResult, ExplainReason); PermissionRequestResult - это обработка ответа пользователя
    procedure TForm.PermissionRequestResult(Sender: TObject; const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>); begin if (Length(AGrantResults) = 2) and (AGrantResults[0] = TPermissionStatus.Granted) and (AGrantResults[1] = TPermissionStatus.Granted) then begin // Ура! Пользователь дал разрешение на оба наших запроса. Выставялем глобальные флаги (к примеру) которые сигнализируют что можно читать/писать карту памти end else TDialogService.ShowMessage('Не возможно продолжить работу, требуемые разрешения не получены') end; И ExplainReason - если пользователь сдуру не дал разрешение, то вам нужно объяснить ему что без этого приложение работать не будет.
    procedure TForm.ExplainReason(Sender: TObject; const APermissions: TArray<string>; const APostRationaleProc: TProc); begin TDialogService.ShowMessage('Приложению нужен доступ к карте памяти для таких то целей, иначе приложение не сможет работать. Зайдите в настроки Андроид и дайте разрешение на доступ', procedure(const AResult: TModalResult) begin APostRationaleProc; end) end;  
  6. Like
    Евгений Корепов отреагировална yooSee в Runtime permissions in Delphi 10.3 Rio   
    Всем привет, вот и я решил внести свою лепту в жизнь форума и сегодня мы разберемся с разрешениями на Delphi Rio под Андроид. 
    Для примера мы будем использовать разрешения на чтение и запись с памяти устройства, для начала в нашем проекте выставим в Delphi>Project>Application>Uses Permissions галочки на Read External Storage и Write External Storage.
    в uses проекта добавьте следующее
    uses System.Permissions, Androidapi.Jni.Os, Androidapi.Helpers, далее  создадим переменные
    var Form: TForm; .... FPermissionWrite: string; FPermissionRead: string; в Form.Create добавим следующий код
    procedure TForm.FormCreate(Sender: TObject); begin FPermissionWrite := JStringToString(TJManifest_permission.JavaClass.WRITE_EXTERNAL_STORAGE); //Значение на запись FPermissionRead := JStringToString(TJManifest_permission.JavaClass.READ_EXTERNAL_STORAGE); //Значение на чтение end; и теперь сам не посредственно запрос на подтверждение наших прав
    procedure TForm.Button1Click(Sender: TObject); begin PermissionsService.RequestPermissions ([FPermissionWrite, FPermissionRead], nil); end; //это вызовет окно с запросом разрешения прав ну и для проверки бросим на форму TMemo и пару TButton , в них реализуем сохранение и чтение из файла с памяти устройства
    procedure TForm.Button1Click(Sender: TObject); begin Memo1.Lines.LoadFromFile(TPath.Combine(TPath.GetSharedDocumentsPath, 'test.txt')); end; procedure TForm.Button2Click(Sender: TObject); begin Memo1.Lines.SaveToFile(TPath.Combine(TPath.GetSharedDocumentsPath, 'test.txt')); end; P.S. Напоминаю что для работы TPath нам понадобится подключить в Uses 
    uses System.system.ioutils; Надеюсь материал будет полезен! 
     
     
     

    video_2019-02-08_00-11-36.mp4 Permissions.zip
  7. Like
    Евгений Корепов отреагировална Rusland в Webbrowser в Windows   
    В ходе работы с js библиотекой leaflet столкнулся с проблемой некорректной работы стандартного веббраузера на виндоуз. 
    Как известно компонент Webbrowser в Windows использует движок Internet Explorer. Но, как выяснилось, по умолчанию он работает в режиме совместимости IE6, даже если у вас установлена гораздо более новая версия.
    Для того чтобы заставить его работать в нужно вам режиме требуется внести правку в реестр:
    HKEY_CURRENT_USER или HKEY_LOCAL_MACHINE SOFTWARE Microsoft Internet Explorer Main FeatureControl FEATURE_BROWSER_EMULATION Project1.exe = (DWORD) 0x00002710 (значения см. ниже) где Project1.exe - это название вашего приложения, а значения параметр принимает следующие:
    Value Description 11001 (0x2AF9 Internet Explorer 11. Webpages are displayed in IE11 edge mode, regardless of the declared !DOCTYPE directive. Failing to declare a !DOCTYPE directive causes the page to load in Quirks. 11000 (0x2AF8) IE11. Webpages containing standards-based !DOCTYPE directives are displayed in IE11 edge mode. Default value for IE11. 10001 (0x2711) Internet Explorer 10. Webpages are displayed in IE10 Standards mode, regardless of the !DOCTYPE directive. 10000 (0x02710) Internet Explorer 10. Webpages containing standards-based !DOCTYPE directives are displayed in IE10 Standards mode. Default value for Internet Explorer 10. 9999 (0x270F) Windows Internet Explorer 9. Webpages are displayed in IE9 Standards mode, regardless of the declared !DOCTYPE directive. Failing to declare a !DOCTYPE directive causes the page to load in Quirks. 9000 (0x2328) Internet Explorer 9. Webpages containing standards-based !DOCTYPE directives are displayed in IE9 mode. Default value for Internet Explorer 9. Important  In Internet Explorer 10, Webpages containing standards-based !DOCTYPE directives are displayed in IE10 Standards mode.   8888 (0x22B8) Webpages are displayed in IE8 Standards mode, regardless of the declared !DOCTYPE directive. Failing to declare a !DOCTYPE directive causes the page to load in Quirks. 8000 (0x1F40) Webpages containing standards-based !DOCTYPE directives are displayed in IE8 mode. Default value for Internet Explorer 8 Important  In Internet Explorer 10, Webpages containing standards-based !DOCTYPE directives are displayed in IE10 Standards mode.   7000 (0x1B58) Webpages containing standards-based !DOCTYPE directives are displayed in IE7 Standards mode. Default value for applications hosting the WebBrowser Control.  
    PS. лично мне помогло исправление ветки HKEY_CURRENT_USER и установка версии IE10
    link
  8. Like
    Евгений Корепов получил реакцию от Евгений (KeeperWorld) в Отображение картинок в ListView   
    У меня на Rio работает как то не стабильно. Изредка вылазят исключения при закрытии приложения. То в TMonitor, то даже в TDictionary (((
     
    Сделал стабильную версию - работает быстро и без глюков, но с использованием внешнего списка с IAsyncResult. Добавил процедуру ClearListViewAndCancelAsynchronousRequests() где выполняется Cancel и очищается ListView. Теперь можно клацать по кнопке сколько угодно.
    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, System.Net.HttpClient, System.Generics.Collections, FMX.Controls.Presentation, FMX.StdCtrls, FMX.Layouts, FMX.ListView; const ListViewItemImageEmpy = -1; ListViewItemImageLoading = 0; ListViewItemImageLoaded = 1; type TForm1 = class(TForm) ListView1: TListView; Layout1: TLayout; Button1: TButton; procedure Button1Click(Sender: TObject); procedure ListView1UpdatingObjects(const Sender: TObject; const AItem: TListViewItem; var AHandled: Boolean); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private { Private declarations } FListViewUpdating : Boolean; FHTTPClient : THTTPClient; FAsyncResultList : TList<IAsyncResult>; procedure LoadImage(const AItem: TListViewItem; const AListItemImage : TListItemImage); procedure ClearListViewAndCancelAsynchronousRequests(); public { Public declarations } end; var Form1: TForm1; implementation {$R *.fmx} procedure TForm1.FormCreate(Sender: TObject); begin listview1.ItemIndex:=0; listview1.ItemAppearance.ItemAppearance:='Custom'; listview1.ItemAppearanceObjects.ItemObjects.Accessory.Visible:=false; FHTTPClient:=THTTPClient.Create; FAsyncResultList:=TList<IAsyncResult>.Create; FListViewUpdating:=False; end; procedure TForm1.FormDestroy(Sender: TObject); begin ClearListViewAndCancelAsynchronousRequests(); FListViewUpdating:=True; if Assigned(FHTTPClient) then FHTTPClient.Free; end; procedure TForm1.ClearListViewAndCancelAsynchronousRequests(); Var I : Integer; begin for I := 0 to FAsyncResultList.Count - 1 do if FAsyncResultList.Items[I] <> Nil then if Not FAsyncResultList.Items[I].IsCompleted then FAsyncResultList.Items[I].Cancel; FListViewUpdating:=True; for I := ListView1.Items.Count - 1 downto 0 do ListView1.Items.Delete(I); FListViewUpdating:=False; end; procedure TForm1.Button1Click(Sender: TObject); var i:integer; item:TListViewItem; ARandom : Integer; begin ClearListViewAndCancelAsynchronousRequests(); //Формирование нового списка for i := 1 to 10000 do begin FAsyncResultList.Add(Nil); FListViewUpdating:=True; Item:=listview1.Items.Add; item.Height:=45; Randomize; ARandom:=Random(6); case ARandom of 0 : item.data['ImageURL']:='http://fire-monkey.ru/uploads/monthly_2017_06/me.thumb.jpg.966ddc17d5602ee14feb43479c1f6963.jpg'; 1 : item.data['ImageURL']:='http://fire-monkey.ru/uploads/monthly_2018_05/B-IpGQmVgTM.thumb.jpg.2ebeb0bd766ab7cf19f10195d6ea2be9.jpg'; 2 : item.data['ImageURL']:='http://fire-monkey.ru/uploads/monthly_2016_04/10.png.b9ab371e8fd38172fee96bcf75fb6699.thumb.png.b0685259b03bfff540903913845532a5.png'; 3 : item.data['ImageURL']:='https://secure.gravatar.com/avatar/9942c50b1641a921c52d4b389bd718d6?d=http://fire-monkey.ru/uploads/monthly_2017_12/K_member_87.png'; 4 : item.data['ImageURL']:='http://fire-monkey.ru/uploads/monthly_2016_11/photo-1529.png.7267be10b59f950b7c5bb3f34a60901e.thumb.png.22027ae85266216220310ed694d57628.png'; 5 : item.data['ImageURL']:='http://fire-monkey.ru/uploads/profile/photo-thumb-115.jpg'; end; Item.Data['ImageState']:=ListViewItemImageEmpy; FListViewUpdating:=False; item.Adapter.ResetView(item); end; end; procedure TForm1.LoadImage(const AItem: TListViewItem; const AListItemImage : TListItemImage); Var AAsyncResult : IAsyncResult; begin if Not Assigned(AItem) or Not Assigned(AListItemImage) then exit; if AItem.Data['ImageState'].AsInteger <> ListViewItemImageEmpy then exit; if AItem.Data['ImageURL'].AsString.IsEmpty then exit; AItem.Data['ImageState']:=ListViewItemImageLoading; FAsyncResultList.Items[AItem.Index]:=FHTTPClient.BeginGet( // AItem.TagObject:=TBaseAsyncResult(FHTTPClient.BeginGet( // FHTTPClient.BeginGet( procedure (const ASyncResult: IAsyncResult) Var AHTTPResponse : IHTTPResponse; begin if ASyncResult.IsCancelled then begin exit; end; try AHTTPResponse:=THTTPClient.EndAsyncHTTP(ASyncResult); if Not Assigned(AHTTPResponse) then exit; if AHTTPResponse.StatusCode <> 200 then exit; except exit; end; TThread.Synchronize(Nil, procedure begin if Not Assigned(AItem) then exit; if Not Assigned(AListItemImage) then exit; AListItemImage.BeginUpdate; AListItemImage.Bitmap:=TBitmap.Create; AListItemImage.Bitmap.LoadFromStream(AHTTPResponse.ContentStream); AListItemImage.EndUpdate; AItem.Data['ImageState']:=ListViewItemImageLoaded; FAsyncResultList.Items[AItem.Index]:=Nil; end ); end, AItem.Data['ImageURL'].AsString ); end; procedure TForm1.ListView1UpdatingObjects(const Sender: TObject; const AItem: TListViewItem; var AHandled: Boolean); function SetupImageObject(const AName : String; AWidth, AHeight, X , Y : Single; AAlign, AVertAlign: TListItemAlign) : TListItemImage; begin Result:=TListItemImage(AItem.View.FindDrawable(AName)); if Result = Nil then begin Result:=TListItemImage.Create(AItem); Result.Name:=AName; Result.Bitmap:=Nil; Result.OwnsBitmap:=True; end; Result.Width:=AWidth; Result.Height:=AHeight; Result.PlaceOffset.X:=X; Result.PlaceOffset.Y:=Y; Result.Align:=AAlign; Result.VertAlign:=AVertAlign; Result.ScalingMode:=TImageScalingMode.StretchWithAspect; Result.Visible:=True; end; Var AListItemImage : TListItemImage; begin if FListViewUpdating then exit; AListItemImage:=SetupImageObject('s_image', 35, 35, 0 , 0, TListitemalign.Leading, TListItemAlign.Center); LoadImage(AItem, AListItemImage); AHandled:=true; end; end.  
  9. Like
    Евгений Корепов отреагировална mmover в ClientWidth и ClientHeight   
    cw :=  Grid1.Content.Width;
     ch :=  Grid1.Content.Height;
     
  10. Like
    Евгений Корепов получил реакцию от Ingalime в PUSH и Android8   
    Этот замкнутый круг разорвали в Rio, переходите на последнюю версию среды и пуши будут работать.
  11. Like
    Евгений Корепов отреагировална Паршенко Виктор в ClientWidth и ClientHeight   
    клиентские размеры есть у TCommonCustomForm (в следствии и у наследников, TForm) 
     
    FWinService: IFMXWindowService;
    ...
    function TCommonCustomForm.GetClientHeight: Integer;
    begin
      Result := round(FWinService.GetClientSize(Self).Y);
    end;
    function TCommonCustomForm.GetClientWidth: Integer;
    begin
      Result := round(FWinService.GetClientSize(Self).X);
    end;
     
  12. Like
    Евгений Корепов получил реакцию от Maka в Проигрывание стандартных звуковых эффектов Android   
    Задался вопросом, нашел ответ, может кому пригодится.
    uses Androidapi.JNI.Media, Androidapi.Helpers, Androidapi.JNIBridge, AndroidApi.Jni.JavaTypes, Androidapi.JNI.GraphicsContentViewText; .... procedure TFormMain.PlaySoundEffects(const ASoundID : Integer; AVolume : Single = 1.0); var AudioObj: JObject; Audio: JAudioManager; begin AudioObj:= TAndroidHelper.Context.getSystemService(TJContext.JavaClass.AUDIO_SERVICE); Audio:= TJAudioManager.Wrap((AudioObj as ILocalObject).GetObjectID); Audio.loadSoundEffects; Audio.playSoundEffect(ASoundID, AVolume); end; Константы звуковых эффектов тут https://developer.android.com/reference/android/view/SoundEffectConstants
    Правда у меня только звук "чпок" на всех константах. Но мне он и был нужен )
  13. Like
    Евгений Корепов отреагировална Ry Koo в [Android] Как отключить клавиатуру в приложении?   
    у формы есть событие FormVirtualKeyboardShown, где можно в общем обработать событие показа\скрытия клавиатуры.
    там и флаг есть KeyboardVisible.
    а в частности, для конкретного поля показывать\скрывать вот так можно как вариант:
    procedure Tform1.Memo1Tap(Sender: TObject; const Point: TPointF);
    var
      KeyboardService: IFMXVirtualKeyboardService;
    begin
      if TPlatformServices.Current.SupportsPlatformService(IFMXVirtualKeyboardService, IInterface(KeyboardService)) then   // Запрашиваем сервис виртуальной клавиатуры
        KeyboardService.HideVirtualKeyboard;
     //KeyboardService.ShowVirtualKeyboard(Memo1); для показа
    end;
  14. Like
    Евгений Корепов отреагировална kami в Поток в Android-е   
    Тут нет кода, выполняемого в доп.потоке.
    Метод Synchronize делает следующее: "приостановить выполнение себя (т.е. доп.потока), переключиться в основной поток, выполнить там действие и после этого вернуться в себя (в доп.поток)".
    Ввиду того, что в коде доп.потока есть только synchronize - то единственное, что поток делает - ждёт, пока выполнится его действие в основном потоке.
    То есть - является абсолютно бессмысленным.
  15. Thanks
    Евгений Корепов отреагировална Slym в Отображение картинок в ListView   
    Логика асинхронности принимает на вход анонимную процедуру для обработки результата, анонимная процедура захватывает переменные в зоне своего определения, а там хоть итем, хоть индекс, хоть блэкджек с простихоспаде чем попало...
  16. Like
    Евгений Корепов получил реакцию от GASCHE в Отображение картинок в ListView   
    Вот вы же сейчас шутите да? Я просто взял код который не работал совсем (отрисовка после скрола - это совсем), и сделал из него 100% рабочий код. Потратил на это около 10 минут. Упомянул что код и метод подгрузки - для забавы. А вы начинаете мне говорить что для продакшина этот код не годится, что я все сделал не правильно, что на больших списках все загнется.
    Автор топика четко описал проблему:
    Я проблему решил? Показал что проблема была в ошибочной логике приложения, а не в неких глюках ListView? А мы мне начинаете рассказывать о том что вот если этот демо код запустить в космос, то ой-ой-ой случится. ?
    По пунктам:
    1. Отрисовка в OnPaint - событие может вызываться как угодно часто, ничто не запретит системе вызвать его несколько раз в секунду. Цикл перебора всех элементов списка в этом событии с остановкой нормальной отрисовки - очень-очень плохой совет.
    2. Картинки в высоком качестве и (или) их комплект с разным scale - логика приложения должна быть построена так, чтобы определять нужный scale и загружать только подходящие картинки с вашего сервера.
    По поводу потоков и их количества - нужно помнить что кроме ресурсов телефона, используются ресурсы полосы пропускания текущего Интернет соединения на устройстве и ресурсы сервера. Поэтому лучше для загрузки использовать 1 (ну может 2-3) потока - десяток видимых картинок последовательно загрузятся почти мгновенно.
    К сожалению логика асинхронности HTTPClient упускает одну важную вещь - идентификацию полученного результата. Т.е. даем пяток заданий GET, загрузить картинку для Item 1,2,3,4,5 - но при получении результата мы никак не можем определить что полученный результат относится к Item 3, а не какому либо другом. По крайней мере мне это не удалось сделать просто и прозрачно. Посему остается вариант создания экземпляра HTTPClient для каждого запроса - возвращаемся к фактически анонимным процедурам, попутно стреляя себе в ногу, потому как создание/уничтожение экземпляра HTTPClient чуть ли не дороже скачивания 10 килобайтной картинки )
     
    Можете посмотреть пример реализации подобной задачи в моем старинном приложении, там как раз реализован подход о котором говорил - загрузка картинок только видимых элементов (+ несколько про запас), загрузка картинок последовательно одним потоком ну и еще что то:. https://play.google.com/store/apps/details?id=ru.flintnet.Actions5
  17. Like
    Евгений Корепов получил реакцию от Maximus в Отображение картинок в ListView   
    Вот вы же сейчас шутите да? Я просто взял код который не работал совсем (отрисовка после скрола - это совсем), и сделал из него 100% рабочий код. Потратил на это около 10 минут. Упомянул что код и метод подгрузки - для забавы. А вы начинаете мне говорить что для продакшина этот код не годится, что я все сделал не правильно, что на больших списках все загнется.
    Автор топика четко описал проблему:
    Я проблему решил? Показал что проблема была в ошибочной логике приложения, а не в неких глюках ListView? А мы мне начинаете рассказывать о том что вот если этот демо код запустить в космос, то ой-ой-ой случится. ?
    По пунктам:
    1. Отрисовка в OnPaint - событие может вызываться как угодно часто, ничто не запретит системе вызвать его несколько раз в секунду. Цикл перебора всех элементов списка в этом событии с остановкой нормальной отрисовки - очень-очень плохой совет.
    2. Картинки в высоком качестве и (или) их комплект с разным scale - логика приложения должна быть построена так, чтобы определять нужный scale и загружать только подходящие картинки с вашего сервера.
    По поводу потоков и их количества - нужно помнить что кроме ресурсов телефона, используются ресурсы полосы пропускания текущего Интернет соединения на устройстве и ресурсы сервера. Поэтому лучше для загрузки использовать 1 (ну может 2-3) потока - десяток видимых картинок последовательно загрузятся почти мгновенно.
    К сожалению логика асинхронности HTTPClient упускает одну важную вещь - идентификацию полученного результата. Т.е. даем пяток заданий GET, загрузить картинку для Item 1,2,3,4,5 - но при получении результата мы никак не можем определить что полученный результат относится к Item 3, а не какому либо другом. По крайней мере мне это не удалось сделать просто и прозрачно. Посему остается вариант создания экземпляра HTTPClient для каждого запроса - возвращаемся к фактически анонимным процедурам, попутно стреляя себе в ногу, потому как создание/уничтожение экземпляра HTTPClient чуть ли не дороже скачивания 10 килобайтной картинки )
     
    Можете посмотреть пример реализации подобной задачи в моем старинном приложении, там как раз реализован подход о котором говорил - загрузка картинок только видимых элементов (+ несколько про запас), загрузка картинок последовательно одним потоком ну и еще что то:. https://play.google.com/store/apps/details?id=ru.flintnet.Actions5
  18. Like
    Евгений Корепов получил реакцию от #WAMACO в Отображение картинок в ListView   
    Проблемы не существует в принципе. Пытался разобраться в коде, но из за его полной не читабельности, плюнул, все удалил а написал заново. Решил для забавы использовать анонимный поток для загрузки картинки. По кнопке все грузится и сразу отображается. Никаких костылей не используется (ну кроме как AListItemImage.OwnsBitmap:=True, но и без этого все работает).
    Прилагаю код и архив проекта:
    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, System.Net.HttpClient, FMX.Controls.Presentation, FMX.StdCtrls, FMX.Layouts, FMX.ListView; const ListViewItemImageEmpy = -1; ListViewItemImageLoading = 0; ListViewItemImageLoaded = 1; type TForm1 = class(TForm) ListView1: TListView; Layout1: TLayout; Button1: TButton; procedure Button1Click(Sender: TObject); procedure ListView1UpdatingObjects(const Sender: TObject; const AItem: TListViewItem; var AHandled: Boolean); procedure FormCreate(Sender: TObject); private { Private declarations } FListViewUpdating : Boolean; procedure LoadImage(const AItemIndex : Integer; AURL : String); public { Public declarations } end; var Form1: TForm1; implementation {$R *.fmx} procedure TForm1.FormCreate(Sender: TObject); begin FListViewUpdating:=False; end; procedure TForm1.Button1Click(Sender: TObject); var i:integer; item:TListViewItem; begin listview1.ItemIndex:=0; listview1.ItemAppearance.ItemAppearance:='Custom'; listview1.ItemAppearanceObjects.ItemObjects.Accessory.Visible:=false; //Очистка ListView for i := Listview1.ItemCount-1 downto 0 do ListView1.Items.Delete(i); //Формирование нового списка for i := 1 to 10 do begin Item:=listview1.Items.Add; item.Height:=45; FListViewUpdating:=true; item.data['ImageURL']:='http://fire-monkey.ru/uploads/monthly_2016_03/5-icon-29796ebcb510717e74ed258822feb2cc.png.365100cc218fe330c717aa80384fa4aa.png'; Item.Data['ImageState']:=ListViewItemImageEmpy; FListViewUpdating:=false; item.Adapter.ResetView(item); end; end; procedure TForm1.LoadImage(const AItemIndex : Integer; AURL : String); Var AHTTPClient : THTTPClient; AHTTPResponse : IHTTPResponse; begin AHTTPClient:=THTTPClient.Create(); AHTTPResponse:=AHTTPClient.Get(AURL); AHTTPClient.Free; if AHTTPResponse.StatusCode <> 200 then exit; TThread.Synchronize(Nil, procedure Var AListItemImage : TListItemImage; begin AListItemImage:=TListItemImage(ListView1.Items.Item[AItemIndex].View.FindDrawable('s_image')); if Assigned(AListItemImage) then begin AListItemImage.Bitmap:=TBitmap.Create; AListItemImage.Bitmap.LoadFromStream(AHTTPResponse.ContentStream); end; end ); end; procedure TForm1.ListView1UpdatingObjects(const Sender: TObject; const AItem: TListViewItem; var AHandled: Boolean); function SetupImageObject(const AName : String; AWidth, AHeight, X , Y : Single; AAlign, AVertAlign: TListItemAlign) : TListItemImage; Var AImageState : Integer; AImageURL : String; AListItemImage : TListItemImage; begin AListItemImage:=TListItemImage(AItem.View.FindDrawable(AName)); if AListItemImage = Nil then begin AListItemImage:=TListItemImage.Create(AItem); AListItemImage.Name:=AName; AListItemImage.Bitmap:=Nil; AListItemImage.OwnsBitmap:=True; end; if AListItemImage.Bitmap=Nil then begin AImageState:=AItem.Data['ImageState'].AsInteger; if AImageState=ListViewItemImageEmpy then begin AImageURL:=AItem.Data['ImageURL'].AsString; if Not AImageURL.IsEmpty then begin AItem.Data['ImageState']:=ListViewItemImageLoading; TThread.CreateAnonymousThread( procedure begin LoadImage(AItem.Index, AImageURL); end).Start; end; end; end; AListItemImage.Width:=AWidth; AListItemImage.Height:=AHeight; AListItemImage.PlaceOffset.X:=X; AListItemImage.PlaceOffset.Y:=Y; AListItemImage.Align:=AAlign; AListItemImage.VertAlign:=AVertAlign; AListItemImage.ScalingMode:=TImageScalingMode.StretchWithAspect; AListItemImage.Visible:=True; Result:=AListItemImage; end; begin if FListViewUpdating then exit; SetupImageObject('s_image', 35, 35, 0 , 0, TListitemalign.Leading, TListItemAlign.Center); AHandled:=true; end; end.  
    LoadBitmaps.zip
  19. Like
    Евгений Корепов получил реакцию от GASCHE в Всплывающее сообщение при его закрытии разворачивает приложение   
    Я бы рекомендовал, как вам уже подсказывали, использовать готовые механизмы уведомлений.
    Или вариант с TNotificationCenter - над треем будет появляться окошко с вашим уведомлением, клик по окошку вы можете произвольно обработать в приложении. Этот механизм "написан по быстрому" через пень-колоду, просто создает окно, не пытаясь взаимодействовать с платформой.

    Или использовать родной механизм Windows  - winapi Shell_NotifyIcon, тогда вы получите еще и уведомления в центре уведомлений Windows

    Второй способ можно использовать с помощью многочисленных компонентов TTrayIcon:
    procedure TForm1.SetNotification(const ATitle, AMessage : String); begin FTrayIcon.BalloonTitle:=ATitle; FTrayIcon.BalloonHint:=AMessage; FTrayIcon.ShowBalloonHint; end;  
  20. Like
    Евгений Корепов отреагировална #WAMACO в Всплывающее сообщение при его закрытии разворачивает приложение   
    Используйте механизм уведомлений, которые всплывают внизу, в трее.
  21. Haha
    Евгений Корепов получил реакцию от Barbanel в Подписание новых приложений в google play   
    Вот точно ничего не надо делать и тем более подключать. Эмбаркадера еще не дочитала новости гугля. Они пока еще за 2015 год переваривают ))))
  22. Like
    Евгений Корепов получил реакцию от Tumaso в Троит картинка при движении объекта   
    В FMX координаты Single и они никак не преобразуются компилятором в Integer. Забудьте о пикселях, они не связаны с координатами. Представьте если бы координаты считались в целых пикселях - сделали вы перемещение под HD монитор, а пользователь запустил приложение на UHD мониторе - все ваши пиксельные расчеты превратятся в тыкву.
  23. Thanks
    Евгений Корепов получил реакцию от alxsev в Подписание новых приложений в google play   
    Вот точно ничего не надо делать и тем более подключать. Эмбаркадера еще не дочитала новости гугля. Они пока еще за 2015 год переваривают ))))
  24. Like
    Евгений Корепов отреагировална mmover в Как перерисовать скролл?   
    Если я правильно понял задачу, то так :
    procedure TForm2.VScrollPaint(Sender: TObject; Canvas: TCanvas; const ARect: TRectF); begin Canvas.Fill.Color := TAlphaColors.Red; Canvas.FillRect(ARect,0,0,[],1); end; procedure TForm2.FormShow(Sender: TObject); begin Grid1.StylesData['vscrollbar.OnPaint'] := TValue.From<TOnPaintEvent>(VScrollPaint); end;  
  25. Like
    Евгений Корепов получил реакцию от Brovin Yaroslav в Как застать TWebBrowser обрабатывать адреса эл. почты (Android)   
    Прошу прощения, я ввел вас в заблуждение (тестировал на одном своем проекте). Открытие подобных ссылок вы должны делать самостоятельно, обрабатывая событие браузера ShouldStartLoadWithRequest.
    Вот код:
    uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, {$IFDEF ANDROID} Androidapi.JNI.Net, Androidapi.JNI.GraphicsContentViewText, Androidapi.Helpers, {$ENDIF ANDROID} FMX.Types, FMX.Graphics, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls, FMX.WebBrowser, FMX.Controls.Presentation; ..... const ConstMainURL = 'http://www.docme.ru/contacts'; .... procedure THeaderFooterForm.WebBrowser1ShouldStartLoadWithRequest( ASender: TObject; const URL: string); begin if Not URL.StartsWith(ConstMainURL) then // Если ссылка перехода отличается от базовой, то открываем ее через активити begin WebBrowser1.Stop; OpenURL(URL); end; end; procedure THeaderFooterForm.OpenURL(const AUrl: string); {$IFDEF ANDROID} var Uri: Jnet_Uri; OpenLinkIntent: JIntent; {$ENDIF ANDROID} begin {$IFDEF MSWINDOWS} ShellExecute(0, 'open', PChar(AUrl), nil, nil, SW_SHOWNORMAL); {$ENDIF MSWINDOWS} {$IFDEF ANDROID} Uri := StrToJURI(AUrl); OpenLinkIntent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_VIEW, Uri); TAndroidHelper.Activity.startActivity(OpenLinkIntent); {$ENDIF ANDROID} end; И прикрепляю ваш проект с моими изменениями
    WebPrj.zip
×
×
  • Создать...