Перейти к содержанию

Вопросы

На незначительном количестве устройств, менее 0,1%, получаю ошибку "Bitmap size too big" при AImage.Bitmap.LoadFromStream(AMemoryStream). Подозреваю что ошибка происходит на слабых устройствах. Картинка 250х250 png. Код выполняется в основном потоке (в интернетах были упоминания что глючит эта операция в отдельном потоке на каких то версиях Delphi).

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Рекомендуемые сообщения

  • 0

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

кстати, да была такая проблема. с фотика шла картинка которая не могла загружаться в битмап из за своих размеров. НО! при этом другие программы ее спокойно показывали и работали с этой картинкой. интеерсно почему на яве программы могли с ней работать а огнеобезьяна упирается в ограничения. 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
1 час назад, haword сказал:

кстати, да была такая проблема. с фотика шла картинка которая не могла загружаться в битмап из за своих размеров. НО! при этом другие программы ее спокойно показывали и работали с этой картинкой. интересно почему на яве программы могли с ней работать а огнеобезьяна упирается в ограничения. 

Причин несколько:

  1. Ява сама не может отобразить большие изображения так же. Но там причина ограничение по потребляемой памяти. Поэтому если открыть документацию, то можно увидеть специальные методы и рекомендации для сжатия размера для отображения на экран. Вместо того, что выводить картинку в 10 МгП на экран, апи выдает картинку 64 на 64 или тд.
  2. В FireMonkey есть два типа для работы с изображениями. 
    1. TBitmap - построен на использовании текстур. Отсюда и идет ограничение на размер в зависимости от аппаратной части девайса.
    2. TBitmapSurface - не зависит ни от чего, кроме, как от памяти. Этот тип полностью совместим с TBitmap - это значит, что можно копировать изображения друг в друга. Это тип создан для хранения, как раз больших изображений.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Можно я уточню?
Можно спокойно забыть про TBitmap и везде использовать TBitmapSurface ?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
В 3/29/2016 в 11:55, Brovin Yaroslav сказал:

На тестовом устройстве с помощью кода

Var MaxWidthHeight: Integer;
begin
  MaxWidthHeight := TCanvasManager.DefaultCanvas.GetAttribute(TCanvasAttribute.MaxBitmapSize);

получаю значение 4096. В каких попугаях это число? Судя по MaxWidthHeight это высота изображения? Как получить тогда ширину? Или его размер в байтах? Хотя это врядли, на этом устройстве успешно загружаются картинки в 200 килобайт.

 

В 3/29/2016 в 13:29, Brovin Yaroslav сказал:

Причин несколько:

  1. Ява сама не может отобразить большие изображения так же. Но там причина ограничение по потребляемой памяти. Поэтому если открыть документацию, то можно увидеть специальные методы и рекомендации для сжатия размера для отображения на экран. Вместо того, что выводить картинку в 10 МгП на экран, апи выдает картинку 64 на 64 или тд.
  2. В FireMonkey есть два типа для работы с изображениями. 
    1. TBitmap - построен на использовании текстур. Отсюда и идет ограничение на размер в зависимости от аппаратной части девайса.
    2. TBitmapSurface - не зависит ни от чего, кроме, как от памяти. Этот тип полностью совместим с TBitmap - это значит, что можно копировать изображения друг в друга. Это тип создан для хранения, как раз больших изображений.

Подскажите наиболее правильный путь в моей ситуации - на форме лежит TImage и нужно загрузить в него картинку:

  1. Проверять максимальный размер картинки, если загрузить не удастся, то отлупливать пользователя фразой "Нищебродам вход воспрещен".
  2. Переписать класс TImage на использование TBitmapSurface вместо TBitmap
  3. Загружать картинку сначала в TBitmapSurface, изменять размер и копировать в визуальный Image.Bitmap. Вот тут облом - если не ошибаюсь TBitmapSurface не умеет абсолютно ничего, никаких  LoadFromStream (как в него вообще загрузить внешнее изображение?) только readonly свойства и методы.
Отредактировал Евгений Корепов

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

скорей всего 4096x4096 это размер в пикселях

а в битах думаю вот так 4096x4096x24/32(смотря какая глубина цвета используется)

Отредактировал ZuBy

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
В 29.03.2016 в 17:27, Евгений Корепов сказал:

Подскажите наиболее правильный путь в моей ситуации - на форме лежит TImage и нужно загрузить в него картинку:

  1. Проверять максимальный размер картинки, если загрузить не удастся, то отлупливать пользователя фразой "Нищебродам вход воспрещен".
  2. Переписать класс TImage на использование TBitmapSurface вместо TBitmap
  3. Загружать картинку сначала в TBitmapSurface, изменять размер и копировать в визуальный Image.Bitmap. Вот тут облом - если не ошибаюсь TBitmapSurface не умеет абсолютно ничего, никаких  LoadFromStream (как в него вообще загрузить внешнее изображение?) только readonly свойства и методы.
  1. Нет. Это значит, что не нужно загружать 4 Мб фотографию в TImage с целью отобразить ее в итоге в размере 400х200.
  2. Нет, просто присваивайте в Bitmap TBtmapSurface. Bitmap.Assign(BitmapSurface)
  3. Методы по загрузки из файлов и потоков есть. Смотрите TBitmapCodecManager

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
6 часов назад, Brovin Yaroslav сказал:
  1. Нет. Это значит, что не нужно загружать 4 Мб фотографию в TImage с целью отобразить ее в итоге в размере 400х200.
  2. Нет, просто присваивайте в Bitmap TBtmapSurface. Bitmap.Assign(BitmapSurface)
  3. Методы по загрузки из файлов и потоков есть. Смотрите TBitmapCodecManager

Огромное вам спасибо! Стало много понятнее. Повторюсь: картинки 250х250 png. Сделал процедуру:

Uses FMX.Graphics, FMX.Surfaces, FMX.Types;


function GetMaxImageSize : Integer;
begin
  Result:=TCanvasManager.DefaultCanvas.GetAttribute(TCanvasAttribute.MaxBitmapSize);
end;

procedure CheckAndLoadFromStream(const AStream : TStream; const ABitmap : TBitmap);
Var MaxImageSize : Integer;
    ABitmapSurface : TBitmapSurface;
begin
  ABitmapSurface:=TBitmapSurface.Create;
  AStream.Position:=0;
  TBitmapCodecManager.LoadFromStream(AStream,ABitmapSurface);
  MaxImageSize:=GetMaxImageSize;
//  MaxImageSize:=64;
  if ABitmapSurface.Height>MaxImageSize then
    ABitmapSurface.SetSize(MaxImageSize,MaxImageSize,ABitmapSurface.PixelFormat);
  if Assigned(ABitmap) then
    ABitmap.Assign(ABitmapSurface);
  ABitmapSurface.Free;
end;

Все отлично работает, до момента раскоментирования строчки   "MaxImageSize:=64" (имитация слабого устройства) - процедура ABitmapSurface.SetSize устанавливает новый размер, но изображение при этом теряется, на форме, в TImage пустота. Видимо я что то не так делаю :-( Перебрал все возможные ABitmapSurface.PixelFormat - результата нет.

Следующий код

  ABitmapSurface:=TBitmapSurface.Create;
  TBitmapCodecManager.LoadFromFile('d:\source.png',ABitmapSurface);
  MaxImageSize:=250;
  ABitmapSurface.SetSize(MaxImageSize,MaxImageSize);
  TBitmapCodecManager.SaveToFile('d:\source_resize.png', ABitmapSurface);

Из файла source.png размером 79642 байта делает файл source_resize.png размером 351 байт. Изображение исчезает.

Что я делаю не так? Заранее благодарю за ответ!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Сам себе отвечаю, процедура procedure TBitmapSurface.SetSize(const AWidth, AHeight: Integer; const APixelFormat: TPixelFormat), судя по коду:

procedure TBitmapSurface.SetSize(const AWidth, AHeight: Integer; const APixelFormat: TPixelFormat);
var  NumOfBytes: Integer;
begin
  FPixelFormat := APixelFormat;
  if FPixelFormat = TPixelFormat.None then
    FPixelFormat := TPixelFormat.BGRA;

  FBytesPerPixel := PixelFormatBytes[FPixelFormat];

  FWidth := Max(AWidth, 0);
  FHeight := Max(AHeight, 0);
  FPitch := FWidth * FBytesPerPixel;
  NumOfBytes := FWidth * FHeight * FBytesPerPixel;

  ReallocMem(FBits, NumOfBytes);
  FillChar(FBits^, NumOfBytes, 0);
end;

изменяет размер и просто забивает нулями содержимое Bitmap. Пойду копать дальше...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Ура! Заработало с TBitmapSurface.StretchFrom :

  ABitmapSurface:=TBitmapSurface.Create;
  ABitmapSurfaceResize:=TBitmapSurface.Create;
  TBitmapCodecManager.LoadFromFile('d:\source.png',ABitmapSurface); // png 250x250
  MaxImageSize:=50;
  ABitmapSurfaceResize.StretchFrom(ABitmapSurface,MaxImageSize,MaxImageSize);
  TBitmapCodecManager.SaveToFile('d:\source_resize.png', ABitmapSurfaceResize);

Все работает отменно :-)

Итоговый код для загрузки картинки из потока и устранения ошибки "Bitmap size too big":

Uses FMX.Graphics,  FMX.Surfaces;


function GetMaxImageSize : Integer;
begin
  Result:=TCanvasManager.DefaultCanvas.GetAttribute(TCanvasAttribute.MaxBitmapSize);
end;

procedure CheckAndLoadFromStream(const AStream : TStream; const ABitmap : TBitmap);
Var MaxImageSize : Integer;
    ABitmapSurface,ABitmapSurfaceResize : TBitmapSurface;
begin
  ABitmapSurface:=TBitmapSurface.Create;
  AStream.Position:=0;
  TBitmapCodecManager.LoadFromStream(AStream,ABitmapSurface);
  MaxImageSize:=GetMaxImageSize;
  if ABitmapSurface.Height>MaxImageSize then
  begin
    ABitmapSurfaceResize:=TBitmapSurface.Create;
    ABitmapSurfaceResize.StretchFrom(ABitmapSurface,MaxImageSize,MaxImageSize);
    if Assigned(ABitmap) then
      ABitmap.Assign(ABitmapSurfaceResize);
    ABitmapSurfaceResize.Free;
  end
  Else
    if Assigned(ABitmap) then
      ABitmap.Assign(ABitmapSurface);
  ABitmapSurface.Free;
end;

Всем спасибо за помощь! Особая благодарность Ярославу! 

P.S. Процедура для частного случая с квадратным изображением. Для меня осталось не до конца ясным значение возвращаемое TCanvasManager.DefaultCanvas.GetAttribute(TCanvasAttribute.MaxBitmapSize), исходил из догадки что это максимальный размер изображения по вертикали в пикселях. Может Ярослав поставит все точки и разъяснит этот вопрос?

Отредактировал Евгений Корепов

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
5 часов назад, Евгений Корепов сказал:

P.S. Процедура для частного случая с квадратным изображением. Для меня осталось не до конца ясным значение возвращаемое TCanvasManager.DefaultCanvas.GetAttribute(TCanvasAttribute.MaxBitmapSize), исходил из догадки что это максимальный размер изображения по вертикали в пикселях. Может Ярослав поставит все точки и разъяснит этот вопрос?

Если я правильно помню, то это сторона квадрата в пикселях. Если возвращает 1000, то это значит, что допустимо хранение изображение размером до 1000 х 1000. На самом деле TBitmap в своей основе использует текстуру для хранения картинки. Поэтому этот размер и ограничение зависит от графического процессора.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

К сожалению я рано радовался. Три недели тестов показали что ошибка "Bitmap size too big" продолжает появляться у не значительной части пользователей.

Вышеприведенный код по видимому работает. Но ошибка появляется там где размер изображения заведомо меньше GetMaxImageSize

 end
  Else
    if Assigned(ABitmap) then
      ABitmap.Assign(ABitmapSurface); // Вот здесь ошибка

Продолжаю наблюдение...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Да ошибка подтвердилась. MaxImageSize=4096, но  ABitmap.Assign(ABitmapSurface) выдает ошибку "Bitmap size too big" при загрузке картинки 250x250

Больше идей у меня нет...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Вот и у меня вылезла эта ошибка ."Bitmap size too big". Причем ошибка коварная, появляется не всегда и из-за этого непонятно почему и как ее отловить.

BigBitmap.png

 

Подозреваю что проблема происходит здесь (к ListView прикреплен ImageList с 2 картинками 44х44):

var
  k:integer;
  Al:TAl;
  IItem:TListItemImage;
  TItem:TListItemText;
begin
      Lsv.BeginUpdate;
      for k := 0 to Lsv.ItemCount-1 do
      begin        
        IItem:=Lsv.Items[k].Objects.FindDrawable('Imp1') as TListItemImage;
        TItem:=Lsv.Items[k].Objects.FindDrawable('TxtNumber') as TListItemText;

        Al:=GetAlFromList(Lsv.Items[k].Tag);
        if Al.Tip=0 then
        begin                                    
          if IItem<>nil then IItem.ImageIndex:=0; 
          if TItem<>nil then TItem.TextColor:=$FFFF0000;
        end
        else
        begin
          if IItem<>nil then IItem.ImageIndex:=1; 
          if TItem<>nil then TItem.TextColor:=$FF0000FE;
        end
      end;
      Lsv.EndUpdate;
end;

Продолжаю выяснять откуда ноги растут.

 

Отредактировал Rusland

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Сделаю предположение что ВОЗМОЖНО данная ошибка вылезает при невозможности выделить память под очередную текстуру

Отредактировал Error

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

скажите, кто-нибудь разобрался с проблемой?

у меня все тоже - 

 ABitmap.Assign(ABitmapSurfaceResize);

даже ПОСЛЕ преобразования размеров выдает ошибку  Bitmap size too big

в FMX ужасно раздражает что на корректную работу строчки TBitmap().LoadFromFile(filename) можно потратить больше суток, причем не факт что в новой версии все будет работать стабильно

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Я смирился. У меня в приложении с ~20000 пользователями ошибка вылезает у 290 :-( Т.е. это примерно 1.5%. 

Отредактировал Евгений Корепов

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

понял... ну тут единственный тогда выход что мне видится переписывать с ХЕ6 TImage и TBitmap, там оно работало, у меня глюк появился с переходом на ХЕ8-ХЕ10

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
В 31.03.2016 в 15:53, Brovin Yaroslav сказал:

Если я правильно помню, то это сторона квадрата в пикселях. Если возвращает 1000, то это значит, что допустимо хранение изображение размером до 1000 х 1000

Опытным путем выяснил что это не так. Это максимальная сторона изображения, а пропорции его не должны превышать пропорции монитора (может десктопа?).

Т.е. если TCanvasManager.DefaultCanvas.GetAttribute(TCanvasAttribute.MaxBitmapSize) возвращает 8192, то для монитора с разрешением 1440х900 можно создать битмапку размером не больше чем 8192х5120,  в противном случае получаю ошибки "Cannot create texture for 'TCanvasD2D'" или "Stack overflow"

 

Позже попробую поэкспериментировать на десктопе, растянутом на 2 монитора с портретной и ландшафтной ориентацией.

Отредактировал dnekrasov

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
В 19.05.2016 в 22:57, Евгений Корепов сказал:

Я смирился. У меня в приложении с ~20000 пользователями ошибка вылезает у 290 :-( Т.е. это примерно 1.5%. 

попробуй переставь

ABitmapSurfaceResize:=TBitmapSurface.Create;

после 

ABitmapSurface:=TBitmapSurface.Create;

и

после определения максимальных длины и ширины  - сделай ABitmapSurfaceResize.SetSize

у меня по крайней мере заработало.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

в общем итоговая процедура получилась у меня вот так, при этом заработало там где до этого не работало

procedure CheckAndLoadFromStream(FileName:string; const ABitmap : TBitmap);
var
  MaxImageSize : Integer;
  ABitmapSurface,ABitmapSurfaceResize : TBitmapSurface;
  mxH,mxW:integer;
begin
  ABitmapSurface:=TBitmapSurface.Create;
  ABitmapSurfaceResize:=TBitmapSurface.Create;
  TBitmapCodecManager.LoadFromFile(FileName,ABitmapSurface);
  MaxImageSize:=TCanvasManager.DefaultCanvas.GetAttribute(TCanvasAttribute.MaxBitmapSize);
  if (ABitmapSurface.Height>MaxImageSize) or (ABitmapSurface.Width>MaxImageSize) then
  begin
    if ABitmapSurface.Height>ABitmapSurface.Width then
    begin
      mxH:=MaxImageSize;
      mxW:=Round(mxH/ABitmapSurface.Height*ABitmapSurface.Height);
    end else
      begin
        mxW:=MaxImageSize;
        mxH:=Round(mxW/ABitmapSurface.Width*ABitmapSurface.Height);
      end;
  end else
    begin
      mxW:=ABitmapSurface.Width;
      mxH:=ABitmapSurface.Height;
    end;
  ABitmapSurfaceResize.SetSize(mxW,mxH);
  ABitmapSurfaceResize.StretchFrom(ABitmapSurface,mxW,mxH);
  FreeAndNil(ABitmapSurface);
  ABitmap.SetSize(mxW,mxH);
  ABitmap.Assign(ABitmapSurfaceResize);
  FreeAndNil(ABitmapSurfaceResize);
end;
Отредактировал tromani

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

К сожалению этот код выполняется неимоверно долго. На моём HTC One, загрузка с локального хранилища 30 картинок 250х250 производится за 15-20 секунд. Прямая операция Assigned(ABitmap) происходит фактически мгновенно. Так что ради полутора процентов пользователей, замедлять работу приложения на два порядка я не готов.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Посмотрел исходники, оказывается там все это уже есть:

unit FMX.Graphics;

procedure TBitmap.AssignFromSurface(const Source: TBitmapSurface);
var
  BitmapData: TBitmapData;
  MaxSize: Integer;
  ResampledSurface: TBitmapSurface;
  I: Integer;
  SourceRect: TRectF;
begin
  MaxSize := CanvasClass.GetAttribute(TCanvasAttribute.MaxBitmapSize);
  if (Source.Width > MaxSize) or (Source.Height > MaxSize) then
  begin
    SourceRect := TRectF.Create(0, 0, Source.Width, Source.Height);
    SourceRect.Fit(TRectF.Create(0, 0, MaxSize, MaxSize));
    ResampledSurface := TBitmapSurface.Create;
    try
      ResampledSurface.StretchFrom(Source, Trunc(SourceRect.Width), Trunc(SourceRect.Height), PixelFormat);
      AssignFromSurface(ResampledSurface);
    finally
      ResampledSurface.Free;
    end;
  end
  else
  begin
    SetSize(Source.Width, Source.Height);
    if Map(TMapAccess.Write, BitmapData) then
    try
      for I := 0 to TBitmapSurface(Source).Height - 1 do
        Move(TBitmapSurface(Source).Scanline[I]^, BitmapData.GetScanline(I)^, BitmapData.BytesPerLine);
    finally
      Unmap(BitmapData);
    end;
  end;
end;

Так что весь мой код не имеет смысла. В процедуре можно ограничится следующим кодом:

procedure TFormMain.CheckAndLoadFromStream(const AStream : TStream; const ABitmap : TBitmap);
Var ABitmapSurface : TBitmapSurface;
begin
  ABitmapSurface:=TBitmapSurface.Create;
  AStream.Position:=0;
  TBitmapCodecManager.LoadFromStream(AStream,ABitmapSurface);
  if Assigned(ABitmap) then
    ABitmap.Assign(ABitmapSurface);
  FreeAndNil(ABitmapSurface);
end;

Ну и изредка глючить он будет по прежнему...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

хм... ну очевидно что медленность работы вызвана 

ABitmapSurfaceResize.StretchFrom(ABitmapSurface,mxW,mxH);

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

 

Я конечно не знаю как у вас происходит но меня очень напрягает когда 2-3% скачавших приложение ставят оценку 1 потому что что-то не заработало или они не разобрались как работает

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить на вопрос...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.


  • Похожий контент

    • От ComAlex
      Господа Delphi-программисты под Android (FireMonkey),
      Нужна ваша помощь или совет в направления решения в несложной, достаточно типичной и прозрачной задаче:
      Я хочу выполнять некоторые вычисления (или выдавать Toastы) через каждые 30 сек. в Android-приложении.
      Что-то типа сервиса (службы): Приложение запускает задание и может закрываться, 
      но периодические вычисления продолжаются (результат сохраняется, допустим, в ini - не важно пока), т.е. служба работает.
      Я не знаю, как это решить:
      1. Организовал как сервис.
          Сервисы убиваются androidом ... И это политика androidов: экономить батарию и т.д.
      2. Сделал через AlarmManager.
          Тоже это не работает на MI 9T (Android 10: API 29). На старых (Android 4...: API 19) всё работает.
      3. Хотел использовать job scheduler: добавлять задания и удалять исполненные. Но не знаю как. ничего не нагуглил под Firemonkey. Может кто знает?
      Какие ещё варианты и вообще, это решается на Firemonkey Delphi XE 10.3.3?
      На поиск решения мною потрачено много время - сдвинуться не могу.
      Если Delphi это не умеет, так и скажите - перепрыгну на другую платформу.
          Может Delphi об этом думает, чтоб это скоро реализовать? В интернете тишина.
      Если Delphi это умеет, просьба дать идею или совет или пример ...
    • От Botov Nikita
      Добрый день Уважаемые!
      Подскажите каким образом можно реализовать функцию ластика в XE 10.3?
      Например на форме лежит 2 TImage один над другим, и когда мышкой проводим по верхней картинке, остается "след" и видно нижнюю картинку?
      С Уважением!
    • От Игорь Маринин
      поделитесь пожалуйста пустым проектом, готовым к публикации для TV устройств.
      что-то никак не получается опубликовать, сейчас отклонили с такими ошибками:
      APK:8
      No Now Playing notification [card]"
      Your App does not contain a “Now Playing” notification [card] for when the user has selected the HOME button within the app. Please refer to our Displaying a Now Playing Card documentation for more details.
      APK:8
      Play/Pause key event is not respected during playback
      Your media apps that play video or music content must respect the play/pause key during playback. Please refer to our Media Play/Pause documentation and Update the Playback State documentation for details.
      Missing DPad functionality
      Your app requires user interaction for menus or app navigation. Please make sure that all menus and app navigation are fully functional using a DPad. Please refer to our DPAD Control and Hardware Declaration documentation.
      ну и баннер не прилепил
      No full-size app banner
      Your app does not contain a full-size app banner or is it not visible in the launcher. We are targeting 1080P, which we consider xhdpi. Apps should include the banner in the xhdpi (320 dpi) drawables folder with a size of (320px × 180px). Please refer to our Home Screen Banner and UI Patterns documentation.
      The title should help users identify apps in the launcher. Please refer to our Visual Design and User Interaction documentation for more information.
      был бы очень благодарен за рабочий пустой костяк с манифестом.
       
    • От krapotkin
      В процессе разработки столкнулся с очень странной ситуацией, корни которой до сих пор неясны, но решение нашлось.
      Итак. Делфи 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
       
    • От krapotkin
      У меня в работе два приложения, и оба они не предназначены для 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.
      Для того, чтобы системный инсталлер запускался, нужно не забыть отметить еще одну галочку

       
       
       
       
       
    • От Дмитрий Потапов
      Приветствую. На просторах интернета нашел BASS-библиотеку для Android и соответственно обертку для нее под Delphi.

      Вроде бы все работает нормально, но вот при настройке библиотеки при помощи методов
      BASS_GetConfigPtr, BASS_SetConfigPtr для работы со строковыми параметрами возникает проблема.
      На Windows с этим я легко работал, там достаточно использовать такую конструкцию

       
      PAnsiChar(BASS_GetConfigPtr(configId)) и получаем значение. BASS_SetConfigPtr(configId, PAnsiChar(value)) и задаем значение. Но вот под Android такой способ не проходит, как минимум по одной причине - он не понимает, что такое PAnsiChar. На docwiki.emarcadero.com, есть вот такая статейка. В которой написано, что PAnsiChar нужно заменить одним из трех: System.String, System.MarshalledString или System.SysUtils.TStringBuiler
      Только вот, если использовать MarshalledString, то при попытке получить значение приложение просто намертво зависает и все.
      Потом я подумал, так как библиотека написана на Java (вероятно), то может стоит попробовать использовать JNI в работе с ней, так как в Java файле, который прилагался вместе с библиотекой. Там я нашел объявления этих функций и заметил, что тип возвращаемого значения Object (ну, не зная Java я просто предположил, что это тоже просто ссылка на значение, например как PAnsiChar в Delphi на Windows).

      Но, при попытке как-либо использовать это натыкаюсь на проблемы в виде ошибки компиляции, либо очередном зависании приложения.
      Так как возвращается Object, то решил попробовать вариант с JObject и JString, но ни один вариант не хочет работать, опять ошибки на стадии компиляции, либо зависание приложение. Может быть, я что-то не так делаю.

      В общем, буду признателен за помощь!
    • От Yarpda
      Доброго времени!
      Есть идея сделать возможность резервирования данных приложения на Google drive под текущим аккаунтом пользователя. Ничего подобного я на форуме не нашел, может быть кто-то уже пытался решать подобную задачу? Интересует работа с Gdrive  в первую очередь.
    • От Ali Ezzeddine
      65/5000       Уважаемые,
      Есть ли способ вызвать сервис Java из приложения Delphi FMX?
    • От Эрик Шакиров
      Привет всем! Кто нибудь может подсказать как реализовать анимацию переходов между формами в Android без использования TabControl? Или подсказать в какую сторону "рыть"
    • От Павел Блажеев
      Добрый день. Очень нужна Ваша помощь. 
      Мне необходимо сделать координатную сетку в виде точек. При масштабировании панели количество точек должно изменяться . 
      Хочу все это сделать на канве панели. Унаследовал класс и переопределил procedure   Paint; override;
        Tfield = class(TPanel)
            Constructor Create( parent: TFmxObject);
              procedure   Paint; override;
              Procedure   OnMyClick (Sender: TObject);
          end;

      В теле метода я пробовал рисовать. Экспериментировал и столкнулся с такой проблемой. Ничего не отображается. Нет никаких изменений.
      Если я наследую не от Tpanel а от Timage то часть кода работает а часть работает очень криво. Очень хочу разобраться почему .
      {Отображается сразу}
      for a:=1 to 1000 do
            begin
              self.Canvas.Fill.Color:=  TAlphaColors.Crimson;
               self.Canvas.FillEllipse(rect(1,1,10,10),self.AbsoluteOpacity);
               self.Canvas.FillEllipse(rect(round(self.Width-9),round(self.Height-9),round(self.Width), round(self.Height)),self.AbsoluteOpacity);
               self.Canvas.FillEllipse(rect(round(self.Width-9),1,round(self.Width), 9),self.AbsoluteOpacity);
               self.Canvas.FillEllipse(rect(1,round(self.Height-9),10, round(self.Height)),self.AbsoluteOpacity);
            end;
       
      {Отображается только после того как я проскролю Scrollbox на котором лежит панель в крайнее нижнее правое положение}
            self.Canvas.Stroke.Color:=  TAlphaColors.Crimson;
            self.Canvas.Stroke.Thickness:=7;
             Canvas.BeginScene;
            self.Canvas.DrawLine(PointF(20, 20), PointF(100, 50), self.AbsoluteOpacity);
             Canvas.EndScene;
      Подскажите пожалуйста, почему не работает такое с панелью?  Как правильно рисовать на панели? 
      Почему в случае с имейджем все работает так некорректно?  Почему работает только после скрола? 
      Каким способом мне лучше сделать координатную сетку? состоящую из точек как в режиме Design?

       


  • Последние посетители   0 пользователей онлайн

    Ни одного зарегистрированного пользователя не просматривает данную страницу

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