У TForm есть свойство StyleLookup (по умолчанию оно установлено в значение backgroundstyle, но естественно вы можете указать другой существующий элемент стиля)
В этом элементе стиля, если он типа TStyleObject, есть свойство SourceLookup (содержит ссылку на картинку) и свойство SourceLink (содержит координаты области, откуда берется заполнение фона TForm)
Конечно, либо используйте стили, либо банально установите свойство Fill у главной формы приложения (можно поменять цвет, можно картинку сделать фоном, можно градиент). И это для андроида тоже работает
да, в uses для андроид нужно добавить модули Androidapi.JNI.Os и Androidapi.JNIBri
uses
... Androidapi.JNI.Os, Androidapi.JNIBridge ...
if TJBuild_VERSION.JavaClass.SDK_INT <= 28 then
begin
try
if not Assigned(FService) then
begin
FService := TLocalServiceConnection.Create;
FService.StartService('serPublic');
end;
except
// что нибудь делаем
end;
end;
пробовал TMS - они их написали несколько лет назад и сейчас ничего не работает, почти все сервисы сменили API (например, тот же firebase), а они не обновили
Приложение может вылетать по разным причинам.
Возможно дело в том, что пытаетесь прочитать или записать файл в публичную папку. На Андроид 11 мало запроса разрешения, теперь чтение и запись файлов делается иначе.
У меня приложение на Делфи работает на 29 СДК (это от Андроид 10), но пришлось добавить строку, о которой написано в теме на которую дал ссылку. Иначе не работало на Андроид 11.
здесь на форуме это все уже обсуждалось... на андроид 11 изменилось расположение системных библиотек... дельфи 10.2 об этом не знает... 10.3.3 еще можно было заставить работать с андроид 11 (исправлением и перекомпиляцией System.pas)... но надо ставить 10.4... надо... sdk и файл манифеста тыркать бесполезно....
Доброго времени суток. Столкнулся с такой же проблемой в 10.4.2. В стандартном примере все работает, а в мигрированном приложении из 10.3.3 - приложение всегда ловит отмену запроса на фото. Я так понимаю где-то не хватает прав для временного файла.
Путем долгих и мучительных экспериментов и курением доки организовал эту всю штуку через нативные интенты.
Код вырезан из рабочего проекта, по этому извините если что-то не так. Но смысл должен быть понятен.
Здесь кроме самого фото еще реализовано автоматическое поворачивание картинки по Exif-даным, относительно того в каком положении был телефон.
Так же очень важно не забыть выставить права в опциях приложение и подключить Secure File Sharing в Entitlement List. С Uses секцией надеюсь разберетесь, не уверен что нужно для Вашей задачи все из того что я кинул.
Uses
....
Androidapi.JNI.GraphicsContentViewText,
Androidapi.JNI.App,
Androidapi.JNI.JavaTypes,
Androidapi.Helpers,
FMX.Platform.Android,
System.Permissions,
Androidapi.JNI.Os,
Androidapi.JNI.Net,
Androidapi.JNI.Support,
Androidapi.JNI.Provider,
Androidapi.JNIBridge,
FMX.Helpers.Android,
FMX.Surfaces,
Androidapi.JNI.Media;
TfmMain = class(TForm)
...
procedure FormCreate(Sender: TObject);
procedure btnPhotos_ShowCameraClick(Sender: TObject);
...
protected
FMessageSubscriptionID : integer;
FPermissionWriteExternalStorage: string;
FPermissionCamera: string;
FPermissionReadExternalStorage: string;
FphotoUri :Jnet_Uri;
JFileName: JFile;
procedure DisplayRationale(Sender: TObject; const APermissions: TArray<string>; const APostRationaleProc: TProc);
procedure TakePicturePermissionRequestResult(Sender: TObject; const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>);
procedure TakePicturePermissionRequestAfterINitResult(Sender: TObject; const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>);
procedure HandleActivityMessage(const Sender: TObject; const M: TMessage);
function OnActivityResult(RequestCode, ResultCode: Integer; Data: JIntent): Boolean;
end;
procedure TfmMain.FormCreate(Sender: TObject);
begin
FPermissionCamera := JStringToString(TJManifest_permission.JavaClass.CAMERA);
FPermissionReadExternalStorage := JStringToString(TJManifest_permission.JavaClass.READ_EXTERNAL_STORAGE);
FPermissionWriteExternalStorage := JStringToString(TJManifest_permission.JavaClass.WRITE_EXTERNAL_STORAGE);
...
end;
procedure TfmMain.HandleActivityMessage(const Sender: TObject; const M: TMessage);
begin
if M is TMessageResultNotification then begin
OnActivityResult(TMessageResultNotification(M).RequestCode, TMessageResultNotification(M).ResultCode, TMessageResultNotification(M).Value);
end;
end;
function TfmMain.OnActivityResult(RequestCode, ResultCode: Integer; Data: JIntent): Boolean;
procedure SetPhotoData();
var
jBmp : JBitmap;
xSurface : TBitmapSurface;
vImage : TImage;
xExif : JExifInterface;
sOrientation : JString;
rotationAngle, orientation : integer;
sFile : string;
ScreenService: IFMXScreenService;
begin
if RequestCode = 1001 then begin
if ResultCode = TJActivity.JavaClass.RESULT_OK then begin
if FphotoUri <> nil then begin
jBmp := TJImages_Media.JavaClass.getBitmap(SharedActivity.getContentResolver, FphotoUri);
if jBmp <> nil then begin
xSurface := TBitmapSurface.Create;
try
JBitmapToSurface(jBmp, xSurface);
vImage := TImage.Create(nil);
try
vImage.Bitmap.Assign(xSurface);
sFile := JStringToString(JFileName.getAbsolutePath);
xExif := TJExifInterface.JavaClass.init(StringToJString(sFile));
sOrientation := xExif.getAttribute(TJExifInterface.JavaClass.TAG_ORIENTATION);
if sOrientation = nil then
orientation := TJExifInterface.JavaClass.ORIENTATION_NORMAL
else
orientation := StrToIntDef(JStringToString(sOrientation), 0);
rotationAngle := 0;
if orientation = TJExifInterface.JavaClass.ORIENTATION_ROTATE_90 then
rotationAngle := 90;
if orientation = TJExifInterface.JavaClass.ORIENTATION_ROTATE_180 then
rotationAngle := 180;
if orientation = TJExifInterface.JavaClass.ORIENTATION_ROTATE_270 then
rotationAngle := 270;
if rotationAngle <> 0 then
vImage.Bitmap.Rotate(rotationAngle);
//Готовое фото уже в vImage - выводим куда нужно
imPhotos_Photo.Bitmap.Assign(vImage.Bitmap);
finally
vImage.Free;
end;
finally
xSurface.Free
end{try..finally};
end else begin
//не вдалось завантажити картинку
TDialogService.ShowMessage('Не вдалось завантажити картинку');
end{if..else};
end else begin
//пустий Uri
TDialogService.ShowMessage('Пустий Uri');
end{if..else};
end else begin
TDialogService.ShowMessage('Відмовились робити фото');
end{if..else};
end{if};
end;
begin
Result := False;
TMessageManager.DefaultManager.Unsubscribe(TMessageResultNotification, FMessageSubscriptionID);
FMessageSubscriptionID := 0;
if RequestCode = 1001 then begin
// робота з камерою - отримання фото
SetPhotoData();
exit;
end;
...
end;
procedure TfmMain.TakePicturePermissionRequestResult(Sender: TObject;
const APermissions: TArray<string>;
const AGrantResults: TArray<TPermissionStatus>);
var
intent : jintent;
Authority: JString;
begin
// 3 permissions involved: CAMERA, READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE
if (Length(AGrantResults) = 3) and (AGrantResults[0] = TPermissionStatus.Granted) and (AGrantResults[1] = TPermissionStatus.Granted) and (AGrantResults[2] = TPermissionStatus.Granted) then begin
//створюємо "намір" отримання фото з камери
intent := TJIntent.JavaClass.init;
intent.setAction(TJMediaStore.JavaClass.ACTION_IMAGE_CAPTURE);
//створюємо тимчасовий файл
JFileName := TJFile.JavaClass.createTempFile(StringToJString('picture'), StringToJString('.jpg'));
//отримуємо опис прав по файлам
Authority := StringToJString(JStringToString(TAndroidHelper.Context.getApplicationContext.getPackageName) + '.fileprovider');
//отримуємо URI-до тимчасового файлу в контексті нашої аплікації
FphotoUri:= TJFileProvider.JavaClass.getUriForFile(TAndroidHelper.Context, Authority, JFileName);
//кажемо що наш "намір" має писати дані в цей тимчасовий файл
intent.putExtra(TJMediaStore.JavaClass.EXTRA_OUTPUT,
TJParcelable.Wrap((FphotoUri as ILocalObject).GetObjectID)
);
//реєструємось на отримання подій
FMessageSubscriptionID := TMessageManager.DefaultManager.SubscribeToMessage(TMessageResultNotification, HandleActivityMessage);
//стартуємо наш "намір"
MainActivity.startActivityForResult(intent, 1001);
end else
TDialogService.ShowMessage('Не надані необхідні для роботи дозволи!');
end;
procedure TfmMain.DisplayRationale(Sender: TObject;
const APermissions: TArray<string>; const APostRationaleProc: TProc);
var
I: Integer;
RationaleMsg: string;
begin
for I := 0 to High(APermissions) do
begin
if APermissions[I] = FPermissionCamera then
RationaleMsg := RationaleMsg + 'The app needs to access the camera to take a photo' + SLineBreak + SLineBreak
else if APermissions[I] = FPermissionReadExternalStorage then
RationaleMsg := RationaleMsg + 'The app needs to read a photo file from your device';
end;
// Show an explanation to the user *asynchronously* - don't block this thread waiting for the user's response!
// After the user sees the explanation, invoke the post-rationale routine to request the permissions
TDialogService.ShowMessage(RationaleMsg,
procedure(const AResult: TModalResult)
begin
APostRationaleProc;
end);
end;
procedure TfmMain.btnPhotos_ShowCameraClick(Sender: TObject);
begin
PermissionsService.RequestPermissions( [FPermissionCamera, FPermissionReadExternalStorage, FPermissionWriteExternalStorage],
TakePicturePermissionRequestResult,
DisplayRationale
);
end;
P.S. возможно нужно будет вставить в манифест:
android:requestLegacyExternalStorage="true"
а где часть "Дано" ?
ОС? Каким способом загружаете файл в браузер?
в теории TMediaPlayer умеет и mp4 воспроизводить, но нужна конкретика
есть также вариант PasLibVlc
Вот еще почитать на эту тему:
https://developer.android.com/training/data-storage/use-cases
и
https://developer.android.com/about/versions/11/privacy/storage
Так что после Target SDK 30 вообще старый код работать не будет... Нужно будет переписывать код работы с файлами.
Дело в том, что ты выставил Target SDK 29
Теперь чтобы на Андроид 10, 11 или 12 старым способом иметь доступ к файлам надо объявить явно это в манифесте
Кажется с помощью android:requestLegacyExternalStorage="true"
но лучше погугли.
И вообще в будущем возможно... Гугл зарежут прямой доступ к файлам и придется через медиа-запросы обращаться к файлам.
самое простое (по мне лично) взять и положить на него новый объект типа крашеный прямоугольник.
Свой класс объектов для listView можно создать аналогично тем, что есть в delphi, унаследовав от TDrawable
https://www.youtube.com/watch?v=XRj3qjUjBlc
Получить UID (идентификатор пользователя - владельца) можно с помошью функции stat (Описана в POSIX стандарте : https://pubs.opengroup.org/onlinepubs/9699919799/ ). Далее есть файл /etc/passwd где UID пользователей сопоставлены с их именами.
В Linux документацию на stat можно посмотреть командой man 2 stat (В случае если установлены средства разработки на языках C/C++).
Если не ошибаюсь, она находится в библиотеке libc. Значит задача состоит в том, чтобы загрузить "dll" библиотеку libc ( например /usr/lib64/libc.so.6 ) и вызвать функцию stat. Для загрузки динамических библиотек (разделяемых объектов - shared object - so) используется вызов dlopen.
Альтернативный способ состоит в том, чтобы напрямую (без библиотеки libc ) выполнить системный вызов stat, обратившись непосредственно к ядру ОС. Для этого, нужно, найти документацию на системный вызов stat, и вызвать его самому - вы по сути сами реализуете функционал библиотеки libc.
Второй способ, хоть и кажется сложнее, на самом деле реализуется буквально несколькими assembler'ными инструкциями.
Возможно в Delphi это уже сделали (не исключаю, что в какой-то библиотеке, уже есть функция Stat ).
В RAD Studio, в каталоге source\rtl\posix, есть файл SysStatAPI.inc - он включен в posix.sysstat.pas.
Значит просто: Uses Posix.SysStat и используйте функцию stat.
вроде уж везде написали что ListBox не предназначен для 20+ элементов. используйте ListView
https://www.youtube.com/watch?v=XRj3qjUjBlc
для облегчения нелегкой работки можно посмотреть тут
http://fire-monkey.ru/topic/5519-фреймворк-для-стандартного-listview/#comment-34912
ну и да, 50000+ элементов это несколько трешовый UI