どうも、Namazuです。
この記事はAndroid講習会の資料となります。
なお、この講習会でソースコードの資料としてgitLabの方にAndroidLectureApp(本記事のLectureApp)とGravityGameが上がっています。
そちらもお役立てください。
1.講習会担当者紹介
- Namazu
本講習会では前半パートを担当。
Androidアプリの開発に初めて触れたのが3月中旬
Androidを触ってみた感想:API充実しすぎでは?
経歴:4月の頭に速攻で作ったアプリ持ってインターンチャレンジしたところ見事に不採用。#それはそう - sobaya007
本講習会では後半パートを担当。
traQのAndroid版の開発とかしてる。
今回メインで教えるはずだったが車校の期限がピンチなので途中から参戦。
プロ?なので質問には†なんでも†答えてくれるはず
2.Android開発の利点
- Javaで開発できる
やや特殊な形式ではありますが、やはりJavaで開発できるのは大きな利点です。
Javaの書き方、オブジェクト指向が分かっていれば十分開発できます。 - APIが充実している
要するに、「○○がしたい」と思ったときにそれ専用の道具が用意されていることが多いということです。画面表示や音楽の再生なども簡単に出来てしまいます。道理で「中学生から始めるAndroidアプリ開発」みたいな本がたくさんあるわけですね。 - 様々なタイプのセンサーが使える
Androidには、デフォルトで加速度、ジャイロ、照度、磁界、圧力、近接、温度、湿度などを取得するセンサーがついています。これらは適切な関数を呼べば使うことができるので、PCでは出来ないようなことも実現可能です。
3.開発の準備 <Android Studio導入、新規プロジェクト作成>
Android Studioの導入
以下のページからAndroid StudioをDLしてください。
http://developer.android.com/intl/ja/sdk/index.html
Android Studioは現在公式で推奨されているAndroidアプリの開発環境です。
以前はEclipseを用いた開発も行われていましたが、今はこちらが主流のようです。
今回はAndroid Studioを用いて開発を行います。
起動するとJDKのインストールが求められますので青文字のリンクから飛んで
自分のPCに合うバージョンのJDKをDLしてください。
一度実行したのちAndroidStudioの方で[Next]を押して進めてください。
新規プロジェクト作成
[File]→[New]→[New Project]を選択し、「LectureApp」と名前を付けてください。
その後[Next]、バージョンはAndroid 4.2(API Level 17)にしてください。
その後[Next]、テンプレートは[Empty Activity]を選択してください。次のページに進み、そのまま[Finish]を押すとプロジェクトの構築が始まります。
4.通過儀礼~Hello,World~ <プロジェクトの実行>
それではAndroidをお持ちの方は早速繋いで、そうでない方も▶をクリックし、実行してみましょう。[Device Chooser]のウィンドウが開きます。
ここでAndroidを持っている方は上のボックスに自分のAndroidが読み込まれて表示されると思います。それを選択して[OK]を押してください。
お持ちでない方は下の[Launch Emulator]を選択し、[none]と書かれた右側の[…]から新しく仮装デバイスを設定します。
[Create Virtual Device]
→[Phone][Galaxy Nexus , xxhdpi]→[Next]
→[Show downloadable system images][JellyBean , API Level17 , ABI x86]→[Next]
→[Finish]
と設定してください。設定出来たらAndroid Virtual Device Managerを閉じて、[Device Chooser]で設定した仮装デバイスが選択されていることを確認して[OK]を押してください。
実は、デフォルトでHello,Worldが書かれているので実行すると「Hello,World」と表示されます。
5.文字とレイアウト <TextView,レイアウトファイル>
まずはAndroidアプリ開発の基本としてTextViewに触れましょう。
これは文字の表示をするためのクラスです。ただのString型とは違います。
activity_main.xmlを開くと、中に以下のような記述があると思います。
activity_main.xml
<TextView
android:text="Hello,World"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
これこそ、Hello,Worldを表示させていたものの正体です。
では、早速少し変えてみましょう。以下のように書き換えてください。
activity_main.xml
<TextView
android:id="@+id/text"
android:text="Hello World!"
android:textSize="40sp"
android:textColor="#ff0088"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
実行すると、文字が大きくなり色も赤くなったと思います。
テキストのデフォルトの色やサイズなどを設定したいときはこのように記述してください。
さて、ここで"デフォルト"と言ったのには理由がありまして、それはプログラムの方で書き換えることが出来るからです。
MainActivity.javaの該当部分を以下のように書き換えてください。
MainActivity.java
public class MainActivity extends AppCompatActivity {
private TextView text;//①
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = (TextView)findViewById(R.id.text);//②
text.setText("Po");//③
}
}
実行してみると、テキストの内容が"Po"になっています。
簡単に流れを説明しますと、
①:TextViewクラスの参照型変数textを作成
②:TextViewクラスのインスタンス:text(activity_mainで作成したもの)を代入
③:textの表示内容を"Po"に変更
という感じになります。
ちなみに、ですが以下のように記述するとjavaだけでレイアウトを記述することもできます。
MainActivity.java
public class MainActivity extends AppCompatActivity {
private TextView text;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RelativeLayout r = new RelativeLayout(this);
text = new TextView(this);
text.setText("Po");
text.setTextSize(40);
text.setTextColor(Color.RED);
r.addView(text);
setContentView(r);
}
}
こちらの方が分かりやすいという方もいらっしゃいますが、プロジェクトの規模が大きくなってくるとプログラムの内容とレイアウトの内容を別のファイルに分けた方が分かりやすくなります。
(レイアウトを全てjavaに書いてしまったのもインターンチャレンジ失敗の一つの要因だったようです)
今回の講習会ではそこまで大事ではありませんが、Androidで何かを開発しようとした時に必要になってくると思うので一応頭にとどめておいてください。
検索用キーワード:
TextView ImageView Button CheckBox Spinner...
RelativeLayout LinearLayout TableLayout...
5.自分だけのViewを作ろう
ここでは、クラス・コンストラクタ・継承の知識が少し必要になります。
<簡単に説明>
- クラス
データの集合体の”鋳型”です。
メソッドも持っていて、外から内包するデータを操作することができます。
クラスをもとに作られる、データの集合体をインスタンスと呼びます。
Droid droid = new Droid();
のようにすると、参照型変数droidにDroidクラスの新しいインスタンスが入ります。 - コンストラクタ
そのクラスのオブジェクトを作成したとき、そのオブジェクトの初期化処理をするメソッドです。
コンストラクタの名前はクラスの名前と同じするというルールがあります。
コンストラクタの記述をしなかった場合、オブジェクトには何も処理が施されません。 - 継承
何かの元のクラス(親クラス)があり、それを拡張したクラスを作りたい時に使います。
親クラスのメソッドをOverrideしたり新たにメソッドを追加したりすることができます。
親クラスは一つだけでなく、複数の親クラスを継承することも可能です。
さて、新しくViewを作るにはまずViewのクラスを用意します。
画面左のProject ViewのMainActivityを右クリックして、[New]→[Java Class]を選択し、名前を「LectureView.java」にします。
まず、該当部分に以下のコードを記述してください。
LectureView.java
public class LectureView extends View {
public LectureView(Context context) {
super(context);
}
@Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LectureView lectureView = new LectureView(this);
setContentView(lectureView);
}
}
これは新しいViewを作成するときの基本形です。
コンストラクタの作成とonDrawメソッドのオーバーライドをこのように行います。
ともに、superクラス(Viewクラス)のメソッドを始めに実行することでViewとしての体裁と整えてくれます。
onDrawメソッドは文字通り描画処理用のメソッドです。
onDrawメソッドの最後にinvalidate();と記述すると毎フレーム実行し直してくれるので繰り返し処理をここに書くこともできます。
さて、今回はGravityGameに入る前に何かしら動くものを作ってみましょう。
まずは新しくMainActivityと同じフォルダにLissajous.javaを作成します。
Lissajous.java
public class Lissajous {
private final Paint paint = new Paint();
private final int range;
private final int circleRange;
public Lissajous(){
this.range = 200;
this.circleRange = 20;
}
public void draw(Canvas canvas,double arg){
int x = canvas.getWidth()/2;
int y = canvas.getHeight()/2;
double centerX = x + range * Math.cos(4*arg);
double centerY = y + range * Math.sin(5*arg);
paint.setColor(Color.GREEN);
canvas.drawCircle((float)centerX,(float)centerY,circleRange,paint);
}
}
これはリサージュ曲線の軌道にそって動く緑色の円のオブジェクトのクラスです。
クラスの中にオブジェクトの描画処理も書いておきます。
今回は分離しましたが、オブジェクトが数種類ある場合などはクラス内に移動処理も書いておくと便利です。
さて、それではこれを描画してみましょう。
LectureView.java
public class LectureView extends View {
private Lissajous lissajous;
private double arg = 0.0;
public LectureView(Context context) {
super(context);
lissajous = new Lissajous();
}
@Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
lissajous.draw(canvas,arg);
arg += 0.01;
invalidate();
}
}
このように、LectureViewでLissajousのインスタンスを作成し、描画処理を書くことでインスタンスの描画が画面に反映されます。
ついでにオブジェクトの扱い方の練習のためにインスタンスを複数作成してみましょう。
LectureView.java
public class LectureView extends View {
private Lissajous[] lissajous = new Lissajous[10];
private double arg = 0.0;
public LectureView(Context context) {
super(context);
for(int i=0;i<10;i++){
lissajous[i] = new Lissajous();
}
}
@Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
for(int i=0;i<10;i++){
lissajous[i].draw(canvas, arg+i*Math.PI/5);
}
arg += 0.01;
invalidate();
}
}
Lissajousの配列を用いることで10個のインスタンスを作成しています。
このように、クラスを一つ作っておくだけで複数のオブジェクトを簡単に作ることができます。
6.SurfaceViewを使おう
さて、これまでViewを継承してLectureViewを作ってきましたが、実はこれには問題があります。
Viewは処理をメインスレッドで実行してしまうので、重い描画処理をしようとした時にカクカクしてしまうのです。
そこで、別スレッドで描画処理を行うような仕組みを活用したいと思います。
それがSurfaceViewです。
LectureViewを以下のように書き換えてください。コピペで構いません。
LectureView.java
public class LectureView extends SurfaceView implements SurfaceHolder.Callback{
private final long DRAW_INTERVAL = 1000 / 100;
private class DrawThread extends Thread {
private final AtomicBoolean isFinished = new AtomicBoolean(false);
public void finish(){
isFinished.set(true);
}
@Override
public void run() {
SurfaceHolder holder = getHolder();
while(!isFinished.get()) {
if(holder.isCreating()){
continue;
}
Canvas canvas = holder.lockCanvas();
if(canvas == null){
continue;
}
drawView(canvas);
holder.unlockCanvasAndPost(canvas);
synchronized (this){
try {
wait(DRAW_INTERVAL);
} catch (InterruptedException e) {
}
}
}
}
}
private DrawThread drawThread;
public void startDrawThread(){
stopDrawThread();
drawThread = new DrawThread();
drawThread.start();
}
public boolean stopDrawThread() {
if(drawThread == null){
return false;
}
drawThread.finish();
drawThread = null;
return true;
}
@Override
public void surfaceCreated(SurfaceHolder holder){
}
@Override
public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){
startDrawThread();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder){
stopDrawThread();
}
private Lissajous[] lissajous = new Lissajous[10];
private double arg = 0.0;
public LectureView(Context context) {
super(context);
for(int i=0;i<10;i++){
lissajous[i] = new Lissajous();
}
getHolder().addCallback(this);
}
public void drawView(Canvas canvas){
canvas.drawColor(Color.WHITE);
for(int i=0;i<10;i++){
lissajous[i].draw(canvas, arg+i*Math.PI/5);
}
arg += 0.01;
}
}
変更された主な点は以下の通りです。
- 継承するクラスがViewからSurfaceViewに変わった。
- コンストラクタの前に記述が増えた。いっぱい。
- コンストラクタの最後にgetHolder().addCallback(this);の記述が加わった。
- onDrawメソッドの中身はそのまま、名前がdrawViewになり@Overrideとスーパークラスのコンストラクタの記述が消えた。
- drawViewの中からinvalidate();の記述が消えた。
要するにおまじないだと思ってください。
ゲームを作成するときは基本的にSurfaceViewが使われます。
自分で何か作ろうとした場合もほとんど上記のコピペで構いません。
追記:
gitの方にLectureAppを拡張したものを上げておきました。
defaultはここで作成した最後の状態です。
係数の変更(タッチイベントの取得)、軌道の表示/非表示の切り替え等ができるようになっています。