AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein GUI-Design mit VCL / FireMonkey / Common Controls Versuch: TFileBrowser für Android, geht das ohne globale Variable?
Thema durchsuchen
Ansicht
Themen-Optionen

Versuch: TFileBrowser für Android, geht das ohne globale Variable?

Ein Thema von Renate Schaaf · begonnen am 12. Nov 2020 · letzter Beitrag vom 13. Nov 2020
Antwort Antwort
Renate Schaaf

Registriert seit: 25. Jun 2020
Ort: Lippe
114 Beiträge
 
Delphi 11 Alexandria
 
#1

Versuch: TFileBrowser für Android, geht das ohne globale Variable?

  Alt 12. Nov 2020, 20:32
Ich habe versucht, einen Ersatz für TFileOpenDialog zu bauen, der möglichst einfach für Android aufrufbar ist. Das geht aber nur, indem der Benutzer eine Callback-Funktion definiert, also ein "OnExecute", jedenfalls fällt mir nichts anderes ein. Damit der Aufruf möglichst zusammenhängend ist, kann man das Callback anonym definieren.

Hier ist ein Beispiel für den Aufruf:

Delphi-Quellcode:
procedure TForm1.BrowseForVideoFile;
begin
{$IFDEF ANDROID}
  TFileBrowser.BrowseForFile(ctVideo,
  // This procedure is called when the user has picked an item
    procedure(var filename: string; var success: boolean)
    begin
      if success then
      begin
        fVideoFilename := filename;
        Edit2.text := fVideoFilename;
        ProcessVideoFile;
      end
      else
        TDialogService.ShowMessage
          ('Pick the file by opening the Documents folder. Don''t use any of the other apps.');
    end);

{$ELSE}
...
end;
Dafür ist TFileBrowser.BrowseForFile eine class procedure. Bisher habe ich das nur ans Laufen bekommen, indem ich im Interface eine globale Variable vom Typ TFileBrowser definiere. Kann man das irgendwie schlauer machen?

Hier ist der source code für TFileBrowser, ist nicht so lang:

Delphi-Quellcode:
unit UAndroidTools;

interface

uses
  System.Messaging,
  AndroidAPI.Helpers,
  AndroidAPI.Jni.Os,
  AndroidAPI.Jni.GraphicsContentViewText,
  AndroidAPI.Jni.Net,
  FMX.Platform.Android,
  AndroidAPI.JNIBridge,
  AndroidAPI.Jni.JavaTypes;

type
  TContentEnum = (ctVideo, ctImages, ctAudio, ctText, ctAll);

  TFileBrowseEvent = reference to procedure(var Filename: string;
    var Success: boolean);

  TFileBrowser = class
  private
    fOnExecute: TFileBrowseEvent;
    fFilename: string;
    function HandleIntentAction(const Data: JIntent): boolean;
    procedure DoExecute(Success: boolean);
  protected
    fMessageSubscriptionID: integer;
    procedure HandleActivityMessage(const sender: TObject; const M: TMessage);
  public
    ///<summary> Use Android API to have the user pick a file with a certain content type </summary>
    /// <param OnExecute> Event occuring when the user has picked a file. Write a (anonymous) procedure(var Filename: string; var Success: boolean) to handle the event. <param>
    class procedure BrowseForFile(Content: TContentEnum;
      OnExecute: TFileBrowseEvent);
  end;

implementation


const
  ContentStrings: array [TContentEnum] of string = ('video/*', 'image/*',
    'audio/*', 'text/*', '*/*');

  { TFileBrowser }

var
  //Wie schafft man das ohne globale Variable?
  _TheFileBrowser: TFileBrowser;

class procedure TFileBrowser.BrowseForFile(Content: TContentEnum;
  OnExecute: TFileBrowseEvent);
var
  Intent: JIntent;
begin
  _TheFileBrowser := TFileBrowser.Create;
  //Das ist doch irgendwie doof.
  With _TheFileBrowser do
  begin
    fOnExecute := OnExecute;
    fMessageSubscriptionID := TMessageManager.DefaultManager.SubscribeToMessage
      (TMessageResultNotification, HandleActivityMessage);
    Intent := TJIntent.Create;
    Intent.setType(StringToJString(ContentStrings[Content]));
    Intent.setAction(TJIntent.JavaClass.ACTION_GET_CONTENT);
    MainActivity.startActivityForResult(Intent, 0);
  end;
end;

procedure TFileBrowser.DoExecute(Success: boolean);
begin
  if assigned(fOnExecute) then
    fOnExecute(fFilename, Success);
  self.disposeof;
end;

procedure TFileBrowser.HandleActivityMessage(const sender: TObject;
  const M: TMessage);
begin
  if M is TMessageResultNotification then
  begin
    DoExecute(HandleIntentAction(TMessageReceivedNotification(M).Value));
  end;
end;

function TFileBrowser.HandleIntentAction(const Data: JIntent): boolean;
var
  C: JCursor;
  I: integer;
begin
  C := MainActivity.getContentResolver.query(Data.getData, nil,
    StringToJString(''),
    nil, StringToJString(''));

  C.moveToFirst;
  Result := false;
  for I := 0 to C.getColumnCount - 1 do
  begin
    if JStringToString(C.getColumnName(I)) = '_datathen
    // '_data' column contains the path
    begin
      fFilename := JStringToString(C.getString(I));
      Result := true;
      Break;
    end;
  end;
  if not Result then
    fFilename := '';
end;

end.
Gruß, Renate
Renate
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
9.582 Beiträge
 
Delphi 11 Alexandria
 
#2

AW: Versuch: TFileBrowser für Android, geht das ohne globale Variable?

  Alt 13. Nov 2020, 08:44
Ich empfinde das with als deutlich schlimmer als die globale Variable.

Da du um eine Instanz ja wohl nicht herumkommst, kannst du die Referenz aber als class var in der Klasse unter private deklarieren. Dann hängt die nicht lose als globale Variable herum. Und zur Freigabe der Instanz gibt es einen class destructor . Dann musst du die Instanz vermutlich gar nicht immer neu erstellen.
Sebastian Jänicke
Alle eigenen Projekte sind eingestellt, ebenso meine Homepage, Downloadlinks usw. im Forum bleiben aktiv!
  Mit Zitat antworten Zitat
Rollo62
Online

Registriert seit: 15. Mär 2007
4.093 Beiträge
 
Delphi 12 Athens
 
#3

AW: Versuch: TFileBrowser für Android, geht das ohne globale Variable?

  Alt 13. Nov 2020, 08:50
Wenn das eine globale Variable sein kann, dann könntest Du im TFileBrowser eine class var anlegen.
Normalerweise wird sowas über z.B. Instance gemacht.

Nur mal so grob hingeschrieben, ohne Gewähr:
Delphi-Quellcode:

type
  TMyClass = class
      class var FInstance;

      class function Instance : TMyClass;

      class constructor Create;
      class destructor Destroy;

      procedure CallWhatever;
  end;


class constructor TMyClass.Create;
begin
    FInstance := nil;
end;

class destructor TMyClass.Destroy;
begin
    FInstance.Free;
end;

class function TMyClass.Instance : TMyClass;
begin
    if not Assigned( FInstance ) then
    begin
        FInstance := TMyClass.Create;
    end;
 
    Result := FInstance;
end;


...
...
...

    TMyClass.Instance.CallWhatever; //<== geht jederzeit, wird beim ersten Aufruf erzeugt, und lebt bis zum Programmende in einer Instanz

....

Je nachdem sollte man das aber noch threadsafe absichern, bei Bedarf.


Edit:
@jaenicke, völlig richtig mit dem destruktor, jetzt isser drin.

Geändert von Rollo62 (13. Nov 2020 um 12:03 Uhr)
  Mit Zitat antworten Zitat
Renate Schaaf

Registriert seit: 25. Jun 2020
Ort: Lippe
114 Beiträge
 
Delphi 11 Alexandria
 
#4

AW: Versuch: TFileBrowser für Android, geht das ohne globale Variable?

  Alt 13. Nov 2020, 09:06
Vielen Dank an euch beide, so wirds gemacht. Mal wieder dazugelernt.

Also, ohne das "with" finde ich, dass es noch doofer aussieht

Gruß, Renate
Renate
  Mit Zitat antworten Zitat
Benutzerbild von haentschman
haentschman

Registriert seit: 24. Okt 2006
Ort: Seifhennersdorf / Sachsen
5.388 Beiträge
 
Delphi 12 Athens
 
#5

AW: Versuch: TFileBrowser für Android, geht das ohne globale Variable?

  Alt 13. Nov 2020, 09:11
Zitat:
Also, ohne das "with" finde ich, dass es noch doofer aussieht
Das hat nichts mit Optik zu tun, sondern mit dem Debugging.
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
9.582 Beiträge
 
Delphi 11 Alexandria
 
#6

AW: Versuch: TFileBrowser für Android, geht das ohne globale Variable?

  Alt 13. Nov 2020, 09:36
Wenn das eine globale Variable sein kann, dann könntest Du im TFileBrowser eine class var anlegen.
Normalerweise wird sowas über z.B. Instance gemacht.

Nur mal so grob hingeschrieben, ohne Gewähr:
Da fehlt noch der Klassendestruktor zur Freigabe (das mag hier nicht so wichtig sein, aber wenn man es sich nicht angewöhnt Speicherlecks zu vermeiden findet man im Fehlerfall, wenn es wichtig gewesen wäre, dann die Ursache nur schlecht). Initialisiert wird die Variable wiederum automatisch.

Das hat nichts mit Optik zu tun, sondern mit dem Debugging.
Zum nicht funktionierenden Debugging kommen noch potentielle Problemen bei Updates zu neuen Delphiversionen. Ein einfaches Beispiel dafür waren damals bei ich glaube XE2 die VirtualTrees. Eigentlich hätten die normal weiter funktioniert. TRect hatte damals aber Width und Height oder so bekommen und plötzlich bog das with <TRect-Variable> den Zugriff auf Width und Height der Komponente auf das TRect um...

So etwas ist dann teilweise sehr schwer zu finden, da es oft problemlos weiter kompiliert, an anderer Stelle aber plötzlich Fehler auftreten, die man dann ewig sucht.

Von daher macht man sich damit nur unnötigerweise Probleme.
Sebastian Jänicke
Alle eigenen Projekte sind eingestellt, ebenso meine Homepage, Downloadlinks usw. im Forum bleiben aktiv!
  Mit Zitat antworten Zitat
Renate Schaaf

Registriert seit: 25. Jun 2020
Ort: Lippe
114 Beiträge
 
Delphi 11 Alexandria
 
#7

AW: Versuch: TFileBrowser für Android, geht das ohne globale Variable?

  Alt 13. Nov 2020, 10:04
So funktioniert's prima, und ganz ohne with:

Delphi-Quellcode:
class function TFileBrowser.Instance: TFileBrowser;
begin
  if not assigned(FInstance) then
    FInstance := TFileBrowser.Create;
  Result := FInstance;
end;

class constructor TFileBrowser.Create;
begin
  //Ist doch eigentlich unnötig, oder?
  FInstance := nil;
end;

class destructor TFileBrowser.Destroy;
begin
  fInstance.free;
end;

class procedure TFileBrowser.BrowseForFile(Content: TContentEnum;
  OnExecute: TFileBrowseEvent);
begin
  Instance.Initialize(Content, OnExecute);
end;

procedure TFileBrowser.Initialize(Content: TContentEnum;
  OnExecute: TFileBrowseEvent);
var
  Intent: JIntent;
begin
  fOnExecute := OnExecute;
  fMessageSubscriptionID := TMessageManager.DefaultManager.SubscribeToMessage
    (TMessageResultNotification, HandleActivityMessage);
  Intent := TJIntent.Create;
  Intent.setType(StringToJString(ContentStrings[Content]));
  Intent.setAction(TJIntent.JavaClass.ACTION_GET_CONTENT);
  MainActivity.startActivityForResult(Intent, 0);
end;
Danke nochmal.
Renate

Geändert von Renate Schaaf (13. Nov 2020 um 10:11 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 15:17 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz