DelphiXE5でFireMonkeyを使わずにAndroid開発
20130926修正:TEngine.Create()内のPAndroid_appを得るコードがNDKサンプル通りになっていなかったので修正
Delphi XE5がAndroid開発に対応しました。色々遊べて楽しいんですが、 まだまだ仕様が安定していないFireMonkeyだけで開発していくには若干の不安も残ります。
そこで、FireMonekyを使わずに素のNDKに近い状態でNativeActivity開発ができるかどうか試してみました。
需要は微妙ですが、せっかく試したので手順を残しておきます。
(続きにソースを載せますが、プロジェクトファイルも上げておきます Download)
Delphi XE5がAndroid開発に対応しました。色々遊べて楽しいんですが、 まだまだ仕様が安定していないFireMonkeyだけで開発していくには若干の不安も残ります。
そこで、FireMonekyを使わずに素のNDKに近い状態でNativeActivity開発ができるかどうか試してみました。
需要は微妙ですが、せっかく試したので手順を残しておきます。
(続きにソースを載せますが、プロジェクトファイルも上げておきます Download)
まず、空のFireMonkeyモバイルアプリケーションを新規作成してください。
このとき、フォームユニットが自動的に作成されますが、必要ないのでプロジェクトマネージャで削除しておいてください。
次にプロジェクトソースを表示してください。 (名前はNativeActivityTestにします)
次に新しいユニットを追加して、NativeActivityを扱うコードを書きます。今回はNDKサンプルのnative-activityをDelphiに移植してみました。↓こんな感じ
このとき、フォームユニットが自動的に作成されますが、必要ないのでプロジェクトマネージャで削除しておいてください。
次にプロジェクトソースを表示してください。 (名前はNativeActivityTestにします)
program NativeActivityTest; uses System.StartUpCopy, FMX.Forms, //* Unit9 in 'Unit9.pas' {Form9}; {$R *.res} begin Application.Initialize;//* Application.Run;//* end.上記のようなコードが表示されるので*行を削除します。
次に新しいユニットを追加して、NativeActivityを扱うコードを書きます。今回はNDKサンプルのnative-activityをDelphiに移植してみました。↓こんな感じ
(* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *) { Port to DelphiXE3 w/o FireMonkey 2013.9.25 TMaeda } unit NA_main; interface uses System.SysUtils, System.Math, Androidapi.AppGlue, Androidapi.NativeWindow, Androidapi.Looper, Androidapi.Input, Androidapi.Sensor, Androidapi.Egl, Androidapi.Gles, Androidapi.Gles2,Androidapi.Log; type // Our saved state data. TSaved_state = record angle: Single; x: Single; y: Single; end; PSaved_state = ^TSaved_state; // Shared state for our app. TEngine = class private FApp: PAndroid_app; FSensorManager: PASensorManager; FAccelerometerSensor: PASensor; FSensorEventQueue: PASensorEventQueue; FAnimating: integer; FDisplay: EGLDisplay; FSurface: EGLSurface; FContext: EGLContext; FWidth: integer; FHeight: integer; FState: TSaved_state; function InitDisplay: integer; procedure DrawFrame; procedure TermDisplay; procedure HandleCmd(Cmd: Int32); function HandleInput(Event: PAInputEvent): Int32; public constructor Create; procedure Run; end; implementation procedure Log_info(Fmt: String; Params: array of const); var Msg: string; M: TMarshaller; begin Msg := Format('native-activity ' + Fmt, Params); LOGI(M.AsAnsi(Msg).ToPointer); end; procedure Log_warn(Fmt: String; Params: array of const); var Msg: string; M: TMarshaller; begin Msg := Format('native-activity ' + Fmt, Params); LOGW(M.AsAnsi(Msg).ToPointer); end; { TEngine } constructor TEngine.Create; begin FApp := InitApp;//(System.DelphiActivity, nil, 0); // FApp.savedState := nil; end; (* * * Initialize an EGL context for the current display. *) function TEngine.InitDisplay: integer; const (* * Here specify the attributes of the desired configuration. * Below, we select an EGLConfig with at least 8 bits per color * component compatible with on-screen windows *) attribs: array[0..8] of EGLint = (EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_RED_SIZE, 8, EGL_NONE); var w, h, format: EGLint; numConfigs: EGLint; config: EGLConfig; surface: EGLSurface; context: EGLContext; display: EGLDisplay; begin // initialize OpenGL ES and EGL display := eglGetDisplay(EGL_DEFAULT_DISPLAY); eglInitialize(display, 0, 0); (* Here, the application chooses the configuration it desires. In this * sample, we have a very simplified selection process, where we pick * the first EGLConfig that matches our criteria *) eglChooseConfig(display, @attribs[0], @config, 1, @numConfigs); (* EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is * guaranteed to be accepted by ANativeWindow_setBuffersGeometry(). * As soon as we picked a EGLConfig, we can safely reconfigure the * ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID. *) eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, @format); ANativeWindow_setBuffersGeometry(FApp.window, 0, 0, format); surface := eglCreateWindowSurface(display, config, FApp.window, nil); context := eglCreateContext(display, config, nil, nil); if eglMakeCurrent(display, surface, surface, context) = EGL_FALSE then begin Log_warn('Unable to eglMakeCurrent',[]); result := -1; exit; end; eglQuerySurface(display, surface, EGL_WIDTH, @w); eglQuerySurface(display, surface, EGL_HEIGHT, @h); FDisplay := display; FContext := context; FSurface := surface; FWidth := w; FHeight := h; FState.angle := 0; // Initialize GL state. glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); glEnable(GL_CULL_FACE); glShadeModel(GL_SMOOTH); glDisable(GL_DEPTH_TEST); result := 0; end; (* * * Just the current frame in the display. *) procedure TEngine.DrawFrame; begin if FDisplay = nil then begin // No display. exit; end; // Just fill the screen with a color. glClearColor(FState.x / FWidth, FState.angle, FState.y / FHeight, 1); glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(FDisplay, FSurface); end; (* * * Tear down the EGL context currently associated with the display. *) procedure TEngine.TermDisplay; begin if FDisplay <> EGL_NO_DISPLAY then begin eglMakeCurrent(FDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); if FContext <> EGL_NO_CONTEXT then begin eglDestroyContext(FDisplay, FContext); end; if FSurface <> EGL_NO_SURFACE then begin eglDestroySurface(FDisplay, FSurface); end; eglTerminate(FDisplay); end; FAnimating := 0; FDisplay := EGL_NO_DISPLAY; FContext := EGL_NO_CONTEXT; FSurface := EGL_NO_SURFACE; end; (* * * Process the next input event. *) function engine_handle_input(App: PAndroid_app; Event: PAInputEvent) : Int32; cdecl; begin result := TEngine(App.userData).HandleInput(Event); end; function TEngine.HandleInput(Event : PAInputEvent) : Int32; begin if AInputEvent_getType(Event) = AINPUT_EVENT_TYPE_MOTION then begin FAnimating := 1; FState.x := AMotionEvent_getX(Event, 0); FState.y := AMotionEvent_getY(Event, 0); result := 1; end else begin result := 0; end; end; (* * * Process the next main command. *) procedure engine_handle_cmd(App: PAndroid_app; Cmd: Int32); cdecl; begin TEngine(App.userData).HandleCmd(Cmd); end; procedure TEngine.HandleCmd(Cmd : Int32); begin case Cmd of APP_CMD_SAVE_STATE: begin // The system has asked us to save our current state. Do so. New(PSaved_state(FApp.savedState)); PSaved_state(FApp.savedState)^ := FState; FApp.savedStateSize := sizeof(TSaved_state); end; APP_CMD_INIT_WINDOW: begin // The window is being shown, get it ready. if FApp.window <> nil then begin InitDisplay; DrawFrame; end; end; APP_CMD_TERM_WINDOW: begin // The window is being hidden or closed, clean it up. TermDisplay; end; APP_CMD_GAINED_FOCUS: begin // When our app gains focus, we start monitoring the accelerometer. if FAccelerometerSensor <> nil then begin ASensorEventQueue_enableSensor(FSensorEventQueue, FAccelerometerSensor); // We'd like to get 60 events per second (in us). ASensorEventQueue_setEventRate(FSensorEventQueue, FAccelerometerSensor, round((1000.0 / 60) * 1000)); end; end; APP_CMD_LOST_FOCUS: begin // When our app loses focus, we stop monitoring the accelerometer. // This is to avoid consuming battery while not being used. if FAccelerometerSensor <> nil then begin ASensorEventQueue_disableSensor(FSensorEventQueue, FAccelerometerSensor); end; // Also stop animating. FAnimating := 0; DrawFrame; end; end; end; (* * * This is the main entry point of a native application that is using * android_native_app_glue. It runs in its own thread, with its own * event loop for receiving input events and doing other things. *) procedure TEngine.Run; var ident, events: integer; source: Pandroid_poll_source; Event: ASensorEvent; begin // Make sure glue isn't stripped. app_dummy; FApp.userData := self; FApp.onAppCmd := engine_handle_cmd; FApp.onInputEvent := engine_handle_input; // Prepare to monitor accelerometer FSensorManager := ASensorManager_getInstance(); FAccelerometerSensor := ASensorManager_getDefaultSensor(FSensorManager, ASENSOR_TYPE_ACCELEROMETER); FSensorEventQueue := ASensorManager_createEventQueue(FSensorManager, FApp.Looper, LOOPER_ID_USER, nil, nil); if (FApp.savedState <> nil) then begin // We are starting with a previous saved state; restore from it. FState := PSaved_state(FApp.savedState)^; end; // loop waiting for stuff to do. while true do begin // Read all pending events. // If not animating, we will block forever waiting for events. // If animating, we loop until all events are read, then continue // to draw the next frame of animation. while true do begin ident := ALooper_pollAll(ifthen(FAnimating <> 0, 0, -1), nil, @events, @source); if ident < 0 then break; // Process this event. if source <> nil then begin source.process(FApp, source); end; // If a sensor has data, process it now. if ident = LOOPER_ID_USER then begin if FAccelerometerSensor <> nil then begin while (ASensorEventQueue_getEvents(FSensorEventQueue, @Event, 1) > 0) do begin Log_info('accelerometer: x=%f y=%f z=%f',[ Event.acceleration.x, Event.acceleration.y, Event.acceleration.z]); end; end; end; // Check if we are exiting. if FApp.destroyRequested <> 0 then begin TermDisplay(); exit; end; end; if FAnimating <> 0 then begin // Done with events; draw next animation frame. FState.angle := FState.angle + 0.01; if FState.angle > 1 then begin FState.angle := 0; end; // Drawing is throttled to the screen update rate, so there // is no need to do timing here. DrawFrame; end; end; end; end.最後にもう一度プロジェクトソースを開き、上述のコードを呼び出します。
program NativeActivityTest; uses System.StartUpCopy, NA_main in 'NA_main.pas'; {$R *.res} var app : TEngine; begin app := TEngine.Create; app.Run; app.Free; end.これで実機実行するとFireMonkeyを使わないAndroidアプリケーションの出来上がりです。
| 固定リンク
この記事へのコメントは終了しました。
コメント