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

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

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

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

  • Посещение

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

    100

Весь контент Евгений Корепов

  1. Возможно вы стали счастливым обладателем телефона Asus, или еще какого то производителя любящего решать за пользователя. Посмотрите в настройках андроида (настройки Приложения и Оповещения) - возможно там отключено оповещение о приходящих пушах. Только вот неделю назад разбирались с пользователем с такой же проблемой.
  2. Вывод на основе анализа кода: THTTPClient.Create влечет за собой создание целого букета объектов - TCookieManager, TURLClient, TCredentialsStorage, TObjectDictionary<string, TURLClient>, несколько TList в недрах этих объектов. Плюс нужно инициализировать параметры - таймауты, хидеры, акцепты, добавлять куки из глобального хранилища (если на сервере есть некая авторизация). Учитывая качество кода Эмбаркадеро, где то в этих объектах возможно есть утечки, так что создание/удаление нескольких тысяч экземпляров может привести к неясным последствиям. Но это мое предвзятое мнение. Для подобных задач предпочитаю создавать один (или стек из нескольких) объект с потоком, который обслуживает нужды приложения от старта до завершения.
  3. Поищите на этом форуме или в гугле TThreadedQueue - универсальный метод под все платформы.
  4. Вот вы же сейчас шутите да? Я просто взял код который не работал совсем (отрисовка после скрола - это совсем), и сделал из него 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
  5. Можно попробовать использовать обычный контрол и символы юникода http://www.fileformat.info/info/unicode/char/20d7/index.htm Попробовал ради интереса - Memo1.Lines.Add('v' + Char($20d7) + ' + ' + 'V' + Char($20d7)); Но результат не очень. То ли Дельфи не очень работает с комбинируемыми символами, толи шрифт надо подобрать (я на дефолтном пробовал).
  6. Проблемы не существует в принципе. Пытался разобраться в коде, но из за его полной не читабельности, плюнул, все удалил а написал заново. Решил для забавы использовать анонимный поток для загрузки картинки. По кнопке все грузится и сразу отображается. Никаких костылей не используется (ну кроме как 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
  7. В развернутом состоянии так и должно быть. Или у вас опечатка? Исходил из того что у вас опечатка и MultiView все же невидим. Не удалось воспроизвести вашу проблему. Все работает как задумано - кнопка Мастер показывает/скрывает MultiView. При открытом MultiView - элементы ListBox корректно реагируют на нажатия, при скрытом MultiView - клики по местам где были элементы ListBox корректно обрабатываются формой. Прикладываю тестовый проект (Rio). test159.zip
  8. Я бы рекомендовал, как вам уже подсказывали, использовать готовые механизмы уведомлений. Или вариант с TNotificationCenter - над треем будет появляться окошко с вашим уведомлением, клик по окошку вы можете произвольно обработать в приложении. Этот механизм "написан по быстрому" через пень-колоду, просто создает окно, не пытаясь взаимодействовать с платформой. Или использовать родной механизм Windows - winapi Shell_NotifyIcon, тогда вы получите еще и уведомления в центре уведомлений Windows Второй способ можно использовать с помощью многочисленных компонентов TTrayIcon: procedure TForm1.SetNotification(const ATitle, AMessage : String); begin FTrayIcon.BalloonTitle:=ATitle; FTrayIcon.BalloonHint:=AMessage; FTrayIcon.ShowBalloonHint; end;
  9. Гугль вроде не знает про это устройство, причина может быть в этом. Так же посмотрите ВСЕ разрешения приложения - к примеру приложение требует камеру, а на устройстве ее нет. Так же смотрю планшет с клавиатурой - тут тоже может быть траблы с поддержкой точскрина, с наличием фейковой эмуляции точскрина. Мне вот вообще не ясно, что заставляет пользователей покупать ребрендинговый мусор, а потом мучатся с ним весь срок существования устройства )
  10. В FMX координаты Single и они никак не преобразуются компилятором в Integer. Забудьте о пикселях, они не связаны с координатами. Представьте если бы координаты считались в целых пикселях - сделали вы перемещение под HD монитор, а пользователь запустил приложение на UHD мониторе - все ваши пиксельные расчеты превратятся в тыкву.
  11. Вот точно ничего не надо делать и тем более подключать. Эмбаркадера еще не дочитала новости гугля. Они пока еще за 2015 год переваривают ))))
  12. Не принимает файл, а создает. Вы должны создать сертификат для вашего конкретного приложения и сохранить его. 1. Нажимаете New KeyStore. 2. Выбираете/создаете папку для хранения сертификата и имя. Я предпочитаю создавать в папке с проектом. Эмба по умолчанию пытается заставить вас хранить все \AppData\Roaming\Embarcadero\BDS\20.0\ . Но тут можно легко накосячить и затереть уже имеющиеся сертификаты, после чего у вас возникнут сложности с обновлением приложений в маркете.
  13. Попробовал воспроизвести проблему у себя (Rio). Но не вышло - не известен интервал вашего таймера, и смущает Circle1.Position.X:=Circle1.Position.X+5 , координаты все таки Single, и пытаться делать "движение" без промежуточных кадров - 100% вы получите эффекты стробоскопа. Сделал изменения в вашем проекте - окружность двигается с возрастающей скоростью по увеличивающейся дуге. В начале движение плавное, но при увеличении дуги и скорости возникает интерференция. Тут надо выбирать между красотой и скоростью. Или писать 3D приложение. Можете менять значение FSpeed в FormCreate от 1 до 10 - чтоб долго не ждать "разгона". Вот листинг проекта: unit Unit25; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Effects, FMX.Controls.Presentation, FMX.StdCtrls, FMX.Objects; type TForm25 = class(TForm) Circle1: TCircle; Timer1: TTimer; procedure Timer1Timer(Sender: TObject); procedure FormCreate(Sender: TObject); private { Private declarations } public { Public declarations } FSpeed : Integer; FIterator : Integer; procedure SetPosition(APosition : TPosition); end; var Form25: TForm25; implementation {$R *.fmx} procedure TForm25.FormCreate(Sender: TObject); begin FSpeed:= 1; FIterator:= 1; with Circle1.Position do begin X:= 0; Y:= 0; end; Timer1.Interval:= 5; Timer1.Enabled:= True; end; procedure TForm25.SetPosition(APosition : TPosition); var AValue :extended; begin Inc(FIterator); AValue:= -(Pi / 180) * FIterator * FSpeed; APosition.X:= ABS(AValue * Cos(AValue)); APosition.Y:= Abs(AValue * Sin(AValue)); end; procedure TForm25.Timer1Timer(Sender: TObject); begin SetPosition(Circle1.Position); end; end.
  14. Убедитесь что в свойствах проекта, Deployment - Provisioning, у вас используется уникальный ключ. Т.е. для каждого проекта вы должны генерировать свой собственный ключ, и хранить его в отдельном файле/папке (к примеру в папке проекта).
  15. Прошу прощения, я ввел вас в заблуждение (тестировал на одном своем проекте). Открытие подобных ссылок вы должны делать самостоятельно, обрабатывая событие браузера 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
  16. На 10.3 отлично работает. Скиньте тестовый проект, проверю, может ошибка в чем то другом?
  17. Век живи, век учись. Спасибо, не знал о такой возможности. Поигрался в тестовом проекте - все действительно работает.
  18. Все отлично обрабатывается и открывается. Любой браузер по определению должен это делать. У вас должна быть правильная ссылка на электронную почту, в примеру <a href="mailto:putin@russia.gov">Напишите письмо президенту</a> P.S. Или так, если хотите видеть адрес <a href="mailto:putin@russia.gov">putin@russia.gov</a>
  19. С GlobalUseDirect2D := false; приложение и должно грузить процессор, вы же явно указываете что рисуем только процессором. Возможно стоит подумать о реализации приложения в VCL? Или пересмотреть архитектуру приложения? Я бы на вашем месте пошел одним из трех путей: 1. Лить логи в отдельный TStringList, и использовать Memo в качестве "окна" для просмотра содержимого TStringList. Ограничить количество строк в Мемо к примеру сотней и с помощью контролов и кода дать возможность гонять это "окно" по содержимому TStringList. Этот метод я использовал в одном приложении, которое должно было работать месяцами, с большим количеством логов. Метод вполне себя оправдал. Вот уважаемый krapotkin тоже намекал на примерно этот способ. 2. Отказался от Memo, все таки этот компонент предназначен для редактирования небольших текстов, а не скоростного вывода десятков тысяч строк логов. Посмотрите в строну ListView. Конечно для копирования части строки лога придется придумывать некий велосипед (к примеру по клику кидать строку в Edit/Memo и оттуда уже копировать). 3. Сделал бы отдельное приложение/систему для логов. К примеру основное приложение льет логи по TCP или UDP куда то в другое приложение (желательно на другом компьютере/хостинге, которое в свою очередь заточено по визуализацию исключительно логов. А лучше лить в sql базу как предложил уважаемый POV
  20. Все ваши проблемы решаются добавлением трех строчек в код: Перед началом работы, сразу задаем количество строк Memo.Lines.Capacity:=1000000; Мемо сразу зарезервирует в своем TStringList нужный объем. Это позволит сократить накладные расходы на добавление в несколько раз. Перед добавлением строк в Memo обязательно делаем Memo.BeginUpdate; Это отключит перерисовку и другие операции. После добавления строк в Memo обязательно делаем Memo.EndUpdate; Это отрисует все изменения которые мы произвели. Вот итоговый код (в форме еще глобальный счетчик FLinesCounter : Integer;): procedure TForm1.FormCreate(Sender: TObject); Var I : Integer; begin Memo.Lines.Capacity:=1000000; Timer.Interval:=10; FLinesCounter:=1; Memo.BeginUpdate; for I := 1 to 10000 do begin Memo.Lines.Add('Это тест ' + FLinesCounter.ToString); Inc(FLinesCounter); end; Memo.EndUpdate; end; procedure TForm1.Button1Click(Sender: TObject); begin Timer.Enabled:=True; end; procedure TForm1.Log(const AMessage : String); const ConsMaxLogSize = 50000; begin Memo.BeginUpdate; // while Memo.Lines.Count > ConsMaxLogSize do // Memo.Lines.Delete(0); Memo.Lines.Add(AMessage); Memo.GoToTextEnd; Memo.EndUpdate; end; procedure TForm1.TimerTimer(Sender: TObject); begin Log('Это тест ' + FLinesCounter.ToString); Inc(FLinesCounter); end; Код добавляет в Мемо 10 тысяч строк за примерно пол секунды. И добавление по таймеру 100 строк в секунду отнимает примерно 0% процессорного времени. Все будет работать без тормозов до разумного предела, при очень больший количествах строк вы столкнетесь с тормозами выделения памяти приложению, тут нужно будет использовать иные механизмы.
  21. Я бы на вашем месте создавал не картинку, а к примеру PDF, там не будет проблем с несколькими страницами
  22. Задался вопросом, нашел ответ, может кому пригодится. 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 Правда у меня только звук "чпок" на всех константах. Но мне он и был нужен )
  23. Подниму тему наверх. Кто нибудь пробовал это делать в 10.3 ? Что то перестала логика работать. Причем пуши висящие в шторке, теперь исчезают сами, при старте приложения. Толи поменялась логика работы то ли просто глюк. function TGCMPushService.GetStartupNotifications: TArray<TPushServiceNotification>; var LBundle: JBundle; begin LBundle := MainActivity.getStartupGCM; if LBundle <> nil then Result := TArray<TPushServiceNotification>.Create(TGCMPushServiceNotification.Create(LBundle)) else Result := nil; end; Вот тут MainActivity.getStartupGCM ВСЕГДА отдает NIL
  24. Спасибо огромное! Опять начал это делать, все забыл, каждый раз как заново ) Начал гуглить и тут раз - и моя же тебя с вашим ответом! Все помогло!
×
×
  • Создать...