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

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

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

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

  • Посещение

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

    100

Сообщения, опубликованные Евгений Корепов

  1. Переработал и запустил один из проектов в Берлине. Все хорошо кроме странного бага - не работает перемотка. Оттягивается список и возвращается на место как приклеенный. Всю голову уже сломал, не понимаю как победить. Может кто знает как добиться такого эффекта? Так я пойму как его устранить ;-) Видео прикладываю:

    https://www.youtube.com/watch?v=R3ESp5RweEY

  2. Оказывается эта проблема была обнаружена еще  26/Apr/16 12:29 AM  https://quality.embarcadero.com/browse/RSP-14307 . И пока доступен только патч с хелперами https://quality.embarcadero.com/secure/attachment/17147/RSP14307.patch.zip

    Написана что проблема решена в Berlin Update 1. Но как понимаю этого апдейта еще не было?

  3. Начал переносить проекты из XE8 в Berlin, столкнулся с странным затыком в простеньком коде - делаем запрос на сайт, получаем куки, делаем post авторизацию, получаем редирект, если все хорошо, то октрываем страницу из header Location. Выяснилось что не смотря на HTTPClient.AllowCookies:=True, в HTTPResponse.Cookies всегда пустота. Пришлось копать исходники. Вот что обнаружилось в source\rtl\net\System.Net.HttpClient.pas:

    procedure THTTPClient.ExecuteHTTPInternal(const ARequest: IHTTPRequest; const AContentStream: TStream; const AResponse: IHTTPResponse);
    var
      LRequest: THTTPRequest;
      LResponse: THTTPResponse;
      State: THTTPState;
      LExecResult: TExecutionResult;
      LClientCertificateList: TCertificateList;
      OrigSourceStreamPosition: Int64;
      OrigContentStreamPosition: Int64;
      OrigContentStreamSize: Int64;
      Status: Integer;
      LCookieHeader: string;
    begin
      LResponse := AResponse as THTTPResponse;
      LRequest := ARequest as THTTPRequest;
      OrigSourceStreamPosition := 0;
      if LRequest.FSourceStream <> nil then
        OrigSourceStreamPosition := LRequest.FSourceStream.Position;
    
      if AContentStream <> nil then
      begin
        OrigContentStreamPosition := AContentStream.Position;
        OrigContentStreamSize := AContentStream.Size;
      end
      else
      begin
        OrigContentStreamPosition := 0;
        OrigContentStreamSize := 0;
      end;
    
      State := Default(THTTPState);
      LClientCertificateList := TCertificateList.Create;
      try
        while True do
        begin
          LRequest.DoPrepare;
    
          // Add Cookies
          if FCookieManager <> nil then
          begin
            LCookieHeader := FCookieManager.CookieHeaders(LRequest.FURL);
            if LCookieHeader <> '' then
              LRequest.AddHeader('Cookie', LCookieHeader);  // do not localize
          end;
    
          if not SetServerCredential(LRequest, LResponse, State) then
            Break;
          if not SetProxyCredential(LRequest, LResponse, State) then
            Break;
    
          if LRequest.FSourceStream <> nil then
            LRequest.FSourceStream.Position := OrigSourceStreamPosition;
    
          if LResponse <> nil then
          begin
            LResponse.FStream.Position := OrigContentStreamPosition;
            LResponse.FStream.Size := OrigContentStreamSize;
          end;
    
          LExecResult := DoExecuteRequest(LRequest, LResponse, AContentStream);
          case LExecResult of
            TExecutionResult.Success:
              begin
                if not SameText(LRequest.FMethodString, sHTTPMethodHead) then
                  LResponse.DoReadData(LResponse.FStream);
                Status := LResponse.GetStatusCode;
                case Status of
                  200:
                    begin
                      Break;  // Если запрос удачен, то выходим из цикла
                    end;
                  401:
                    begin
                      State.Status := InternalState.ServerAuthRequired;
                    end;
                  407:
                    begin
                      State.Status := InternalState.ProxyAuthRequired;
                    end;
                  else
                    begin
                      case Status of
                        301..304, 307:
                          if FHandleRedirects and (LRequest.FMethodString <> sHTTPMethodHead) then
                          begin
                            Inc(State.Redirections);
                            if State.Redirections > FMaxRedirects then
                              raise ENetHTTPRequestException.CreateResFmt(@SNetHttpMaxRedirections, [FMaxRedirects]);
                          end
                          else
                            Break;
                      else
                      end;
                      State.Status := InternalState.Other;
                      if DoProcessStatus(LRequest, LResponse) then
                        Break;
                    end;
                end;
              end;
            TExecutionResult.ServerCertificateInvalid:
              begin
                DoValidateServerCertificate(LRequest);
              end;
            TExecutionResult.ClientCertificateNeeded:
              begin
                DoNeedClientCertificate(LRequest, LClientCertificateList);
              end
            else
              raise ENetHTTPClientException.CreateRes(@SNetHttpClientUnknownError);
          end;
    
          if AllowCookies then  
            UpdateCookiesFromResponse(LResponse); // Вот эта, критически важная процедура, при Status=200 никогда не выполняется
        end;
     // После выхода из цикла попадаем сюда
        if LRequest.FSourceStream <> nil then
          LRequest.FSourceStream.Seek(0, TSeekOrigin.soEnd);
        LResponse.FStream.Position := OrigContentStreamPosition;
      finally
        LClientCertificateList.Free;
      end;
    end;

    Т.е. разработчики исключили выполнение UpdateCookiesFromResponse(LResponse), которая помещает куки из ответа в HTTPClient.

    А вот код из XE8 который нормально работает с Cookies:

    function THTTPClient.ExecuteHTTPInternal(const ARequest: IHTTPRequest; const AContentStream: TStream): IHTTPResponse;
    var
      LRequest: THTTPRequest;
      LResponse: THTTPResponse;
      State: THTTPState;
      LExecResult: TExecutionResult;
      LClientCertificateList: TCertificateList;
      OrigSourceStreamPosition: Int64;
      OrigContentStreamPosition: Int64;
      OrigContentStreamSize: Int64;
      Status: Integer;
      LCookieHeader: string;
    begin
      Result := nil;
      LResponse := nil;
      LRequest := ARequest as THTTPRequest;
      OrigSourceStreamPosition := 0;
      if LRequest.FSourceStream <> nil then
        OrigSourceStreamPosition := LRequest.FSourceStream.Position;
    
      if AContentStream <> nil then
      begin
        OrigContentStreamPosition := AContentStream.Position;
        OrigContentStreamSize := AContentStream.Size;
      end
      else
      begin
        OrigContentStreamPosition := 0;
        OrigContentStreamSize := 0;
      end;
    
      State := Default(THTTPState);
      LClientCertificateList := TCertificateList.Create;
      try
        while True do
        begin
          LRequest.DoPrepare;
    
          // Add Cookies
          if FCookieManager <> nil then
          begin
            LCookieHeader := FCookieManager.CookieHeaders(LRequest.FURL);
            if LCookieHeader <> '' then
              LRequest.AddHeader('Cookie', LCookieHeader);  // do not localize
          end;
    
          if not SetServerCredential(LRequest, LResponse, State) then
            Break;
          if not SetProxyCredential(LRequest, LResponse, State) then
            Break;
    
          if LRequest.FSourceStream <> nil then
            LRequest.FSourceStream.Position := OrigSourceStreamPosition;
    
          if LResponse <> nil then
          begin
            LResponse.FStream.Position := OrigContentStreamPosition;
            LResponse.FStream.Size := OrigContentStreamSize;
          end;
    
          LExecResult := DoExecuteRequest(LRequest, LResponse, AContentStream);
          case LExecResult of
            TExecutionResult.Success:
              begin
                if not SameText(LRequest.FMethodString, sHTTPMethodHead) then
                  LResponse.DoReadData(LResponse.FStream);
                Status := LResponse.GetStatusCode;
                case Status of
                  200:
                    begin
                      Break;
                    end;
                  401:
                    begin
                      State.Status := InternalState.ServerAuthRequired;
                    end;
                  407:
                    begin
                      State.Status := InternalState.ProxyAuthRequired;
                    end;
                  else
                    begin
                      case Status of
                        301..304, 307:
                          if FHandleRedirects and (LRequest.FMethodString <> sHTTPMethodHead) then
                          begin
                            Inc(State.Redirections);
                            if State.Redirections > FMaxRedirects then
                              raise ENetHTTPRequestException.CreateResFmt(@SNetHttpMaxRedirections, [FMaxRedirects]);
                          end
                          else
                            Break;
                      else
                      end;
                      State.Status := InternalState.Other;
                      if DoProcessStatus(LRequest, LResponse) then
                        Break;
                    end;
                end;
              end;
            TExecutionResult.ServerCertificateInvalid:
              begin
                DoValidateServerCertificate(LRequest);
              end;
            TExecutionResult.ClientCertificateNeeded:
              begin
                DoNeedClientCertificate(LRequest, LClientCertificateList);
              end
            else
              raise ENetHTTPClientException.CreateRes(@SNetHttpClientUnknownError);
          end;
    
        end;
        if LRequest.FSourceStream <> nil then
          LRequest.FSourceStream.Seek(0, TSeekOrigin.soEnd);
        if AllowCookies then
          UpdateCookiesFromResponse(LResponse);  // Здесь все верно, процедура за пределами цикла и выполняется всегда когда нужно.
      finally
        LClientCertificateList.Free;
        Result := IHTTPResponse(LResponse);
      end;
    end;

    А теперь вопрос: ну как так то? В продукте за 54 тысячи рублей сильно обидно исправлять такие косяки. Такое ощущение что разраб подрабатывал на стороне в проектах на php и забыл переключится на другой язык, там break прерывает работу аналога case и код работал бы правильно.

     

  4. В 30.06.2016 в 12:57, krapotkin сказал:

    я еще раз положу рабочий проект для Delphi Berlin. там есть отличия от общепринятого метода

    ListViewTestBerlin.7z

    Отличный пример, спасибо! А UListItemElements это просто праздник какой то, даже не представлял что так можно. Не подскажете источник информации по TListItemDrawable, TListViewItem.Adapter и Objects.FindObjectT? В справке эмбаркадеры информация разнится от "Embarcadero Technologies does not currently have any additional information" до "Triggers the OnResetView event, passes the specified list item to its event handler, and calls DoResetView." что не особо раскрывает тему.

  5. Нововведения конечно впечатляют! Руки чешутся все испробовать.

    Правда некоторые примеры не работают (их кстати тоже еле отыскал в недрах профиля) :-( И удивляет стиль написания примеров. С каждой версией все хуже хуже, в XE8 они были избыточно запутанными мусорным кодом, а теперь еще одно нововведение - имена переменных почти в стиле Turbo Pascal. Ну почему нужно делать TS: TToggleSwitch вместо ToggleSwitch: TToggleSwitch, и далее по тексту grpColors, lblColor, cbxColor, lblThumbColor, grpState, grpAlignment. Это ведь пример, а не образец кода с понтами типа "куллхацкер". При изучении кода приходится выяснять что FClient это THTTPClient, а не какой либо другой клиент.

    Долгожданные сервисы почему то не заботают, пример AndroidNotificationServiceDemo ничего не делает. А вот DownloadServiceDemo прекрасно заработал, отлично!

  6. Самое смешное что у меня для таких случаем настроена переменная среды окружения TEMP=D:\Temp\System_Temp\, но видимо писатели не стали парится с их использованием, а написали нечто вроде Куда_срем := Папка_профиля + 'AppData\Local\Temp';

    Установить в итоге удалось выделив на системном около 45 гигов. Но мусор на системном диске остался, буду разбираться как его переносить. Интересно, хоть кто нибудь пишет проекты в папке профиля Documents\Embarcadero\Studio\Projects, по станной задумке эмбаркадеры? Нужно больше вложенных папок! Глубже, еще глубже! Все еще не достаточно глубоко! ;-)

    С трудом нашел Android SDK C:\Users\Public\Documents\Embarcadero\Studio\18.0\CatalogRepository\AndroidSDK-24.3.3_GIB.Build.22858.6822, хотя не уверен что это используемая средой копия, слишком мало вложенных папок, нужно копать глубже ;-)

  7. Получил сегодня ключи от Берлина, на радостях начал установку. Как и у многих, конфигурация типичная - SSD 128Gb в качестве системного, и большой HDD в качестве рабочего. На SSD было 20 гигов свободного места, студию естественно устанавливал в D:\Embarcadero\Studio\18.0.

    Установка закончилась падением винды. Убогий установщик Берлина засрал системный диск полностью, распихав по системному диску 30 гигов мусора и обрушил систему. Посмотреть бы в глаза разработчику этого убожества. Почему нельзя ставить студию туда куда указано? Для чего такой кривой интерфейс установщика, скрин прилагаю, надеюсь ниже обезанного пункта в списке ничего нет что мне понадобится. Почему нельзя использовать Application.ProcessMessage для долгих операций, чтоб винда по нескольку раз не говорила что приложение не отвечает, давай ее убъем. Пока складывается ощущение что я выкинул 54 тысячи на ветер, если среда исполнена теми же авторами, то чувствую весь ад еще впереди.

    001.png

  8. 9 минут назад, golomeen сказал:

    я забыл у них апгрейдом считается только переход с прошлых версий?

    а то я захалявил стартер, теперь хочу с него апгрейд с аддоном за 53 :) - не выйдет?

    ОБНОВЛЕНИЕ NAMED (UPGRADE) С РЕДАКЦИИ STARTER + BONUS PACK + MOBILE ADD-ON PACK В ПОДАРОК ДО 15.09.2016 Цена 75 999,00 руб

    UPDATE: Обновление с стартер возможно и с той же версии. Я так на XE7 Proffesional обновлялся. Но лучше позвонить и узнать. Но звоните не в allsoft, там никогда ничего не знают, а в отдел продаж эмбаркадеры.

  9. Один из способов Edit1.KillFocusByReturn:=True;

    Или можно использовать более гибкий метод, скрывать клавиатуру когда вам захочется:

    В Uses добавьте FMX.VirtualKeyboard, FMX.Platform. Код примерно такой:

    procedure TForm1.Edit1ChangeTracking(Sender: TObject);
    begin
      If Edit1.Text.ToLower.Contains('карамба') Then
        HideVirtualKeyboard;
    end;
    
    procedure TForm1.HideVirtualKeyboard;
    var Keyboard: IFMXVirtualKeyboardService;
    begin
      if TPlatformServices.Current.SupportsPlatformService( IFMXVirtualKeyboardService, IInterface( Keyboard ) ) then
        Keyboard.HideVirtualKeyboard;
    end;

     

  10. Вот кусок кода исходников из XE8 unit FMX.Controls.Model :

    procedure TDataModel.SetData(const Index: string; const Value: TValue);
    var
      DataRecord: TDataRecord;
    begin
      if FDataSource = nil then
        FDataSource := TDictionary<string, TValue>.Create;
      if Value.IsEmpty then
        FDataSource.Remove(Index)
      else
        FDataSource.AddOrSetValue(Index, Value);
    
      DataRecord := TDataRecord.Create(Index, Value);
      SendMessage<TDataRecord>(MM_DATA_CHANGED, DataRecord);
    end;

    Судя по коду, нужно делать как то так :

    procedure TForm1.FormCreate(Sender: TObject);
    begin
      Edit1.Model.Data['aaa']:=TStringList.Create;
      Edit1.Model.Data['aaa'].AsType<TStringList>.Add('111');
      Edit1.Model.Data['aaa'].AsType<TStringList>.Add('222');
      Edit1.Model.Data['aaa'].AsType<TStringList>.Add('333');
      Edit1.Model.Data['aaa'].AsType<TStringList>.Clear;
      Edit1.Model.Data['aaa'].AsType<TStringList>.Free;
      Edit1.Model.Data['aaa']:=TValue.Empty;
      if Edit1.Model.Data['aaa'].IsEmpty Then
        beep;
    end;

     

  11. 8 минут назад, Kitty сказал:

    Читаю статью и такой вопрос - а можно ли при обновлении поменять также имя пакета com.embarcadero.$(ModuleName). Например на другое ru.beacon.$(ModuleName)

    Или имя пакета при обновлении менять нельзя?

    Нет, нельзя. Имя пакета это главный идентификатор приложения, создается раз и навсегда. Даже при блокировке приложения и повторной публикации имя пакета нужно другое. Единственное исключение, если вам заблокировали приложение, но вы доказали свою правоту, то спустя неопределенное количество времени (в моем случае почти два месяца), можно опубликовать приложение с тем же именем пакета.

  12. У них сейчас еще одна замечательная акция - Delphi 10.1 Berlin Professional Обновление Named (Upgrade) + Bonus Pack + Mobile Add-on Pack в подарок до 15.09.2016

    Я воспользовался. Хватит уже сидеть на XE8, вливаюсь в ряды берлинщиков :-)

    Бонус пак кстати приятный - Konopka Signature VCL Controls, Radiant Shapes, VCL & FireMonkey Premium Styles и Marco Cantu’s Object Pascal handbook. Не знаю пригодится ли  Konopka и Radiant, но книгу как раз в отпуске на море буду читать. 

  13. 11 часов назад, krapotkin сказал:

    не согласен. совсем. в родной разработке для андроидов у листа ВСЕГДА присутствует дата адаптер. и это жжж неспроста.

    было несколько специальных лекций от разработчиков андроид, почему и как этим пользоваться...

    данные на экране хранить нельзя.

    Со всем уважением, но категоричность ваших высказываний вызывает удивление. Те же разработчики андроид, всего лишь люди, а не апостолы религиозной секты, которым нужно слепо подчиняться. Для меня "нельзя" - лишь повод проверить почему нельзя, и выяснить что оказывается можно, еще как можно.

    В качестве примера приведу одно из своих приложений - Инвентаризация для узкого круга заказчиков. В Т.З. были довольно жесткие ограничения по цене устройств, обязательная оффлайн работа, с последующей синхронизацией, слабые, нестабильные каналы этой самой синхронизации и довольно большой объем данных (от 60 до 100 тысяч наименований) с которым нужно работать. Естественно заказчик хотел мгновенного отклика от приложения. Мои конкуренты пошли по пути завещанному вебинарами и правильными теоретиками - база данных на устройстве, дата-адаптер, визуализация. На тестовом пакете данных в 50к наименований это было жалкое и печальное зрелище, минимум трехсекундные интервалы после любого телодвижения. Импорт данных (из csv файла) занимал несколько минут, тесовые устройства грелись и сажали батарею. Естественно они сдулись, заявив что выполнить задание не возможно.

    Было понятно, что конкурировать с бригадой "правильных" разработчиков в классической схеме мне не светит, поэтому пошел "запрещенным путём" - в приложении, помимо необходимых элементов управления был один ListView, которых адаптировался под текущую задачу и хранил все данные. Результат был потрясающий. Мгновенная реакция на поиск, фильтры. Для смеха использовал пакет данных с 150 тысячами записей. Демонстрировал на Samsung GT-S7390 https://market.yandex.ru/product/10542116/spec?hid=91491&track=char . Импорт/экспорт занимал около 30 секунд. Честно говоря я снял шляпу перед разработчиками ListView. Приложение успешно работает по сей день, конечно спустя полтора года я бы многое изменил/дополнил (особенно доступ ко всем данным несмотря на фильтрацию, тогда не нашел решения кроме ее временного отключения, выполнения действий, включения, недавно опубликовал здесь найденное решение), но ListView оставил бы в качестве хранилища.

  14. 1 час назад, ZuBy сказал:

    эм, как-бы вот (без БД, регистрации и смс)

    
      FilterJSON := aData;
      if (not menu_ids.IsEmpty) and (menu_ids <> ',,') then
      begin
        xFilter := SO(FilterJSON);
        aStatus := '{"status":"' + xFilter.S['status'] + '","struct":';
    
        FilterJSON := xFilter.A['struct'].Where(
          function(Arg: IMember): Boolean
          begin
            with Arg.AsObject do
              Result := Pos(S['id_menu'], menu_ids) > 0;
          end).AsJSON;
        FilterJSON := aStatus + FilterJSON + '}';
      end;

    на выходе у нас FilterJSON только с теми данными, которые нужны (фильтруется по id_menu)

    Повторю вопрос - для чего? ListView мощный инструмент способный превосходно хранить данные и манипулировать ими. В определенных задачах естественно. Искусственное раздувание требуемых объемов оперативной памяти путем хранения трех-четырех дубликатов необходимых данных может и оправдано на настольных платформах, но в мобильных приложениях резко ограничит количество потенциальных пользователей.

    Напомню - тема всего лишь о простом решении продвинутой фильтрации в ListView. Давайте не будем раздувать флейм о моделях хранения.

  15. 3 часа назад, krapotkin сказал:

    это не дополнительные. откуда данные появляются в листе?

    вот там и место для фильтраций

    К примеру данные получены от удаленного сервера в виде json. Для чего в таком случае плодить прокладки в виде баз данных, увеличивая объем и сложность приложения в два раза? 

  16. 7 часов назад, krapotkin сказал:

    а я вот все-таки подхожу к фильтрациям с т.з. не listItems, а данных, из которых они создаются

    получается все просто и без вывертов

    Я руководствуюсь принципом бритвы Оккама - не использую дополнительные сущности (базы данных к примеру), без крайней на то необходимости ;-)

  17. Один из вариантов вызывать активити вот так:

    procedure ShareFile(aFileName, aComment : String);
    var Intent    : JIntent;
        uri       : Jnet_Uri;
        AttachmentFile: JFile;
        S : String;
    begin
      Intent := TJIntent.Create;
      Intent.setAction(TJIntent.JavaClass.ACTION_SEND);
      Intent.setFlags(TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK);
      S:=TPath.GetFileName(aFileName);
      Intent.putExtra(TJIntent.JavaClass.EXTRA_SUBJECT, StringToJString(aComment));
      Intent.putExtra(TJIntent.JavaClass.EXTRA_TEXT, StringToJString(aComment));
      AttachmentFile := TJFile.JavaClass.init(StringToJString(aFileName));
      Uri := TJnet_Uri.JavaClass.fromFile(AttachmentFile);
      Intent.putExtra(TJIntent.JavaClass.EXTRA_STREAM, TJParcelable.Wrap((Uri as ILocalObject).GetObjectID));
      Intent.setType(StringToJString('text/plain'));
    //  Intent.setDataAndType(StrToJURI('file:' + TPath.Combine(TPath.GetSharedDownloadsPath, 'picture.png')), StringToJString ('image/png'));
      SharedActivity.startActivity(Intent);
    end;

    Пользователю нужно будет выбрать что именно использовать, список будет из всего что установлено (гугледиск, дропбокс и т.д.). Можно вызывать активити для определенного приложения, но под рукой примера нет.

  18. Возникла задача поиска (фильтрации) в ListView по нескольким критериям. К примеру нужно искать по Item.Text, Item.Detail и Item.Date['Category']. Пошел путем, ранее подсказанным на этом форуме - правкой "function TListViewItems.ApplyFilter: Boolean" в FMX.ListView.pas. Передавать строку поиска хотел в строке типа "Text=Пеньки&Detail=по три рубля&Category=Осиновые", но быстро понял что код превращается в одноразовую кашу, а хотелось сделать красиво и на будущее.

    Сделал так - добавил еще один тип "TFilterPredicateEx = TPredicate<TListViewItem>;", к существующему "TFilterPredicate = TPredicate<string>;" в TListViewItems. Ну и далее по списку добавил аналоги к переменным, функциям и property.

    Смысл в том чтобы вместо обычного бутылочного горлышка в фильтации:

      ListView.Items.Filter:=
        function(X: string): Boolean
        begin
          Result := AFilterText.IsEmpty or X.ToLower.Contains(AFilterText.ToLower);
        end;

    иметь мощный инструмент с доступом ко всем плюшкам TListViewItem. Теперь это выглядит вот так:

    type
      TListViewFilterEx = record
        Category : String;
        Name : String;
      end;
    ...
    Var AFilter : TListViewFilterEx; // это для удобства хранения фильтров в одном месте
    ...
     AFilter.Category:='Береза';
     AFilter.Name:='Пеньки';
    AListView.Items.FilterEx:=
        function(X: TListViewItem): Boolean
        begin
          Result:=
            (AFilter.Category.IsEmpty or X.Data['Category'].AsString.ToLower.Contains(AFilter.Category.ToLower)) And
             ((AFilter.Name.IsEmpty or X.Text.ToLower.Contains(AFilter.Name.ToLower)) or
             (AFilter.Name.IsEmpty or X.Detail.ToLower.Contains(AFilter.Name.ToLower)));
        end;

    Подозреваю что все это можно было сделать с помощью хелперов, но до их освоения руки никак не дойдут. Если кто то сделает хелпер, дабы не править FMX.ListView.pas, буду очень благодарен.

    Внимание! FMX.ListView.pas от Delphi XE8, к другим версиям думаю не подойдет.

    Прилагаю дополненный FMX.ListView.pas к сообщению.

    FMX.ListViewWithFilterEx.zip

  19. Операция Result := lApiResponse.ResultObject; копирует в Result ссылку на тот же самый объект. И логично что при попытке освободить его функция вернет пустоту, так как вы освобождаете и результат функции. Как вариант можно использовать функцию Assign - копирование объекта в Result, тогда освобождение lApiResponse.Free не затронет результат возвращаемый функцией.

  20. Решил проблему следующим образом:

    ListViewItem создаю в массиве ListViewItemArray : TArray<TListViewItem>, вот таки образом 

    ListViewItemArray[I]:=AListView.Items.Add;

    В итоге имею массив для доступа к ListViewItem, вне зависимости от текущего состояния фильтрации в ListView. Костыль конечно, но другого способа не нашел. Да и вроде по ресурсам и памяти нагрузки никакой, добавился только массив ссылок на объекты.

  21. Я бы отделил логику от интерфейса. Проще создать двумерный массив (или сразу класс) для доступа к чекбоксам и остальному. Вот пример кода:

    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.DateTimeCtrls, FMX.Controls.Presentation, FMX.StdCtrls, FMX.Layouts,
      FMX.ListBox;
    
    type
      TWeekCheckBox = array[1..7] of TCheckBox;
    
      TMyItem = record
        TimeStart : TTimeEdit;
        TimeEnd : TTimeEdit;
        WeekCheckBoxArray : TWeekCheckBox;
      end;
    
      TForm1 = class(TForm)
        ListBox: TListBox;
        procedure FormCreate(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
        MyItems : TArray<TMyItem>;
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.fmx}
    
    procedure TForm1.FormCreate(Sender: TObject);
    Var I, J : Integer;
        ListBoxItem : TListBoxItem;
    begin
      SetLength(MyItems,7);
      for I := 0 to Length(MyItems)-1 do
      begin
        ListBoxItem:=TListBoxItem.Create(ListBox);
        MyItems[I].TimeStart:=TTimeEdit.Create(ListBoxItem);
        ListBoxItem.AddObject(MyItems[I].TimeStart);
        MyItems[I].TimeEnd:=TTimeEdit.Create(ListBoxItem);
        ListBoxItem.AddObject(MyItems[I].TimeEnd);
        for J := 1 to 7 do
        begin
          MyItems[I].WeekCheckBoxArray[J]:=TCheckBox.Create(ListBoxItem);
          MyItems[I].WeekCheckBoxArray[J].Align:=TAlignLayout.Left;
          ListBoxItem.AddObject(MyItems[I].WeekCheckBoxArray[J]);
        end;
        ListBox.AddObject(ListBoxItem);
      end;
    
    end;
    
    end.

    Естественно нужно еще все контролы расставить как вам нужно. В итоге вы имеете двумерный массив, с помощью которого можете с легкостью обращаться к нужным элементам. Для наглядности туда же добавил и TTimeEdit подсмотренные на вашем скриншоте.

  22. Как получить доступ к отфильтрованным элементам ListView. Ситуация такая - ListView частично сформирован и показан пользователю, он может начать поиск через SearchBox, но потоки должны продолжать заполнение некоторых данных, не критичных для работы. Но дело в том, что получить доступ по индексу, к отфильтрованному итему нельзя, а нужно. Что делать? Может есть другой путь получить нужный ListViewItem?

  23. Как узнать количество свободного места на "диске"? К примеру получаю путь System.IOUtils.TPath.GetCachePath, и перед тем как туда писать, хочу узнать есть ли и сколько свободного места. С наскока не нашел ответ.

    Заранее благодарю!

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