はじめに
この記事は Android Advent Calendar 2019 の6日目です!
普段、Androidアプリケーションをどの言語どのフレームワークetc..を使って開発していますか?
自分は Android Studio + Java
や Unity + C#
で開発することが多いです
思えば自分が触ったことが無いフレームワークはたくさんありますが、自分が開発したいもの合わせて、よりベストな環境を選んでおきたい身としてはいろんなフレームワーク、言語を知って触っておくに越したことは無いのはエンジニアとしては当然だと思ってます。
なので、いろんなフレームワークを使って 同じ動作をする Androidアプリを作成します!
TL;DR
Androidアプリケーション開発のフレームワーク一覧
フレームワーク | 言語 | 公式ページ | IDE | 一言特徴 | 実装済(死に戻り) |
---|---|---|---|---|---|
Android Studio | Java/Kotlin/C++ | https://developer.android.com/studio/ | Android Studio | ネイティブアプリケーション | ✅ |
Unity | C# | https://unity3d.com/jp/unity | Unity Editor | ゲームエンジン | ✅ |
Processing | Java | https://processing.org/ | Processing | グラフィックフレームワーク | ✅ |
openFrameworks | C++ | https://openframeworks.cc/ | Android Studio | グラフィックフレームワーク | ✅ |
Cordova | HTML5+JavaScript+CSS | https://cordova.apache.org/ | Visual Studio(Win) | クロスプラットフォーム | ✅ |
React Native | Javascript/TypeScript | https://facebook.github.io/react-native/ | 特になし | クロスプラットフォーム | ✅ |
Xamarin.Form | C#/F# | https://dotnet.microsoft.com/apps/xamarin | Visual Studio | クロスプラットフォーム | ✅ |
Xamarin.Android | C#/F# | https://dotnet.microsoft.com/apps/xamarin | Visual Studio | ネイティブアプリケーション | ✅ |
Unreal Engine | C++/UnrealScript | https://www.unrealengine.com/ | Unreal Editor | ゲームエンジン | ❌ |
Flutter | Dart | https://flutter.dev/ | Android Studio | クロスプラットフォーム | ❌ |
Cocos2d-x | C++/Lua/JavaScript/TypeScript | https://www.cocos.com/en/ | Cocos Creator | ゲームエンジン | ❌ |
Monaca | HTML5+JavaScript | https://ja.monaca.io/ | ブラウザ/Monaca Localkit | クロスプラットフォーム | ❌ |
DXライブラリ | C++ | https://dxlib.xsrv.jp/ | Android Studio | ゲームエンジン | ❌ |
Qt | C++ | https://www.qt.io/ | Qt Creator IDE | クロスプラットフォーム | ❌ |
ionic | JavaScript | https://ionicframework.com/ | Ionic Studio | クロスプラットフォーム | ❌ |
Titanium | JavaScript | https://www.appcelerator.com/Titanium/ | Appcelerator Studio | クロスプラットフォーム | ❌ |
NativeScript | TypeScript/JavaScript | https://www.nativescript.org/ | VSCode/WebStorm | クロスプラットフォーム | ❌ |
Weex | JavaScript | https://weex.apache.org/ | 特になし | クロスプラットフォーム | ❌ |
meteor | JavaScript | https://www.meteor.com/ | 特になし | クロスプラットフォーム | ❌ |
Vue Native | JavaScript | https://vue-native.io/ | 特になし | クロスプラットフォーム | ❌ |
Defold | Lua | https://defold.com/ | Defold editor | ゲームエンジン | ❌ |
Godot | GDScript | https://godotengine.org/ | Godot Engine | ゲームエンジン | ❌ |
SCADE | Swift | https://www.scade.io/ | Scade IDE | クロスプラットフォーム | ❌ |
Kotlin Native | Kotlin | https://kotlinlang.org/docs/reference/native-overview.html | Android Studio | クロスプラットフォーム | ❌ |
AndEngine | Java | http://www.andengine.org/ | IntelliJ IDEA | ゲームエンジン | ❌ |
作るもの
画面にただHello World
と表示しても面白くないので、ボタンを押すと背景の色が変わるアプリを作ります。
UI要素がどのように設置できるか
ボタンイベントをどのように取得できるか
Viewはどのように描画できるか
、という点をみれるので良いチュートリアルだと思います
Android Studio
- Google謹製のAndroidプラットフォーム向けアプリ開発用の統合開発環境
- この環境が出るまでは、Eclipse + ADT (Android Developer Tools)でAndroidアプリの開発が行われていた
- なにより公式、ドキュメントもツールも揃っている
- ネイティブアプリケーションを作りたいなら王道
Java
まずは、JavaでAndroidアプリの作成を行います
-
Android Studioのインストール(説明は割愛します
-
Android Studioを立ち上げて、今回は新しくアプリを作成するので
Start a new Android Studio project
を選びます。最近開いたプロジェクトは左に表示されています
-
必要な情報を入力します。
Name
はアプリ名を入れます。Save Location
はプロジェクトを設置するディレクトリパスです。(既にプロジェクトが存在する場合はalready exists...
と警告してくれます
Finish
を押すとAndroidアプリのプロジェクトが生成されます
生成されたファイルはざっと以下の感じになります
AndroidStuidoJava
├── app
│ ├── build.gradle
│ └── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── jp
│ │ └── cha84rakanal
│ │ └── hellotogglecolor_studio_java
│ │ └── MainActivity.java
│ └── res
│ ├── drawable
│ │ └── ic_launcher_background.xml
│ ├── layout
│ │ └── activity_main.xml
│ └── values
│ ├── colors.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── gradle.properties
├── local.properties
└── settings.gradle
4.プロジェクトが生成されたらボタン実装する前に兎にも角にも一旦起動します。起動には上部の緑の▶マーク
を押すか、Ctrl + R
を押します。その後、インストールするデバイスを選んでOK
を押すと、ビルドされインストールされ起動します。
ボタンを実装
アプリが動くことを確認できたらボダンを実装していきます
res/layout/activity_main.xml
を開いて次の内容に変更します。するとボタンが画面中央に配置された画面が構成されます
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Hello World!" />
</RelativeLayout>
ボタンがクリックしたことを検知
設置したボタンはこのままではただのボタンなので、プログラムでボタンが押されたことを確認できるようにします。
MainActivity.java
に引数にView
クラスを1個を持ち返り値void
の適当なメソッドを作成します
package jp.cha84rakanal.hellotogglecolor_studio_java;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
void onButtonClick(View v){
Log.v(MainActivity.class.getSimpleName(),"onButtonClick");
}
}
Log.v
はログを出力するメソッドでボタンを押すと onButtonClick
とログに表示されます
ボタンを押したらどのメソッドを実行するか、今はわからないので、 activity_main.xml
で指定しますButton
要素に android:onClick
プロパティを追加して作成した適当なメソッド名を渡します
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:onClick="onButtonClick"
android:text="Hello World!" />
これで、ボタンを押すとログにonButtonClick
と表示するアプリになりました
ログはターミナルで、adb logcat
コマンドを実行するか、Android StudioのLogcat
を使います
Logのタグに MainActivity.class.getSimpleName()
を指定しているのでフィルターにMainActivity
を打つと、onButtonClick
のログだけを見れます
※ターミナルでのフィルタリング adb logcat -s MainActivity:*
背景の色を変更
残りは背景の色を変えるだけです
activity_main.xml
のRelativeLayout
要素に android:background
プロパティを追加して色を指定します。色の指定は#[透明度00~FF][赤00~FF][緑00~FF][青00~FF]
です
MainActivity.javaから参照できるように、IDをつけておきます
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/back"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFF0000"
tools:context=".MainActivity">
MainActivity.java
では、findViewById
を使って、先程IDをつけた要素を取得して setBackgroundColor
を使って色を変える部分を実装します。
package jp.cha84rakanal.hellotogglecolor_studio_java;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
public class MainActivity extends Activity {
private boolean mToggle = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
void onButtonClick(View v){
Log.v(MainActivity.class.getSimpleName(),"onButtonClick");
mToggle = !mToggle;
findViewById(R.id.back).setBackgroundColor( mToggle? 0xFFFF0000 : 0xFF0000FF);
}
}
これでボタンを押すと 赤から青、青から赤に色が変わるアプリができました!
Kotlin
引き続いて、Android StudioでKotlinを使って同じアプリを作っていきます
Android Studioでは、プロジェクトの中に モジュール
という単位で、アプリのプロジェクトやライブラリなどのプロジェクトが存在します。(デフォルトだとプロジェクトを作成した段階でのアプリのモジュール名は app
)
次は新しくプロジェクトを作らずに現在のプロジェクトにKotlinのアプリのモジュールを追加します
-
File > New > New Module... を開き、アプリなので
Phone & Tablet Module
を選択します
-
Source Languageを
Kotlin
にします。選択するだけでJavaもKotlinも使えるのはAndroid Studio素晴らしい!
Finish
を押すとAndroidアプリのモジュールが生成されます
先程作ったプロジェクトの中に、hellotogglecolor_studio_kotlin ディレクトリができてると思います
AndroidStuidoJava
├── app <-- さっきのJavaでのアプリ
├── hellotogglecolor_studio_kotlin <-- これから作るKotlinのアプリ
├── build.gradle
├── gradle.properties
├── local.properties
└── settings.gradle
ただ色が変わるだけのアプリでは、JavaとKotlinでの書き方がちょっと違うぐらいなので、別の方法で実装すすめていきます
ボタンを実装
すべてMainActivity.kt
で実装します
MainActivity
におけるルートViewはConstraintLayout
でその子にButton
があり、センタリングは上下左右の制約で行います。
制約をKotlinで書くと凄い大変なので、極力XMLでレイアウトを組むのが良さそうです...
package jp.cha84rakanal.hellotogglecolor_studio_kotlin
import android.app.Activity
import android.os.Bundle
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import android.widget.Button
import android.view.View
class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val cLayout = ConstraintLayout(this)
val button = Button(this)
cLayout.addView(button)
val viewId = View.generateViewId()
button.id = viewId
button.text = "Hello World!"
val constraintSet = ConstraintSet()
constraintSet.clone(cLayout)
// android:layout_width="wrap_content"
constraintSet.constrainHeight(button.id,
ConstraintSet.WRAP_CONTENT)
//android:layout_height="wrap_content"
constraintSet.constrainWidth(button.id,
ConstraintSet.WRAP_CONTENT)
// app:layout_constraintBottom_toBottomOf="parent"
constraintSet.connect(
button.id,
ConstraintSet.BOTTOM,
ConstraintSet.PARENT_ID,
ConstraintSet.BOTTOM,
0)
// app:layout_constraintLeft_toLeftOf="parent"
constraintSet.connect(
button.id,
ConstraintSet.LEFT,
ConstraintSet.PARENT_ID,
ConstraintSet.LEFT,
0)
// app:layout_constraintRight_toRightOf="parent"
constraintSet.connect(
button.id,
ConstraintSet.RIGHT,
ConstraintSet.PARENT_ID,
ConstraintSet.RIGHT,
0)
// app:layout_constraintTop_toTopOf="parent"
constraintSet.connect(
button.id,
ConstraintSet.TOP,
ConstraintSet.PARENT_ID,
ConstraintSet.TOP,
0)
constraintSet.applyTo(cLayout)
// ConstraintLayout set on ContentView
setContentView(cLayout)
}
}
これでボタンが中央に実装されました。
ボタンがクリックしたことを検知
ボタンのクリック検知にはOnClickListener
を使います
onCreate
メソッドの中のbutton
の定義の後に次のコードを加えます
button.setOnClickListener {
Log.v(MainActivity::class.java.simpleName, "onButtonClick")
}
これで、ボタンを押すとログにonButtonClick
と表示するアプリになりました
背景の色を変更
背景色だけはJavaとほとんど変わらず、setBackgroundColor
を使えばできます
Kotlinでは三項演算子がなく、ifを式として扱えるのは特徴的な気がします
package jp.cha84rakanal.hellotogglecolor_studio_kotlin
import android.app.Activity
import android.graphics.Color
import android.os.Bundle
import android.util.Log
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import android.widget.Button
import android.view.View
class MainActivity : Activity() {
private var mToggle = true
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val cLayout = ConstraintLayout(this)
cLayout.setBackgroundColor(Color.RED)
val button = Button(this)
cLayout.addView(button)
val viewId = View.generateViewId()
button.id = viewId
button.text = "Hello World!"
button.setOnClickListener {
Log.v(MainActivity::class.java.simpleName, "onButtonClick")
mToggle = !mToggle
cLayout.setBackgroundColor(if(mToggle) Color.RED else Color.BLUE)
}
見た目は、Java
で作られたのと変わらなかったです...
C++
Android Studioで使える言語は、Java
Kotlin
だけではありません。 NDK(Native Development Kit)を利用すれば C/C++
でも開発ができます
いままでJava/Kotlinで開発していた Activity
をまるごと実装することができるので、今回はNativeActivityを使って、目的のアプリをつくります。
-
NDKを使える環境をインストールします。 Tools > SDK Managerを使って、NDK/CMakeをインストールします。(File > Project Structureからもダウンロードできます)
File > Project Structure
を開いて、Android NDK Location
にNDKをインストールしたディレクトリパスが指定されていればOKです
-
一からプロジェクトを作成すると大変なので、既にできているサンプルを改造します。
File > New > Import Module...
を開き、検索窓にNative
と打ち込みます。するとサンプルの一覧にNdk > Native Activity
とあるのでそれを選択します。
Finish
を押すとサンプルがGitHubからクローンされます。
- 自動でプロジェクトが開くので、
File > Sync Project with Gradle Files
を押します。それが終わったら兎にも角にもビルドしてアプリをRUN
しましょう
ボタンを実装
これからボタンを実装していきます。C++
だけで実装されたアプリのソースは、app/src/main/cpp/main.cpp
だけなので、ボタンも描画もここに実装していくしかなさそうです。しかし、TeapotのNDKサンプルでは、Java側でUIが書かれているので参考にしていきます。
まずは、Java側のActivityを実装していきます。
app/src/main
にjava
ディレクトリを作成して、java
ディレクトリにActivityを作成します。java
ディレクトリを右クリックして New > Activity > Empty Activity
を選択します。パッケージ名はここではjp.cha84rakanal.hellotogglecolor_studio_c_plusplus
にします。
MainActivity.javaとactivity_main.xmlが自動生成されて、ディレクトリの構造は以下のようになります。
main
├── AndroidManifest.xml
├── cpp
│ ├── CMakeLists.txt
│ └── main.cpp
├── java
│ └── jp
│ └── cha84rakanal
│ └── hellotogglecolor_studio_c_plusplus
│ └── MainActivity.java
└── res
├── layout
│ └── activity_main.xml
├── mipmap-hdpi
│ └── ic_launcher.png
├── mipmap-mdpi
│ └── ic_launcher.png
├── mipmap-xhdpi
│ └── ic_launcher.png
├── mipmap-xxhdpi
│ └── ic_launcher.png
└── values
└── strings.xml
自動生成されてMainActivity.javaはAppCompatActivityクラスを継承していますが、C++のActivityと共存させるためにNativeActivity
クラスを継承します。 MainActivity.javaの内容は次のようにして、アプリが起動したらログにin onCreate
と出るようにします。
package jp.cha84rakanal.hellotogglecolor_studio_c_plusplus;
import android.app.NativeActivity;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends NativeActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.v("java_code","in onCreate");
}
}
このままでは、MainActivity.javaが起動しないので、AndroidManifest.xml
を編集しておきます。application
要素のandroid:hasCode="false"
を消して、activity
要素のandroid:name
を先程作成したActivityの名前にします。
<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="jp.cha84rakanal.hellotogglecolor_studio_c_plusplus"
android:versionCode="1"
android:versionName="1.0">
<!-- This .apk has no Java code itself, so set hasCode to false. -->
<application
android:allowBackup="false"
android:fullBackupContent="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name">
<!-- Our activity is the built-in NativeActivity framework class.
This will take care of integrating with our NDK code. -->
<activity android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="orientation|keyboardHidden">
<!-- Tell NativeActivity the name of our .so -->
<meta-data android:name="android.app.lib_name"
android:value="native-activity" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
<!-- END_INCLUDE(manifest) -->
これでアプリをビルド/RUNするとC++で作成したmain.cppとJavaで作成したMainActivity.javaが起動していることを確認できると思います。
次は、XMLで定義したViewを表示していけるようにします。サンプルをみると、onCreate
ではsetContentView
をしておらず、showUI
というメソッドでUIを表示しているみたいです。なのでまずはMainActivity
にshowUI
メソッドを実装します。
MainActivity activity;
PopupWindow popupWindow;
// Our popup window, you will call it from your C/C++ code later
@SuppressLint("InflateParams")
public void showUI() {
Log.v("java_code","showui Called");
if( popupWindow != null )
return;
activity = this;
this.runOnUiThread(()->{
LayoutInflater layoutInflater
= (LayoutInflater)getBaseContext()
.getSystemService(LAYOUT_INFLATER_SERVICE);
View popupView = layoutInflater.inflate(R.layout.activity_main, null);
popupWindow = new PopupWindow(
popupView,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT);
LinearLayout mainLayout = new LinearLayout(activity);
ViewGroup.MarginLayoutParams params = new ViewGroup.MarginLayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT);
params.setMargins(0, 0, 0, 0);
activity.setContentView(mainLayout, params);
// Show our UI over NativeActivity window
popupWindow.showAtLocation(mainLayout, Gravity.CENTER, 0, 0);
popupWindow.update();
});
}
layoutInflater.inflate
でR.layout.activity_main
をしているので、activity_main.xml
を次のように変更しておきます
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Hello World!" />
</RelativeLayout>
これで、ビルド/RUNで画面の真ん中にHello World!!
と表示されるわけではありません。showUI
メソッドがどこからも呼び出されていません。じゃあどこから呼び出すのかというとC++で実装しているNativeActivitymain.cpp
から呼び出します。 main.cpp
に次の関数を加えます。実行中のVMにアタッチして、showUIをC++側から呼び出せるようにします。
void showUI(struct engine* engine) {
JNIEnv* jni;
engine->app->activity->vm->AttachCurrentThread(&jni, NULL);
// Default class retrieval
jclass clazz = jni->GetObjectClass(engine->app->activity->clazz);
jmethodID methodID = jni->GetMethodID(clazz, "showUI", "()V");
jni->CallVoidMethod(engine->app->activity->clazz, methodID);
engine->app->activity->vm->DetachCurrentThread();
return;
}
そして、engine_init_display(struct engine* engine)
の一番最後で、加えたshowUI(struct engine* engine)
を呼び出します。
/**
* Initialize an EGL context for the current display.
*/
static int engine_init_display(struct engine* engine) {
// initialize OpenGL ES and EGL
//-----省略-----//
// Initialize GL state.
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
glEnable(GL_CULL_FACE);
glShadeModel(GL_SMOOTH);
glDisable(GL_DEPTH_TEST);
ShowUI(engine); //<--さっき追加した関数
return 0;
}
これで、NativeActivityでの画面の初期化が終わったあとに、Java側でViewをオーバーレイして表示します。
これで、背景はC++でUIがJavaの状態で画面中央にHello World
と表示されました!
ここまで来るとボタンの設置は簡単です。activity_main.xml
を次のように変更するだけでボタンを実装できます。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Hello World!" />
</RelativeLayout>
ボタンがクリックしたことを検知
Java側でボタンがクリックされたことを検知することは簡単ですね。ボタンにbutton
とIDをふっているので、setOnClickListener
メソッドを使っておしまいです。
popupView.findViewById(R.id.button).setOnClickListener(v -> {
Log.e("java_code","ButtonClicked Called");
});
Java側では検知できましたが、C++側は何もわかりません。背景の描画を担当しているのは、C++側なので、JavaからC++の関数を叩いて上げる必要があります。
まずmain.cpp
にJavaから呼ばれる関数を作成します。命名規則は Java_{Package Name}_{呼び出し元クラス名}_{メソッド名}
です。引数や戻り値などついてもろもろありますがここでは説明ません。詳しいことはandroid jni
とかでググってください。 main.cpp
に次のような関数を追加します。
extern "C"
JNIEXPORT void JNICALL
Java_jp_cha84rakanal_hellotogglecolor_1studio_1c_1plusplus_MainActivity_toggle(JNIEnv * env, jobject obj){
LOGW("called in native");
}
次に、MainActivityから呼び出せるようにします。System.loadLibrary
メソッドの引数に、native-activity
を指定して、Java側でC++の関数を使えるようにロードします。あとは、C++に実装した関数を、 public native {戻り値の型} {関数名}();
のようにJavaで定義します。そうすることで、Javaメソッドのように普通にC++の関数を呼び出せます。
public class MainActivity extends NativeActivity {
MainActivity activity;
PopupWindow popupWindow;
static {
System.loadLibrary("native-activity");
}
public native void toggle();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.v("java_code","in onCreate");
}
//----省略----//
popupView.findViewById(R.id.button).setOnClickListener(v -> {
Log.e("java_code","ButtonClicked Called");
toggle();
});
//----省略----//
これで、ボタンをを押すと、Java側で、ButtonClicked Called
とログが出力され、C++でcalled in native
とログが出力されます。
これで、C++でもボタンがクリックされたことわかりました。
背景の色を変更
背景の描画はmain.cpp
のengine_draw_frame(struct engine* engine)
で行われています。実装をみると、EGL/OpenGLESを使って描画されているのがわかります。とすると、単に背景を塗りつぶすだけならglClearColor
で良さそうです。なのでmain.cpp
を次のように変更していきます。
static int toggle_count = 0;
extern "C"
JNIEXPORT void JNICALL
Java_jp_cha84rakanal_hellotogglecolor_1studio_1c_1plusplus_MainActivity_toggle(JNIEnv * env, jobject obj){
LOGW("called in native");
toggle_count++;
}
/**
* Just the current frame in the display.
*/
static void engine_draw_frame(struct engine* engine) {
if (engine->display == NULL) {
// No display.
return;
}
if(toggle_count%2 == 0){
// Just fill the screen with a color.
glClearColor(1,0,0,1);
}else{
glClearColor(0,0,1,1);
}
glClear(GL_COLOR_BUFFER_BIT);
eglSwapBuffers(engine->display, engine->surface);
}
※engine.animating
が1じゃないとengine_draw_frame(struct engine* engine)
が呼ばれないので注意
とりあえずこれでC++
??でも同じアプリができました!
Nativeで動いてるとなんだかよくわからんけど速いって気持ちになっていいですね。Android NDKも色々できることがあるので今後まとめて行けたらと思います。
Unity
- ゲーム開発エンジン
- クロスプラットフォームの開発が可能
- アセットと呼ばれる素材やツールが便利
- プラグインという形でOS固有のAPIを呼び出すことも可能
それでは、Unityを使ってAndroidアプリの作成をおこなっていきます!
-
Unity Editor/Unity Hubのインストール(説明は割愛します。
Android Build Support
を入れるのを忘れずに)
これでUnity Editorの画面が表示されます。
兎にも角にもこの状態でAndroid上で動かして、エラーとか起こらないことを確認しましょう。
File > Build Settings
を開くと、 PC Mac & Linux Standalone
にUnityマークがあるので、Androidを選択してSwitch Platform
を押します。
そうすると Android
にUnityマークがついて、Build and Run
を押せるので押します。ファイルダイアログが出るので、適当にBuild
ディレクトリを作ってSave
を押します。そうすると、Unity内部でビルドがはしってAPKが生成され自動でインストールされ実行されます。
Unityのロゴが表示されて、何もない暗いブルーが表示されます。
ボタンを実装
それでは、ボタンを実装していきます。Unityではシーンにコンポーネントを設置して、ゲームやアプリを作っていきます。
Hierarchy
ウィンドウで右クリックを押してUI > Button
をクリックでボタンが設置されます。
これでBuild and Run
をするとなんとなく中央から左下に向かう途中あたりにボタンが来ます。
位置を真ん中にして押しやすいようにもうちょっと大きくしようと思います。
Hierarchy
に表示されているCanvas
をクリックすると右側のInspector
に、Canvasに設定されてるコンポーネントの情報が表示されます。
Canvas Scaler (Script)
という項目があるので、そこの設定を以下のようにしましょう。これで、横幅を基準に横1080x縦1920の領域として画面がレンダリングされます。
Unity EditorのGameウィンドウで確認すると、アスペクト比や位置を維持していますね。
次は、Hierarchy
に表示されているCanvas > Button
をクリックすると右側のInspector
に、Buttonに設定されてるコンポーネントの情報が表示されます。
Rect Transform
という項目があるので、そこの設定を以下のようにしましょう。これでボタンが中心に位置して大きくなります。
文字もよしなに大きくするとAndroid上でこんな感じになります。
ボタンの実装はこれで良さそうです。
ボタンがクリックしたことを検知
次は、ボタンイベントを取得しましょう。Assets > Create > C# Script
でC#ファイルをUntiyプロジェクトに作成します。名前は今回はButtonClick
としました。 Start()
メソッドはUnityのロジックから呼ばれるものでシーンの1番最初に呼ばれます。Update()
メソッドもUnityのロジックから呼ばれるものでシーンの描画の際毎回呼ばれます。このクラスにボタンが押されたら呼ばれるメソッドを実装します。メソッド名は適当に、onClick
にして、呼ばれたらログを出すようにしておきます。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ButtonClick : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public void onClick(){
Debug.Log("On Click");
}
}
この作成したButtonClick
クラスをインスタンス化できるように、GameObjectにアタッチしようと思います。
Hierarchy
ウィンドウで右クリックを押してCreate Empty
をクリックでGameObjectが設置されます。この設置されたGameObjectをクリックすると、右側のInspector
にはTransform
だけが表示されていると思います。GameObject
にButtonClick
クラスをアタッチするのは簡単で、先ほど作成したC#ScriptをInspector
ウィンドウにドラッグアンドドロップするだけです。(Add Componentからもアタッチできます。
GameObjectへのアタッチを終えたら、次はButtonからonClick()
呼べるようにします。Hierarchy
ウィンドウにあるButtonのInspectorを開きます。Button (Script)
の項目にOn Click()
があり、そこは、List is Empty
になってると思います。
On Click()
についている+
マークを押すと、このようにListに要素が追加されます。
Hierarchy
ウィンドウにある先ほど作成したGameObject
をOn Click()
のListにあるNone (Object)
に向かってドラッグアンドドロップすると、次のようになり、ButtonがButtonClick
クラスのメソッドにアクセスできる状態になります
No Function
となっているところをクリックすると、リストがドロップダウンするので、先ほどGameObjectにアタッチしたクラスと、呼びたいメソッドを指定できます。
これでボタンを押すと、ログとOn Click
表示されます。実際のUnity Editor上で確認するとこんな感じです。
背景の色を変更
あとは、背景を色で塗りつぶすだけです。Hierarchy
ウィンドウで右クリックを押してUI > Panel
をクリックで、画面を覆う1枚のパネルが設置されます。UnityのHierarchy
は上の方が奥側なので、Panel
をButton
より上にします。設置したPanel
のInspector
を開くと、Image (Script)
にColor
の項目があるので、これをいじるとパネルの色が変わります。
このパネルの色をonClick()
メソッド内で変えることができると良さそうですね。色を変えられるようにButtonClick.cs
を次のように変更します。このスクリプトのこの状態では、Imageクラスのimg
がNull
なような気がします。実際、このままではNull
なので、Unity Editor上でImageクラスのimg
にオブジェクトを代入?アタッチ?します。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ButtonClick : MonoBehaviour
{
public Image img;
private bool toggle = false;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public void onClick(){
Debug.Log("On Click");
img.color = (toggle)? Color.red : Color.blue;
toggle = !toggle;
}
}
GameObject
のInspector
を開いてみると、ButtonClick (Script)
のimg
の項目がNone (Image)
になっています。None (Image)
の右側のターゲットマークをクリックするとImageをアタッチしているGameObjectの一覧が表示されるので、Pnaelを選択します。するとimgにPanelにアタッチされたImageが代入?アタッチ?されます。
これで背景色を変えられるようになったので、アプリは完成です!
(4つも同じアプリを作ってかつまとめると疲れてきますね...
Processing
- クリエイティブコーディングのためのツール
- エンジニアではない人でも簡単に扱える
- ライブラリが様々あって便利
- 言語はJavaで描画をうまくWrapしてくれている
傾向を変えてアAndroidアプリ開発としてはマイナーなProcessing
でアプリを作って行きます。
-
Processingをインストールします。公式のダウンロードページからダウンロードしてZIPを解凍するだけです。
-
Processingを起動すると、IDEが立ち上がってすぐにコーディングを開始できます。デフォルトでは
Java
モードなので、PC上でプログラムが動作します。
-
IDE右上の
Java ▼
とあるところをクリックするとモードの追加...
とあるのでクリックします。すると、ライブラリマネージャーが表示されるので、Android Mode
を選択してInstall
を押します。インストールが完了するとstatus
に緑のチェックマークがつくので、ライブラリマネージャーを閉じます。
-
閉じた後、IDE右上の
Java ▼
とあるところをクリックするとAndroid
の項目が追加されているので、Android
を押すとAndroidモードになります。 -
またまた、兎にも角にも実行してみます。
Android > Devices >
にPCに接続しているデバイスがあることを確認したら、IDE左上の▶マーク
を押します。すると、ビルドが走りインストールされアプリがRUNします。
ボタンを実装
Processingは描画が専門な感じなので、UIを作るとなるとライブラリが必要になります。 ProcessingではcontrolP5 という、GUI作成するライブラリがあるので、これを使います。
スケッチ > インポートライブラリ > ライブラリを追加...
でライブラリマネージャーが開くので、ControlP5
を検索し、インストールします。Processing
といっても基本はJava
なので、ライブラリのインポートは import controlP5.*;
でおしまいです。ボタンをただ置くだけならIDEに次のプログラムを書くだけです。
import controlP5.*;
ControlP5 control;
void setup() {
fullScreen();
control = new ControlP5(this);
control.addButton("onClick")
.setLabel("Red_Button")
.setPosition(50, 40)
.setSize(100, 40)
.setColorActive(color(0, 40)) //押したときの色
.setColorBackground(color(255)) //通常時の色
.setColorForeground(color(255)) //マウスを乗せた時の色
.setColorCaptionLabel(color(0)); //テキストの色
}
void draw() {
}
めちゃくちゃ小さいですが、ボタンを置くことができました。
ボタンがクリックしたことを検知
クリックの検知は簡単で、addButton()
の引数と同じ名前のメソッドを作成するだけで、ボタンイベントを拾うことができます。今回は"onClick"
としているのでonClick()
メソッドを実装します。println
メソッドを使うとProcessingのIDEのコンソールに文字を表示させられます。
import controlP5.*;
ControlP5 control;
int count = 0;
void setup() {
fullScreen();
control = new ControlP5(this);
control.addButton("onClick")
.setLabel("Red_Button")
.setPosition(50, 40)
.setSize(100, 40)
.setColorActive(color(0, 40)) //押したときの色
.setColorBackground(color(255)) //通常時の色
.setColorForeground(color(255)) //マウスを乗せた時の色
.setColorCaptionLabel(color(0)); //テキストの色
}
void draw() {
}
void onClick(){
println("onClick:" + count);
count++;
}
クリックの検知もできたので、文字もボタンも小さいのを改善します。
まずは文字を改善します。、ツール > フォントの作成...
を開いて、好きなフォントを選びます。今回はHelvetica
を選びました。サイズを128
ぐらいにして、OK
を押すとフォントファイルが生成されます。
フォントファイルはloadFont
メソッドで読み込んで使います。ボタンのサイズはsetPosition(int,int)
setSize(int,int)
を使うと改善できそうです。なのでHelloToggleColor.pde
を次のように変更します。
import controlP5.*;
ControlP5 control;
int count = 0;
void setup() {
fullScreen();
PFont p = loadFont("Helvetica-128.vlw");
ControlFont font = new ControlFont(p);
font.setSize(32);
int button_width = (int)(width*0.25);
int button_height = (int)(height*0.08);
control = new ControlP5(this);
control.setFont(font);
control.addButton("onClick")
.setLabel("Button")
.setSize(button_width,button_height)
.setPosition(width/2 - button_width/2, height/2 - button_height/2)
.setColorActive(color(0, 40)) //押したときの色
.setColorBackground(color(255)) //通常時の色
.setColorForeground(color(255)) //マウスを乗せた時の色
.setColorCaptionLabel(color(0)); //テキストの色
}
void draw() {
}
void onClick(){
println("onClick:" + count);
count++;
}
文字のサイズ、ボタンの位置、ボタンのサイズがちょうどよくなりました。
背景の色を変更
Processing
で背景を特定の色で塗りつぶすのは簡単です。background
メソッドを使うだけなので、プログラムは次のようになります。
import controlP5.*;
ControlP5 control;
int count = 0;
boolean toggle = false;
void setup() {
fullScreen();
background(255, 0, 0);
PFont p = loadFont("Helvetica-128.vlw");
ControlFont font = new ControlFont(p);
font.setSize(32);
int button_width = (int)(width*0.25);
int button_height = (int)(height*0.08);
control = new ControlP5(this);
control.setFont(font);
control.addButton("onClick")
.setLabel("Button")
.setSize(button_width,button_height)
.setPosition(width/2 - button_width/2, height/2 - button_height/2)
.setColorActive(color(0, 40)) //押したときの色
.setColorBackground(color(255)) //通常時の色
.setColorForeground(color(255)) //マウスを乗せた時の色
.setColorCaptionLabel(color(0)); //テキストの色
}
void draw() {
}
void onClick(){
println("onClick:" + count);
count++;
toggle = !toggle;
if(toggle){
background(0, 0,255);
}else{
background(255, 0, 0);
}
}
これでProcessing
でも完成しました。
openFrameworks
- クリエイティブコーディングのためのツール
- エンジニアではない人でも簡単に扱える
- アドオンが豊富で便利
- C++で書かれているため速度だせる(?)
同じく、Androidアプリ開発としてはマイナーなopenFrameworks
でアプリを作ります。
この記事では v0.11.0
の android
版を使います。
-
openFrameworks
をインストールします。これも、公式のダウンロードページからダウンロードしてZIPを解凍するだけです。 今回は、mobileの行にあるAndroid版をダウンロードしてきます。
2.of_v0.11.0_android_release/projectGenerator-{OS}
ディレクトリにあるprojectGenerator
を開きます。初めて開くとおそらくopenFrameworksのパスを求められるので先ほどダウンロードして展開したディレクトリを選択します。Project Nameにプロジェクトの名前、Project pathにプロジェクトの保存先を指定して、Generate
を押すとopenFrameworks
のプロジェクトが生成されます。(念のためPlatformがAndroidになっていることを確認しておきましょう
Android Studio側でTools -> Create Command-line Launcher...
の設定を行っているとOpen in IDE
でAndroid Studioが開きます。
3.筆者は設定していないので手動でAndroid Studio
を開き、 Import Project (Gradle ,Eclipse ADT, etc.)
をクリックして、生成されたプロジェクトのディレクトリを選択します。Openを押すと自動でGradle Syncが走ります。(Gradle Syncが走らない場合は、手動でSyncします。
4.Gradle Sync
に失敗した場合は、build.gradle
とsettings.gradle
を修正する必要があります。スクリプトファイルでは、openFrameworks
のパスが../../../
になっているので正しいパスに直します。
自分の環境では/Users/cha84rakanal/Documents/of_v0.11.0_android_release
ディレクトリにopenFrameworks
を配置しているので、そのパスに置き換えます。
def ofRoot(){ return '/Users/cha84rakanal/Documents/of_v0.11.0_android_release/' }
// Load common functions
apply from: ofRoot()+"libs/openFrameworksCompiled/project/android/common-functions.gradle"
buildscript {
apply from: "/Users/cha84rakanal/Documents/of_v0.11.0_android_release/libs/openFrameworksCompiled/project/android/ndk-verify.gradle"
repositories {
jcenter()
}
dependencies {
// Using the gradle-experimental version that supports c++
classpath 'com.android.tools.build:gradle-experimental:0.9.3'
}
}
// openFrameworks-relative root directories (don't touch)
def ofRoot = '/Users/cha84rakanal/Documents/of_v0.11.0_android_release/'
// Load common functions
apply from: ofRoot+"libs/openFrameworksCompiled/project/android/common-functions.gradle"
ERROR: Wrong version of NDK library found. Found version 19.2.5345600, but openFrameworks requires version r15c
というエラーが出た場合は、local.properties
を編集してAndroid NDK r15cのインストールされてるパスを指定します。
5.Gradle Sync
が通れば、まずは実行してみます
灰色で背景が塗りつぶされていれば正しいです。
ボタンを実装
openFrameworksもProcessingと同様に、描画が専門な感じなので、UIを作るとなるとアドオンが必要になります。openFrameworksではofxDatGui という、GUI作成するアドオンがあるので、これを使います。 試してみたらofxDatGui
を使ってできなかったので、 Android Studio + NDKで使った方法でボタンを実装していきます。(この方法はずるいなぁと思いながら...
AndroidManifest.xml
をみると、起動するActivityがcc.openframeworks.HelloColorToggle.OFActivity
となっているので、srcJava/OFActivity.java
を見てみます。
<activity
android:name="cc.openframeworks.HelloColorToggle.OFActivity"
android:label="@string/app_name"
android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
OFActivity.java
にはこれといった実装はないのですが、cc.openframeworks.OFActivity
クラスを継承しています。なので、cc.openframeworks.OFActivity
にマウスカーソルを合わせて、右クリックしてGo To > Declaration
で定義に飛びます。
すると、this.setContentView(view);
で、R.layout
のXMLで定義されたViewを読み込んでいることを発見できました。
public void initView(){
String packageName = this.getPackageName();
try {
Log.v("OF","trying to find class: "+packageName+".R$layout");
Class<?> layout = Class.forName(packageName+".R$layout");
View view = this.getLayoutInflater().inflate(layout.getField("main_layout").getInt(null),null);
if(view == null) {
Log.w("OF", "Could not find main_layout.xml.");
throw new Exception();
}
this.setContentView(view);
Class<?> id = Class.forName(packageName+".R$id");
mOFGlSurfaceContainer = (ViewGroup)this.findViewById(id.getField("of_gl_surface_container").getInt(null));
if(mOFGlSurfaceContainer == null) {
Log.w("OF", "Could not find of_gl_surface_container in main_layout.xml. Copy main_layout.xml from latest empty example to fix this warning.");
throw new Exception();
}
} catch (Exception e) {
Log.w("OF", "couldn't create view from layout falling back to GL only",e);
mOFGlSurfaceContainer = new FrameLayout(this);
this.setContentView(mOFGlSurfaceContainer);
}
}
なので、ボタンを配置するだけなら、res/layout/main_layout.xml
で配置するだけでおしまいです。main_layout.xml
を次のように編集するとボタンが置かれます
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="@+id/relativeLayout1" android:layout_width="fill_parent" android:layout_height="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android">
<RelativeLayout android:id="@+id/of_gl_surface_container" android:layout_width="fill_parent" android:layout_height="fill_parent"/>
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
<!-- add here other views' layouts -->
</RelativeLayout>
ボタンがクリックしたことを検知
次は、ボタンがクリックしたことを検知します。親クラスでsetContentView()
したViewに子クラスでもアクセスできるのでsrcJava/OFActivity.java
でボタンにOnClickListenerを実装します。(普通に親のabstructクラスに実装はするのはよくないし...
サクッとfindViewById()
メソッドをつかってsetOnClickListener()
メソッドでOnClickListener
当てます。これで、ボタンをクリックするとログにonClick
と表示されます。
public class OFActivity extends cc.openframeworks.OFActivity{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.e("of","onClick");
}
});
}
今回も描画自体は、C++側で実装されているので、JNIを使ってC++側にボタンがクリックされたことを伝えて行きます。
まずは、C++側にJavaから呼び出せる関数を実装します。src/ofApp.h
に#include <jni.h>
を加えます。
#pragma once
#include "ofMain.h"
#include "ofxAndroid.h"
#include <jni.h> // <---- 追加
class ofApp : public ofxAndroidApp{
public:
//----省略----//
次に、実際に呼び出す関数をsrc/ofApp.cpp
に実装します。今回はclick
というメソッド名で呼び出せるようにします。命名規則通りの関数名は次のようになります。
#include "ofApp.h"
extern "C"
JNIEXPORT void JNICALL
Java_cc_openframeworks_HelloColorToggle_OFActivity_click(JNIEnv* env,jobject thiz){
ofBackground(255,255,255);
}
//--------------------------------------------------------------
void ofApp::setup(){
}
あんまり良くないTipsですが、 C++でつける関数名がわからない場合は、C++
で何も実装せずにJava側
でpublic native {戻り値の型} {関数名}(引数...);
とメソッド名だけ定義して適当なところで呼び出す形で実行します。アプリがUnsatisfiedLinkError
をだして、本来呼べたはずの関数名をログに出力するので、それを使います。下の場合だとhoge()
というメソッド名で関数を作成したい場合はJava_cc_openframeworks_HelloColorToggle_OFActivity_hoge
という関数名でC++側で実装するべきたとわかります。
2019-12-03 12:18:09.617 17648-17648/cc.openframeworks.HelloColorToggle E/AndroidRuntime: FATAL EXCEPTION: main
Process: cc.openframeworks.HelloColorToggle, PID: 17648
java.lang.UnsatisfiedLinkError: No implementation found for void cc.openframeworks.HelloColorToggle.OFActivity.hoge() (tried Java_cc_openframeworks_HelloColorToggle_OFActivity_hoge and Java_cc_openframeworks_HelloColorToggle_OFActivity_hoge__)
at cc.openframeworks.HelloColorToggle.OFActivity.hoge(Native Method)
at cc.openframeworks.HelloColorToggle.OFActivity$1.onClick(OFActivity.java:27)
at android.view.View.performClick(View.java:6294)
at android.view.View$PerformClick.run(View.java:24770)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
OFActivity.java
を次のように編集すると、ボタンをクリックすると背景が白く塗りつぶされるアプリが完成します。
public class OFActivity extends cc.openframeworks.OFActivity{
public native void click();
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
click();
}
});
}
背景の色を変更
ボタンのクリックでもうやってしまいましたが、背景色を変えるのは、ofBackground(int 0-255,int 0-255,int 0-255);
ですね。openFrameworks
の構造はofApp::setup()
はアプリが立ち上がった後の1回のみ呼ばれて、画面描画ごとにofApp::update()
ofApp::draw()
が呼ばれます。それを踏まえて、ofApp.cpp
を次のように編集します。
#include "ofApp.h"
static bool toggle;
extern "C"
JNIEXPORT void JNICALL
Java_cc_openframeworks_HelloColorToggle_OFActivity_click(JNIEnv* env,jobject thiz){
toggle = !toggle;
}
//--------------------------------------------------------------
void ofApp::setup(){
toggle = true;
ofBackground(255,0,0);
}
//--------------------------------------------------------------
void ofApp::update(){
}
//--------------------------------------------------------------
void ofApp::draw(){
if(toggle)
ofBackground(255,0,0);
else
ofBackground(0,0,255);
}
ボタンはmain_layout.xml
のボタン要素にandroid:layout_centerInParent="true"
を加えるだけで真ん中にきます。
Cordova
- 簡単にクロスプラットフォーム
- HTML/JS/CSSが分かれば、アプリを作れる
- Webの資産を使い回せる
今度は、Nativeから離れて、WebViewを元に作られているCordovaというフレームワークを使ってアプリを作ります。
1.Cordovaをインストールします。npm
が入っていることが前提です。ターミナルに次のコマンドを打ち込むだけでインストールは終わりです。
$npm install -g cordova
2.アプリのプロジェクトを作成します。事前にアプリのプロジェクトを生成するディレクトリへ移動しておきます。
$cordova create HelloToggleColor
プロジェクトディレクトリを見てみると、index.html
や index.js
が見受けられるので、どうやらこれらを編集して開発をすすめていけば良さそうな気がします。
HelloToggleColor
├── config.xml
├── hooks
│ └── README.md
├── package.json
├── platforms
├── plugins
└── www
├── css
│ └── index.css
├── img
│ └── logo.png
├── index.html
└── js
└── index.js
3.プロジェクトディレクトリに移動して、Platformを追加します。今回は、Androidアプリなのでandroid
を追加します。
$cd HelloToggleColor
$cordova platform add android
4.アプリを起動します。実機をつないでいれば自動でインストールされた立ち上がります。
$cordova run android
え、やば、簡単すぎんか...?
$adb shell dumpsys activity top
を使うとViewの構成とかが見れます。なので、Cordovaアプリがどうなっているかというと、SystemWebView
で構成されていることがわかります。
TASK io.cordova.hellocordova id=140 userId=0
ACTIVITY io.cordova.hellocordova/.MainActivity 17398e4 pid=24311
View Hierarchy:
DecorView@8e127c8[MainActivity]
android.widget.LinearLayout{ead1061 V.E...... ........ 0,0-1080,1794}
android.view.ViewStub{6b3b786 G.E...... ......I. 0,0-0,0 #1020187 android:id/action_mode_bar_stub}
android.widget.FrameLayout{4142847 V.E...... ........ 0,63-1080,1794 #1020002 android:id/content}
org.apache.cordova.engine.SystemWebView{ec4f874 VFEDH.C.. .F...... 0,0-1080,1731 #64}
android.view.View{b49519d V.ED..... ........ 0,1794-1080,1920 #1020030 android:id/navigationBarBackground}
android.view.View{183c612 V.ED..... ........ 0,0-1080,63 #102002f android:id/statusBarBackground}
ここからの気持ちはAndroidアプリ開発ではなくモバイルWeb開発なのか...と思いながら目的のアプリを作っていきます。
ボタンを実装
HTML
なら <button>
タグを追加するだけでbuttonを置けます...
<body>
<button type=button>Button</button>
<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" src="js/index.js"></script>
</body>
Androidなのに味気が無いので、マテリアルデザインっぽいボタンしていきます。
今はサポートされて無いですが、ボタンだけなら扱いやすいので、Material Design Liteを導入します。プロジェクトのディレクトリに移動して次のコマンドを実行してローカルに落としてきます。
$npm install material-design-lite --save
CordovaのHTMLからは、基本的には、path/to/project/www
のフォルダしかアクセスできないようなので、./node_modules/から ./wwwへcssとjsファイルをコピーします。
cp ./node_modules/material-design-lite/material.min.css ./www/css/
cp ./node_modules/material-design-lite/material.min.js ./www/js/
あとは、ヘッダーに次のコードを追加して、HTMLでの読み込みは完了です。
<link rel="stylesheet" href="./css/material.min.css">
<script src="./js/material.min.js"></script>
ボタンを中央に置きたいので index.css
に次のコードを追加して、index.html
のButton要素を囲います。
.wrap2 {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
<div class="wrap2">
<button class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect">
Button
</button>
</div>
NativeのUIとほとんど変わらないように見えます
ボタンがクリックしたことを検知
これも楽ですね。Button
タグにonClickプロパティをつけるか、JavascriptでaddEventListener
を使うかです。
今回はJavascriptを書きます。js/index.js
を開いてコードをサクッと実装します。ここでのconsole.log()
は、Google ChromeのInspectorにログを出力できます。GoogleChromeのアドレスバーにchrome://inspect/
と入力してページを開き、端末のinspect
を押すと、実行中の画面とともに、consoleが表示されます。
var app = {
// Application Constructor
initialize: function() {
document.addEventListener('deviceready', this.onDeviceReady.bind(this), false);
document.getElementById('button1').addEventListener('click',()=>{
console.log('click');
});
},
背景の色を変更
あとは背景の色を変えるだけです。CSSのbackgtoundプロパティをいじっていきます。js/index.js
を開いてコードを加えます。
var app = {
toggle: false,
// Application Constructor
initialize: function() {
document.addEventListener('deviceready', this.onDeviceReady.bind(this), false);
document.getElementById('back').style.background = 'red';
document.getElementById('button1').addEventListener('click',()=>{
document.getElementById('back').style.background = (this.toggle)? 'red' : 'blue';
this.toggle = !this.toggle;
console.log('click');
});
},
ボタンに設定されているアルファ値が低いので後ろの色が反映されてしまいましたがCordova
でも同じアプリができました。
React Native
Getting Startedのページに行くと、React Nativeの開発ツールとしてExpo CLI
を使うのとReact Native CLI
を使うのとがある。
まずは、Expo CLI
から試してみることにする。
1.まず、Expo CLI
をインストール。
$npm install -g expo-cli
2.プロジェクトを作るディレクトリに移動して、プロジェクトを生成
$expo init HelloToggleColor
Node.jsのバージョンはサポートしているものに上げる必要がある。
ERROR: Node.js version 10.8.0 is no longer supported.
expo-cli supports following Node.js versions:
* >=8.9.0 <9.0.0 (Maintenance LTS)
* >=10.13.0 <11.0.0 (Active LTS)
* >=12.0.0 (Current Release)
3.どのテンプレートで作成するか聞かれるので、blank
を選びます。
? Choose a template: (Use arrow keys)
----- Managed workflow -----
❯ blank a minimal app as clean as an empty canvas
blank (TypeScript) same as blank but with TypeScript configuration
tabs several example screens and tabs using react-navigation
----- Bare workflow -----
minimal bare and minimal, just the essentials to get you started
minimal (TypeScript) same as minimal but with TypeScript configuration
4.プロジェクトが生成されたら、まずは端末で実行します。プロジェクトのディレクトリに移動して、npm start
します。
cd HelloToggleColor
npm start
すると、サーバーが立ち上がり、ブラウザに開発ツール、ターミナルにQRコードが表示されます。
5.実行する端末で Expo というアプリをインストールしておきます。
6.インストールされたExpoアプリでQRコードをScanすると、JavaScriptをバンドルして、アプリが表示されます。
ターミナルでは次のコマンドが使えるので、a
を押すとターミナルからアプリを起動できます。
› Press a to run on Android device/emulator, or i to run on iOS simulator, or w to run on web.
› Press c to show info on connecting new devices.
› Press d to open DevTools in the default web browser.
› Press shift-d to disable automatically opening DevTools at startup.
› Press e to send an app link with email.
› Press p to toggle production mode. (current mode: development)
› Press r to restart bundler, or shift-r to restart and clear cache.
› Press s to sign in.
ボタンを実装
それでは、ボタンを実装していきます。アプリのメインが画面を作成しているところは App.js
なので、これを編集していきます。
HTML
のノリで <button>
ダグを置いてみます。
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
export default function App() {
return (
<View style={styles.container}>
<Text>Open up App.js to start working on your app!</Text>
<button><Text>Button</Text></button>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
よくわからないときは公式ドキュメントを見てみます。公式ドキュメントのボタンの項目のサンプルコードを参考に書き直します。
import React from 'react';
import { StyleSheet, Text, Button,View } from 'react-native';
export default function App() {
return (
<View style={styles.container}>
<Button
onPress={this.onPressLearnMore}
title="Learn More"
color="#858585"
accessibilityLabel="Learn more about this purple button"
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
なんとかボタンが表示されました。
ボタンがクリックしたことを検知
ボタンが押されたらonPressにセットしたメソッドが呼ばれるのでメソッドを実装します。
import React from 'react';
import { StyleSheet, Text, Button, View, Alert} from 'react-native';
export default function App() {
onPressLearnMore = ()=>{
Alert.alert('pressed button')
};
return (
<View style={styles.container}>
<Button
onPress={this.onPressLearnMore}
title="Learn More"
color="#858585"
accessibilityLabel="Learn more about this purple button"
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
ボタンを押したらダイアログが表示されるようにしました。
背景の色を変更
背景の色を変えます。画面いっぱいに構成されているView
コンポーネントのスタイルを変更するのが良さそうです。まずは、styleに背景が赤になるスタイルと青になるスタイルを加えます。
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
red: {
flex: 1,
backgroundColor: '#f00',
alignItems: 'center',
justifyContent: 'center',
},
blue: {
flex: 1,
backgroundColor: '#00f',
alignItems: 'center',
justifyContent: 'center',
},
});
ボタンが押されたら、setState
を呼び出して、Viewに割り当てるスタイルを変えます。さっきは、export default fuction
でしたが、export default class
に変えておきます。
export default class App extends React.Component{
constructor(props) {
super(props);
this.state = {
styleVal : styles.container,
toggle : true
}
}
onPressLearnMore = () => {
this.setState(
{
styleVal : (this.state.toggle)?styles.red : styles.blue,
toggle : !this.state.toggle
}
);
}
render = () => {
return (
<View style={this.state.styleVal}>
<Button
onPress={this.onPressLearnMore}
title="Learn More"
color="#858585"
accessibilityLabel="Learn more about this purple button"
/>
</View>
);
}
};
これでなんとかReact Native
でもできました!
Visual Studio
まずはインストールから初めていきます。(Windowsの方はすみません
1.MSのVisual Studioのページにいき、インストーラーをダウンロードします。今回は、Visual Studio for Mac
をダウンロードしてきます
2.ダウンロードしたdmgを開いてインストーラーを起動します。インストールするコンポーネントのAndroidにチェックがついていることを確認してインストール
を押します。
3.Visual Studioを開き、+新規
でプロジェクトを新しくプロジェクトを作成します
Visual Studioで作成できるAndroidアプリはXamarin.Forms
とXamarin.Android
があるので、まずはXamarin.Forms
でアプリを作っていきます。
Xamarin.Forms
1.マルチプラットフォームで、空白フォームのアプリ
を選択します。
2.必要な情報を入力します。
3.プロジェクトの保存先などを指定して作成します。
とりあえず実機で実行してみます。この部分で実行するOSと端末を設定します。
こんな感じで Welcome to Xamarin.Forms!
と画面中央に設置されます。
ボタンを実装
まずは、ボタンを設置していきます。最初の画面はMainPage.xmal
で構成されているので、編集していきます。
Android Studioで読み書きするactivity_main.xml
に似ていますね。要素だったところを要素に変えるだけです。IDEの右側にコンポーネントのリストがあるので実際ドラッグアンドドロップで要素を置けるの楽ですね。
<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:d="http://xamarin.com/schemas/2014/forms/design" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="HelloToggleColor.MainPage">
<StackLayout>
<!-- Place new controls here -->
<Button Text="Button" HorizontalOptions="Center" VerticalOptions="CenterAndExpand"/>
</StackLayout>
</ContentPage>
ボタンがクリックしたことを検知
次はボタンクリックの検知です。MainPage.xmal
に対応して、MainPage.xmal.cs
があるので、おそらくイベント処理的なものはここに書いて行けば良さそうです。まずはボタンをクリックしたら呼ばれる関数を実装します。C#でのログ出力はSystem.Console.WriteLine
を使います。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace HelloToggleColor
{
// Learn more about making custom code visible in the Xamarin.Forms previewer
// by visiting https://aka.ms/xamarinforms-previewer
[DesignTimeVisible(false)]
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
void OnButtonClicked(object sender, EventArgs args)
{
Console.WriteLine("DEBUG - Button Clicked!");
}
}
}
ボタンにはどの関数を呼び出せばいいかをClicked
プロパティにセットしておきます。
<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:d="http://xamarin.com/schemas/2014/forms/design" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="HelloToggleColor.MainPage">
<StackLayout>
<!-- Place new controls here -->
<Button Text="Button"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
Clicked="OnButtonClicked" />
</StackLayout>
</ContentPage>
これでボタンを押すとログにDEBUG - Button Clicked!
とでるアプリができました。
背景の色を変更
MainPage.xmal.cs
でロジックを書いて、MainPage.xmal
でViewを書くことがわかったのであとは簡単です。
MainPage.xmal.cs
から参照できるように、MainPage.xmal
の要素にNameプロパティを設定します。
<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:d="http://xamarin.com/schemas/2014/forms/design" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="HelloToggleColor.MainPage">
<StackLayout x:Name="layout">
<!-- Place new controls here -->
<Button Text="Button"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
Clicked="OnButtonClicked" />
</StackLayout>
</ContentPage>
Nameプロパティにセットした名前で直接アクセスできるので、BackgroundColor
プロパティに色を代入するだけでおしまい。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace HelloToggleColor
{
// Learn more about making custom code visible in the Xamarin.Forms previewer
// by visiting https://aka.ms/xamarinforms-previewer
[DesignTimeVisible(false)]
public partial class MainPage : ContentPage
{
private bool toggle = true;
public MainPage()
{
InitializeComponent();
}
void OnButtonClicked(object sender, EventArgs args)
{
Console.WriteLine("DEBUG - Button Clicked!");
layout.BackgroundColor = toggle ? Color.Red : Color.Blue;
toggle = !toggle;
}
}
}
はぇ〜、Android Studio + C#
みたいな感じで開発しやすい。
Xamarin.Android
Xamarin.Android
も試していきます。
1.Visual Studioを開いて、新規で空のネイティブアプリ(iOS、Android)
を選択します。
2.必要な情報を入力します。
3.これでプロジェクトが完成しました。
テンプレートなのに既にボタンもボタンイベントへの反応も実装されている!?
ボタンを実装
既にボタンは実装されますが、どこでボタンが実装されているのかは確認します。この画面のレイアウトはResources/layout/Main.axml
に定義されています。中ををみると、普通にAndroidのレイアウトファイルになっているいます。なので、ボタンが中央にある感じに直しちゃいます。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button android:id="@+id/myButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="@string/hello" />
</RelativeLayout>
ボタンがクリックしたことを検知
ボタンがクリックしたことを受け取るロジックは、MainActivity.cs
に書かれています。Java
のメソッド名のままC#
に持ってきた感じがすごいします。interfaceの代わりにdelegate
が使われてるのが特徴的です。
using Android.App;
using Android.Widget;
using Android.OS;
namespace HelloToggleColor.Droid
{
[Activity(Label = "HelloToggleColor", MainLauncher = true, Icon = "@mipmap/icon")]
public class MainActivity : Activity
{
int count = 1;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
// Get our button from the layout resource,
// and attach an event to it
Button button = FindViewById<Button>(Resource.Id.myButton);
button.Click += delegate {
Android.Util.Log.Info("Xamarine","onClick");
};
}
}
}
背景の色を変更
delegate
のあたりをいじって従来のAndroid
開発のノリでサクッと書きます。今回はボタンの親Viewが背景のRelativeLayout
だとわかっているので、親を取得してキャストして背景色を変えます。
using Android.App;
using Android.Widget;
using Android.OS;
namespace HelloToggleColor.Droid
{
[Activity(Label = "HelloToggleColor", MainLauncher = true, Icon = "@mipmap/icon")]
public class MainActivity : Activity
{
private bool toggle = true;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
// Get our button from the layout resource,
// and attach an event to it
Button button = FindViewById<Button>(Resource.Id.myButton);
button.Click += delegate {
Android.Util.Log.Info("Xamarine","onClick");
((RelativeLayout)button.Parent).SetBackgroundColor((toggle)?Android.Graphics.Color.Red : Android.Graphics.Color.Blue);
toggle = !toggle;
};
}
}
}
Android Studio + C#
な感じで開発しやすかった。
最後に
Web系の知識と、Android Studioでの開発がわかってると、色んなフレームワークをサクッと使っていける感じはありました。
今回試したフレームワークでも、普段より楽な点や面倒な点もあるので、やっぱり作るものに応じてという感じがします。
フレームワークによってはHello World
アプリの実行まで10分もかからないものもあるので、Eclipseを使って開発していた頃とは違うし、めちゃくちゃ敷居は低くなったと思います。なので、Androidアプリを作りたいなぁとか思ってる人は、どれかフレームワークでも選んでサクッと作り始めるのがいいです。
もうちょっとフレームワークの利点・欠点の説明とか、フレームワークの仕組みとかにも触れて話していきたいので、随時更新していくのでよろしくお願いします。