Difference between revisions of "Android Interface/Native Android GUI"

From Lazarus wiki
Jump to navigationJump to search
Line 4: Line 4:
 
==Android API Hello World in Pascal==
 
==Android API Hello World in Pascal==
  
Here is an example Pascal application written for Android.
+
Here is an example Pascal application written for Android. It shows how to create controls, receive callback events and how to use the timer.
  
 
The full directory structure can be download with this svn command:
 
The full directory structure can be download with this svn command:
  
   svn co https://p-tools.svn.sourceforge.net/svnroot/p-tools/PascalNotes4Android PascalNotes4Android
+
   svn co https://p-tools.svn.sourceforge.net/svnroot/p-tools/turbochessclock4android turbochessclock4android
  
 
Here is the Pascal code from this example:
 
Here is the Pascal code from this example:
  
 
<delphi>
 
<delphi>
program pascalnotes4android;
+
{
 +
  A simple Chess Clock application
 +
 
 +
  Author: Felipe Monteiro de Carvalho - 2011
 +
  License: Public Domain
 +
}
 +
program turbochessclock4android;
  
 
{$mode objfpc}{$H+}
 
{$mode objfpc}{$H+}
  
 
uses
 
uses
   Classes, androidpipescomm, androidui, javalang;
+
   Classes, SysUtils, androidpipescomm, androidview, javalang,
 +
  androidapp, androidtimer, androidutil;
  
{$R *.res}
+
type
 +
  TEventHandler = class
 +
  public
 +
    procedure HandleOnTimer(ASender: TObject);
 +
    procedure buttonStartClickCallback(v: TView);
 +
    procedure buttonMoveClickCallback(v: TView);
 +
  end;
  
 
var
 
var
   layout: TAbsoluteLayout;
+
   layout: TLinearLayout;
   params: TAbsoluteLayout_LayoutParams;
+
   params: TLayoutParams;
   tv: TTextView;
+
   tv, black_label, white_label: TTextView;
   et: TTextView;
+
   scroller: TScrollView;
   btn: TButton;
+
   btn_move, btn_start: TButton;
 +
  tp: TTimePicker;
 +
  WhiteTimeCount: Integer = 0;
 +
  BlackTimeCount: Integer = 0;
 +
  MyTimer: TAndroidTimer;
 +
  MyEventHandler: TEventHandler;
 +
  IsWhitePlayerMove: Boolean = True;
 +
 
 +
procedure TEventHandler.buttonStartClickCallback(v: TView);
 +
begin
 +
  black_label.setVisibility(VISIBLE);
 +
  white_label.setVisibility(VISIBLE);
 +
  btn_move.setVisibility(VISIBLE);
 +
  //
 +
  WhiteTimeCount := tp.getCurrentHour() * 60 * 60 + tp.getCurrentMinute() * 60;
 +
  BlackTimeCount := WhiteTimeCount;
 +
  //
 +
  MyTimer.removeCallbacks();
 +
  MyTimer.postDelayed(100);
 +
end;
 +
 
 +
procedure TEventHandler.buttonMoveClickCallback(v: TView);
 +
begin
 +
  IsWhitePlayerMove := not IsWhitePlayerMove;
 +
end;
 +
 
 +
procedure TEventHandler.HandleOnTimer(ASender: TObject);
 +
var
 +
  lSeconds, lMinutes, lHours: Integer;
 +
begin
 +
//  Inc(TimerCount);
 +
  if IsWhitePlayerMove then
 +
  begin
 +
    lSeconds := WhiteTimeCount mod 60;
 +
    lMinutes := (WhiteTimeCount mod (60 * 60)) div 60;
 +
    lHours := WhiteTimeCount div (60 * 60);
 +
    white_label.setText(Format('White %d:%d:%d', [lHours, lMinutes, lSeconds]));
 +
    Dec(WhiteTimeCount);
 +
  end
 +
  else
 +
  begin
 +
    lSeconds := BlackTimeCount mod 60;
 +
    lMinutes := (BlackTimeCount mod (60 * 60)) div 60;
 +
    lHours := BlackTimeCount div (60 * 60);
 +
    black_label.setText(Format('White %d:%d:%d', [lHours, lMinutes, lSeconds]));
 +
    Dec(BlackTimeCount);
 +
  end;
 +
  //
 +
  MyTimer.removeCallbacks();
 +
  MyTimer.postDelayed(1000);
 +
end;
 +
 
 
begin
 
begin
 
   // Here add any initialization.
 
   // Here add any initialization.
Line 34: Line 98:
 
   // It should mostly contain GUI initialization
 
   // It should mostly contain GUI initialization
 
   // User interface
 
   // User interface
 +
  MyEventHandler := TEventHandler.Create;
  
 
   // Prepares the UI of the program
 
   // Prepares the UI of the program
   layout := TAbsoluteLayout.Create;
+
   layout := TLinearLayout.Create;
 
+
   params := TLayoutParams.Create(androidview.FILL_PARENT, androidview.FILL_PARENT);
   tv := TTextView.Create;
+
   layout.setLayoutParams(params);
  tv.setText('The first Pascal Android application =)');
 
  params := TAbsoluteLayout_LayoutParams.Create(320, 300, 0, 120);
 
   layout.addView(tv, params);
 
 
   params.Free;
 
   params.Free;
 +
  layout.setOrientation(androidview.VERTICAL);
  
  et := TEditText.Create;
+
   // Game UI
  et.setText('edit me please');
 
  params := TAbsoluteLayout_LayoutParams.Create(320, 50, 0, 0);
 
  layout.addView(et, params);
 
  params.Free;
 
 
 
  btn := TButton.Create;
 
  btn.setText('Go!');
 
{  btn.setOnClickListener(buttonClickCallback);}
 
  params := TAbsoluteLayout_LayoutParams.Create(320, 50, 0, 60);
 
  layout.addView(btn, params);
 
  params.Free;
 
 
 
  Activity.setContentView(layout);
 
 
 
   // Now tell Java that the initialization has finished
 
  myAndroidPipesComm := TAndroidPipesComm.Create;
 
  myAndroidPipesComm.InitializationFinished();
 
  // Here you can add any other initialization,
 
  // specially non-GUI code
 
 
 
  // Now we block our execution waiting for callbacks from Java
 
  myAndroidPipesComm.MessageLoop();
 
end.
 
</delphi>
 
  
[[Image:Simple Android app.png]]
+
  black_label := TTextView.Create;
 +
  black_label.setText('Black time:');
 +
  black_label.setVisibility(GONE);
 +
  black_label.setTextSize(COMPLEX_UNIT_PX, 40);
 +
  layout.addView(black_label);
  
===Compiling the example project in Linux===
+
  white_label := TTextView.Create;
 +
  white_label.setText('White time:');
 +
  white_label.setVisibility(GONE);
 +
  white_label.setTextSize(COMPLEX_UNIT_PX, 40);
 +
  layout.addView(white_label);
  
1> Get a working arm-linux cross-compiler which generates ARMv5 eabi with softfloat (as many phones like HTC Wildfire have no FPU)
+
  btn_move := TButton.Create;
 +
  btn_move.setText('Move finished!');
 +
  btn_move.setOnClickListener(@MyEventHandler.buttonMoveClickCallback);
 +
  btn_move.setVisibility(GONE);
 +
  layout.addView(btn_move);
  
To do this one can download an unofficial build from here:
+
  // Config UI
  
http://members.yline.com/~tom_at_work/fpc-2.4.2.UNOFFICIAL.arm-linux.tar . Compiled for ARMv5 eabi with softfloat.
+
  tv := TTextView.Create;
 +
  tv.setText('Please select how much to give to the players and press "Start Game":');
 +
  layout.addView(tv);
  
Or build your own. There are instructions here: [[Setup_Cross_Compile_For_ARM]]
+
  tp := TTimePicker.Create;
 +
  tp.setIs24HourView(True);
 +
  tp.setCurrentHour(0);
 +
  tp.setCurrentMinute(30);
 +
  layout.addView(tp);
  
2> Install the Android SDK. Instructions here: [[Android_Interface/Using_the_Android_SDK%2C_Emulator_and_Phones#Using_the_Android_SDK]]
+
  btn_start := TButton.Create;
 +
  btn_start.setText('Start game!');
 +
  btn_start.setOnClickListener(@MyEventHandler.buttonStartClickCallback);
 +
  layout.addView(btn_start);
  
3> Install ant, for example in Mandriva Linux:
+
  // And also allow the user to scroll the UI if it is larger then the screen width
 +
  // Scrolling takes place only horizontally
  
   urpmi ant
+
   scroller := TScrollView.Create;
 +
  scroller.addView(layout);
  
4> Open the project PascalNotes4Android/pascalsrc/pascalnotes4android.lpi in Lazarus and build it's build mode Android
+
  Activity.setContentView(scroller);
  
5> Copy the generate executable
+
  MyTimer := TAndroidTimer.Create;
 +
  MyTimer.OnTimer := @MyEventHandler.HandleOnTimer;
  
   cp pascalsrc/pascalnotes4android libs/armeabi/libpascalnotes4android.so
+
   // Now tell Java that the initialization has finished
 +
  vAndroidPipesComm.onCreateFinished();
 +
  // Here you can add any other initialization,
 +
  // specially non-GUI code
  
if the directory libs/armeabi doesn't exist, create it
+
   // Now we block our execution waiting for callbacks from Java
 
+
   vAndroidPipesComm.MessageLoop();
6> Build the APK file in debug mode
 
 
 
  ant debug
 
 
 
7> Connect your phone and make sure you can connect to it via ADB. More info here: [[Android_Interface/Using_the_Android_SDK%2C_Emulator_and_Phones#Recognition_of_devices_under_Linux]]
 
 
 
8> Install the APK file in your phone via ADB or whatever other method you prefer:
 
 
 
  ../android-sdk-linux_x86/tools/adb install bin/PascalNotes4Android-debug.apk
 
 
 
If the package is already installed you need need to do this instead:
 
 
 
  ../android-sdk-linux_x86/tools/adb uninstall com.pascalnotes
 
  ../android-sdk-linux_x86/tools/adb install bin/PascalNotes4Android-debug.apk
 
 
 
==Manifest configuration==
 
 
 
===Control the program restart===
 
 
 
By default the program will restart on orientation change, on keyboard change, and in a lot of other cases. This is usually unwanted. To disable program restart o keyboard showing and orientation changed add this to the manifest file:
 
 
 
<code>
 
  <activity
 
    ...
 
    android:configChanges="orientation|keyboardHidden"
 
</code>
 
 
 
See also:
 
 
 
* http://developer.android.com/guide/topics/manifest/activity-element.html#config
 
 
 
==Using other APIs==
 
 
 
===Using the Timer===
 
 
 
The Android API bindings include a handy timer control called TAndroidTimer. It works just like a Runnable, and inside it a Handler class is utilized to run the Runnable in the main GUI thread so that event executed in this timer can call Android APIs.
 
 
 
====Timer example====
 
 
 
<delphi>
 
program turbochessclock4android;
 
 
 
{$mode objfpc}{$H+}
 
 
 
uses
 
  Classes, SysUtils, androidpipescomm, androidview, javalang,
 
  androidapp, androidtimer;//, gles11;
 
 
 
type
 
  TEventHandler = class
 
  public
 
    procedure HandleOnTimer(ASender: TObject);
 
    procedure buttonClickCallback(v: TView);
 
  end;
 
 
 
var
 
   //...
 
   TimerCount: Integer = 0;
 
  MyTimer: TAndroidTimer;
 
  MyEventHandler: TEventHandler;
 
 
 
procedure TEventHandler.buttonClickCallback(v: TView);
 
begin
 
  MyTimer.postDelayed(100);
 
end;
 
 
 
procedure TEventHandler.HandleOnTimer(ASender: TObject);
 
begin
 
  Inc(TimerCount);
 
  tv.setText(Format('Timer event #%d', [TimerCount]));
 
  MyTimer.postDelayed(1000);
 
end;
 
 
 
begin
 
  MyEventHandler := TEventHandler.Create;
 
  // ...
 
 
end.
 
end.
 
</delphi>
 
</delphi>

Revision as of 19:08, 15 February 2011

Go back to Android Interface

Android API Hello World in Pascal

Here is an example Pascal application written for Android. It shows how to create controls, receive callback events and how to use the timer.

The full directory structure can be download with this svn command:

 svn co https://p-tools.svn.sourceforge.net/svnroot/p-tools/turbochessclock4android turbochessclock4android

Here is the Pascal code from this example:

<delphi> {

 A simple Chess Clock application
 Author: Felipe Monteiro de Carvalho - 2011
 License: Public Domain

} program turbochessclock4android;

{$mode objfpc}{$H+}

uses

 Classes, SysUtils, androidpipescomm, androidview, javalang,
 androidapp, androidtimer, androidutil;

type

 TEventHandler = class
 public
   procedure HandleOnTimer(ASender: TObject);
   procedure buttonStartClickCallback(v: TView);
   procedure buttonMoveClickCallback(v: TView);
 end;

var

 layout: TLinearLayout;
 params: TLayoutParams;
 tv, black_label, white_label: TTextView;
 scroller: TScrollView;
 btn_move, btn_start: TButton;
 tp: TTimePicker;
 WhiteTimeCount: Integer = 0;
 BlackTimeCount: Integer = 0;
 MyTimer: TAndroidTimer;
 MyEventHandler: TEventHandler;
 IsWhitePlayerMove: Boolean = True;

procedure TEventHandler.buttonStartClickCallback(v: TView); begin

 black_label.setVisibility(VISIBLE);
 white_label.setVisibility(VISIBLE);
 btn_move.setVisibility(VISIBLE);
 //
 WhiteTimeCount := tp.getCurrentHour() * 60 * 60 + tp.getCurrentMinute() * 60;
 BlackTimeCount := WhiteTimeCount;
 //
 MyTimer.removeCallbacks();
 MyTimer.postDelayed(100);

end;

procedure TEventHandler.buttonMoveClickCallback(v: TView); begin

 IsWhitePlayerMove := not IsWhitePlayerMove;

end;

procedure TEventHandler.HandleOnTimer(ASender: TObject); var

 lSeconds, lMinutes, lHours: Integer;

begin // Inc(TimerCount);

 if IsWhitePlayerMove then
 begin
   lSeconds := WhiteTimeCount mod 60;
   lMinutes := (WhiteTimeCount mod (60 * 60)) div 60;
   lHours := WhiteTimeCount div (60 * 60);
   white_label.setText(Format('White %d:%d:%d', [lHours, lMinutes, lSeconds]));
   Dec(WhiteTimeCount);
 end
 else
 begin
   lSeconds := BlackTimeCount mod 60;
   lMinutes := (BlackTimeCount mod (60 * 60)) div 60;
   lHours := BlackTimeCount div (60 * 60);
   black_label.setText(Format('White %d:%d:%d', [lHours, lMinutes, lSeconds]));
   Dec(BlackTimeCount);
 end;
 //
 MyTimer.removeCallbacks();
 MyTimer.postDelayed(1000);

end;

begin

 // Here add any initialization.
 // Any initialization code will be run inside Activity.onCreate,
 // so keep it as short as possible!
 // It should mostly contain GUI initialization
 // User interface
 MyEventHandler := TEventHandler.Create;
 // Prepares the UI of the program
 layout := TLinearLayout.Create;
 params := TLayoutParams.Create(androidview.FILL_PARENT, androidview.FILL_PARENT);
 layout.setLayoutParams(params);
 params.Free;
 layout.setOrientation(androidview.VERTICAL);
 // Game UI
 black_label := TTextView.Create;
 black_label.setText('Black time:');
 black_label.setVisibility(GONE);
 black_label.setTextSize(COMPLEX_UNIT_PX, 40);
 layout.addView(black_label);
 white_label := TTextView.Create;
 white_label.setText('White time:');
 white_label.setVisibility(GONE);
 white_label.setTextSize(COMPLEX_UNIT_PX, 40);
 layout.addView(white_label);
 btn_move := TButton.Create;
 btn_move.setText('Move finished!');
 btn_move.setOnClickListener(@MyEventHandler.buttonMoveClickCallback);
 btn_move.setVisibility(GONE);
 layout.addView(btn_move);
 // Config UI
 tv := TTextView.Create;
 tv.setText('Please select how much to give to the players and press "Start Game":');
 layout.addView(tv);
 tp := TTimePicker.Create;
 tp.setIs24HourView(True);
 tp.setCurrentHour(0);
 tp.setCurrentMinute(30);
 layout.addView(tp);
 btn_start := TButton.Create;
 btn_start.setText('Start game!');
 btn_start.setOnClickListener(@MyEventHandler.buttonStartClickCallback);
 layout.addView(btn_start);
 // And also allow the user to scroll the UI if it is larger then the screen width
 // Scrolling takes place only horizontally
 scroller := TScrollView.Create;
 scroller.addView(layout);
 Activity.setContentView(scroller);
 MyTimer := TAndroidTimer.Create;
 MyTimer.OnTimer := @MyEventHandler.HandleOnTimer;
 // Now tell Java that the initialization has finished
 vAndroidPipesComm.onCreateFinished();
 // Here you can add any other initialization,
 // specially non-GUI code
 // Now we block our execution waiting for callbacks from Java
 vAndroidPipesComm.MessageLoop();

end. </delphi>