Delphi uses JNIBridge to access to Java classes using JNI. Delphi represents all external Java classes by interfaces. One interface is needed for Java class methods and another for the instance methods.
For example, here is a declaration of Java Boolean class located in Androidapi.JNI.JavaTypes.pas:
JBooleanClass = interface(JObjectClass)
['{CD51CE90-BCDA-4291-99B0-7BC70033C3CB}']
{class} function _GetFALSE: JBoolean; cdecl;
{class} function _GetTRUE: JBoolean; cdecl;
{class} function _GetTYPE: Jlang_Class; cdecl;
{class} function init(string_: JString): JBoolean; cdecl; overload;
{class} function init(value: Boolean): JBoolean; cdecl; overload;
{class} function compare(lhs: Boolean; rhs: Boolean): Integer; cdecl;
{class} function getBoolean(string_: JString): Boolean; cdecl;
{class} function parseBoolean(s: JString): Boolean; cdecl;
{class} function toString(value: Boolean): JString; cdecl; overload;
{class} function valueOf(string_: JString): JBoolean; cdecl; overload;
{class} function valueOf(b: Boolean): JBoolean; cdecl; overload;
{class} property FALSE: JBoolean read _GetFALSE;
{class} property TRUE: JBoolean read _GetTRUE;
{class} property &TYPE: Jlang_Class read _GetTYPE;
end;
[JavaSignature('java/lang/Boolean')]
JBoolean = interface(JObject)
['{21EAFAED-5848-48C2-9998-141B57439F6F}']
function booleanValue: Boolean; cdecl;
function compareTo(that: JBoolean): Integer; cdecl;
function equals(o: JObject): Boolean; cdecl;
function hashCode: Integer; cdecl;
function toString: JString; cdecl; overload;
end;
TJBoolean = class(TJavaGenericImportTJBoolean is a helper class that allows to get access to Java Boolean class or instance.
Accesing to the class provided by JavaClass method. When you create an instance of Java class, you can access all instance method declared in JBoolean.
To create new instance of Java class you should use Java-class constructor called init.
var
BoolValue: JBoolean; // Java Interface
begin
// Instantiate using constructor
BoolValue :=TJBoolean.JavaClass.init(True);
end;
All Java instances managed by Java runtime, Delphi JNI bridge hold global link to java instances, which tell Java runtime do not destroy instance. When Delphi interfaces is destroyed, Java instances destroyed by garbage collection.
Custom Java classes are needed when you need to subclass an existing Java class.
The base class for it is TJavaObject, another way is use TJavaGenericTo declare custom Java class you should declare new interface new class inherited from TJavaObject (or TJavaGeneric) class.
Here is an example of subclassing Android View class:
[JavaSignature('com/turbococoa/customview/CustomView')]
JCustomView = interface(JView)
['{69D7FFB8-F3BB-4976-8D6E-1E966A3887C3}']
procedure onDraw(const canvas: JCanvas);
end;
TCustomView = class(TJavaGenericprivate
protected
public
procedure onDraw(const canvas: JCanvas);
end;
...
procedure TCustomView.onDraw(const canvas: JCanvas);
begin
… custom drawing
end;
initialization
TCustomView.RegisterJava;
First you need declare custom attribute [JavaSignature] to Java interface.
In some cases Java runtime create an instance automatically (for example Activity or layout members) using class name and signature. To allow this for custom Java classes it should be registered by calling RegisterJava method in initialization section.To create a new instance of custom class, just call the constructor:
var
Obj: TCustomView;
...
Obj := TCustomView.Create;
...
The instance of custom class destroyed by garbage collection, when Delphi instance is destroyed.
To access to super class members use TJavaObject.Super property.
If you need to post reference to your custom java class instance to external API, you can use the following two methods:
1. If API requires Delphi interface, you should use TJavaObject.Super property
2. If API requires JNI object, you should use TJavaObject.JNIObject property.
To write virtual method in custom class, you should declare it in custom class interface and mark it using special attribute [Override]. For example:
[JavaSignature('com/turbococoa/controls/SeekBarActivity')]
JSeekBarActivity = interface(JActivity)
['{2688244E-D2EE-4AE3-8779-CE500F4B4224}']
[Override]
procedure onCreate(const savedInstanceState: JBundle);
...
If you need to call inherited Java class method in your implementation, use TJavaObject.CallSuper method. TJavaObject.CallSuper makes sense only in virtual method marked by [Override] attribute.
procedure TSeekBarActivity.onCreate(const savedInstanceState: JBundle);
begin
CallSuper; // This method call super class onCreate
...
end;
The easiest way to implement JView_onClickListener is just implement action in your custom class and set action name in layout XML file.
To make an action, just declare and implement special method in your custom class and mark it by [IBAction] attribute:
...
[IBAction]
procedure ButtonClick(id: Pointer);
...
The set onClick property in layout XML file in Android Studio designer or just in XML file like this:
Android classes usually uses listeners to callback from java instances. Let's describe how to use listeners on example of handling onClick event for Button.
You should write JView_OnClickListener implementation for this.
TMyButtonClickListener = class(TJavaLocal, JView_OnClickListener)
private
public
{ JView_OnClickListener }
procedure onClick(v: JView); cdecl;
end;
….
procedure TMyButtonClickListener.onClick(v: JView);
begin
… // Action
end;
To set listener to particular Button instance, you should create listener instance and set it to the Button. For example in Activity.onCreate:
procedure TMyActivity.onCreate(const savedInstanceState: JBundle);
var
L: TMyButtonClickListener;
begin
CallSuper;
Super.setContentView(R.layout.mainactivity);
L := TMyButtonClickListener.Create;
Super.findViewById(R.id.seekBar).setOnClickListener(L);
end;
Another way to handle actions is implementing Java listener directly in your custom class, for example in Activity class. To do that, you should add listener meebers to custom class interface and implement it in Delphi class. Here code from Controls demo, SeekBarActivity1.pas:
[JavaSignature('com/turbococoa/controls/SeekBarActivity')]
JSeekBarActivity = interface(JActivity)
['{2688244E-D2EE-4AE3-8779-CE500F4B4224}']
...
{ JSeekBar_OnSeekBarChangeListener }
procedure onProgressChanged(seekBar: JSeekBar; progress: Integer; fromUser: Boolean); cdecl;
procedure onStartTrackingTouch(seekBar: JSeekBar); cdecl;
procedure onStopTrackingTouch(seekBar: JSeekBar); cdecl;
end;
TSeekBarActivity = class(TJavaGeneric...
{ JSeekBar_OnSeekBarChangeListener }
procedure onProgressChanged(seekBar: JSeekBar; progress: Integer; fromUser: Boolean); cdecl;
procedure onStartTrackingTouch(seekBar: JSeekBar); cdecl;
procedure onStopTrackingTouch(seekBar: JSeekBar); cdecl;
end;
…
procedure TSeekBarActivity.onCreate(const savedInstanceState: JBundle);
begin
CallSuper;
Super.setContentView(R.layout.seekbaractivity1);
FSeekBar := TJSeekBar.Wrap(Super.findViewById(R.id.seekBar));
FSeekBar.setOnSeekBarChangeListener(Self); // set listener
end;
…
{ JSeekBar_OnSeekBarChangeListener Implementation }
procedure TSeekBarActivity.onProgressChanged(seekBar: JSeekBar; progress: Integer; fromUser: Boolean);
begin
… // Action
end;
procedure TSeekBarActivity.onStartTrackingTouch(seekBar: JSeekBar);
begin
end;
procedure TSeekBarActivity.onStopTrackingTouch(seekBar: JSeekBar);
begin
end;