Android Lecture #04 @PRO&BSC Inc.
-
Upload
yuki-higuchi -
Category
Education
-
view
6.075 -
download
0
description
Transcript of Android Lecture #04 @PRO&BSC Inc.
本日の内容
• アニメーション
–User eXperience を高めましょう
•楽しい感じに
•処理待ちのイライラを軽減
•各種テスト
–単体/結合/安定化試験を自動化し、アプリの品質を高めましょう
2
アニメーション
クラス名 概要
AlphaAnimation フェードイン/アウト
RotateAnimation 回転
ScaleAnimation 拡大/縮小
TranslateAnimation 移動
AnimationSet アニメーションの合成
4
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center"> <Button android:layout_width="wrap_content" android:padding="20dp" android:id="@+id/button" android:layout_height="wrap_content" android:text="Hello World !!" /> </LinearLayout>
main.xml
サンプルのレイアウト
AlphaAnimation: フェードイン/アウト
5
public class MainActivity extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main);
Button btn = (Button)findViewById(R.id.button); btn.setOnClickListener(new OnClickListener() { public void onClick(View v) {
AlphaAnimation alpha = new AlphaAnimation(1.0f, 0.0f); alpha.setDuration(3000); v.startAnimation(alpha);
} }); } }
6
RotateAnimation: 回転
RotateAnimation rotate = new RotateAnimation( 0, 360, v.getWidth() / 2, v.getHeight() / 2); rotate.setDuration(3000); v.startAnimation(rotate);
ScaleAnimation: 拡大/縮小
ScaleAnimation scale = new ScaleAnimation( 1, 2, 1, 2, v.getWidth() / 2, v.getHeight() / 2); scale.setDuration(3000); scale.setInterpolator(new CycleInterpolator(3)); v.startAnimation(scale);
7
TranslateAnimation: 移動
TranslateAnimation translate = new TranslateAnimation( -10, 10, -10, 10); translate.setDuration(3000); translate.setInterpolator(new CycleInterpolator(3)); v.startAnimation(translate);
AnimationSet: アニメーションの合成
AnimationSet set = new AnimationSet(true); set.addAnimation(alpha); set.addAnimation(rotate); set.addAnimation(scale); v.startAnimation(set);
これまでのアニメーションの定義部分( startAnimation以外)を残しておき、それらの定義の下に書いてみましょう
アニメーションを利用したビューア • APK
– http://goo.gl/LLYXP
• ソース – http://goo.gl/oWcyG
• ソースのインポート方法
– Eclipse の [ファイル(F)] – [インポート(I)...] から「インポート」ダイアログを開く
– [一般] - [既存プロジェクトをワークスペースへ] を選び、「ルート・ディレクトリーの選択」にてインポート対象フォルダを選択して取り込み完了 8
Tips: メニューの追加方法
9
/** メニューID (バージョン) */ static final int MENU_ID_MENU1 = (Menu.FIRST + 1);
public boolean onCreateOptionsMenu(Menu menu) { // メニュー (バージョン) の追加 MenuItem menuVersion = menu.add( Menu.NONE, MENU_ID_MENU1, Menu.NONE, "バージョン"); menuVersion.setIcon(android.R.drawable.ic_menu_info_details);
return super.onCreateOptionsMenu(menu); }
public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case MENU_ID_MENU1: // バージョン表示 showDialog(DIALOG_ID_VERSION); break;
default: return super.onOptionsItemSelected(item); }
return true; }
Tips: ハードウェアキーの補足
10
public boolean onKeyDown(int keyCode, KeyEvent event) { super.onKeyDown(keyCode, event);
switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_DOWN: case KeyEvent.KEYCODE_DPAD_DOWN: case KeyEvent.KEYCODE_DPAD_RIGHT: case KeyEvent.KEYCODE_SPACE: case KeyEvent.KEYCODE_ENTER:
... (省略) ...
return true;
case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_DPAD_UP: case KeyEvent.KEYCODE_DPAD_LEFT: case KeyEvent.KEYCODE_DEL:
... (省略) ...
return true; }
return false; }
Tips: 回転後も消えないダイアログ
11
/** ダイアログID (バージョン) */ static final int DIALOG_ID_VERSION = 0;
protected Dialog onCreateDialog(int id) { Dialog dialog;
switch (id) { case DIALOG_ID_VERSION: AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setIcon(R.drawable.icon); builder.setTitle("イメージビューア"); builder.setMessage("version 3.0"); builder.setPositiveButton("OK", null); dialog = builder.create(); break;
default: dialog = null; }
return dialog; }
... (省略) ... showDialog(DIALOG_ID_VERSION);
onCreateDialogを経由して表示することでダイアログのライフサイクルをActivityに管理してもらうことになります。 このため、正確に書くと「(画面の)回転後も消えない」ではなく、「(画面の)回転後にActivityの再生成に伴ってダイアログも再生成される」が正しくなります。
Tips: FrameLayout の使い方
12
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@android:color/white" android:padding="10dp" >
<ImageView android:id="@+id/imgView" android:layout_width="fill_parent" android:layout_height="fill_parent" android:src="@drawable/img01" />
<Button android:id="@+id/btnLeft" android:layout_width="100dp" android:layout_height="wrap_content"
android:layout_gravity="bottom|left"
android:text="←" android:textSize="24sp" />
... (省略) ...
どこにアンカーを置くかがポイントになります
アンカーを明示しないと左上に配置されます
各種テスト
ツール 概要
junit.framework.TestCase ①単体試験 (主な対象: ロジック)
android.test.ActivityInstrumentationTestCase2 ②結合試験 (主な対象: UI)
Monkey Runner ③安定化試験 (主な対象: メモリリーク)
テストプロジェクトの追加
• Androidプロジェクト作成時に追加できます
14
サンプル(SimpleCalc)の用意①
15
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android= "http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:orientation="horizontal" > <EditText android:id="@+id/editNum1" android:layout_width="68dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:gravity="center" android:inputType="number" /> <LinearLayout android:layout_width="48dp" android:layout_height="wrap_content" android:orientation="vertical" > <Button android:id="@+id/btnAdd" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="+" android:textSize="22sp" /> <Button android:id="@+id/btnSub" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="-" android:textSize="22sp" />
<Button android:id="@+id/btnMul" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="×" android:textSize="22sp" /> <Button android:id="@+id/btnDiv" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="÷" android:textSize="22sp" /> </LinearLayout> <EditText android:id="@+id/editNum2" android:layout_width="68dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:gravity="center" android:inputType="number" /> <TextView android:layout_width="48dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:gravity="center" android:text="=" android:textSize="22sp" /> <EditText android:id="@+id/editAnswer" android:layout_width="68dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:editable="false" android:gravity="center" /> </LinearLayout> ・res/layout/main.xml
16
サンプル(SimpleCalc)の用意② package jp.probsc.simplecalc;
/** *計算クラス */ public class Calculator { /** 足し算 */ public static int Add(int a, int b) { return a + b; }
/** 引き算 */ public static int Sub(int a, int b) { return a - b; }
/** 掛け算 */ public static int Mul(int a, int b) { return a * b; }
/** 割り算 */ public static double Div(int a, int b) { return a / b; } }
・src/.../Calculator.java
package jp.probsc.simplecalc;
import android.app.Activity; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText;
/** * シンプル電卓クラス */ public class MainActivity extends Activity { /** * onCreate コールバックメソッド */ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main);
// ウィジェットの初期化 init(); }
/** * ウィジェットの初期化 */ private void init() { final EditText num1 = (EditText)findViewById(R.id.editNum1); num1.setSelectAllOnFocus(true);
final EditText num2 = (EditText)findViewById(R.id.editNum2); num2.setSelectAllOnFocus(true);
final EditText numAnswer = (EditText)findViewById(R.id.editAnswer);
Button btnAdd = (Button)findViewById(R.id.btnAdd); btnAdd.setOnClickListener(new OnClickListener() { public void onClick(View v) { int ret = Calculator.Add( escape(num1), escape(num2)); numAnswer.setText(String.valueOf(ret)); } }); }
/** * EditText をエスケープして整数値を返す * * @param target エスケープ対象の EditText ウィジェット * @return 整数値 */ public static int escape(EditText target) { try { return Integer.parseInt(target.getText().toString()); } catch (Exception ex) { target.setText("0"); return 0; } } } 17
サンプル(SimpleCalc)の用意③
・src/.../MainActivity.java
サンプル(SimpleCalc)の完成 • ソース
– サンプルプロジェクト
• http://goo.gl/SQ0LW
– テストプロジェクト
• http://goo.gl/KM1bW
• この四則演算を行うアプリのテストを行います。試験は下記の4種類です – ① 単体試験
– ② 結合試験
– ③ 安定化試験 18
単体試験の準備
• ロジックを試験します
– テストプロジェクトに「CalculatorTest.java」を追加
19
①単体試験
package jp.probsc.simplecalc.test; import jp.probsc.simplecalc.Calculator; import junit.framework.TestCase; public class CalculatorTest extends TestCase { public void testAdd() { assertEquals(0, Calculator.Add(0, 0)); assertEquals(1, Calculator.Add(1, 0)); assertEquals(2, Calculator.Add(2, 0)); assertEquals(1, Calculator.Add(0, 1)); assertEquals(2, Calculator.Add(0, 2)); assertEquals(2, Calculator.Add(1, 1)); assertEquals(4, Calculator.Add(2, 2)); } }
単体試験の実施 • クラスを右クリックし、[実行(R)]-[Android JUnit Test]を実行
①単体試験
20
1つでもパスしないテストケースがあると、バーが赤色になります
テスト実施はテストケース(メソッド)ごと、クラスごと、プロジェクトごとの単位にて実施できます。
結合試験の準備 • UIを試験します ※テストプロジェクトに「MainActivityTest.java」を追加
21
②結合試験
package jp.probsc.simplecalc.test; import android.test.ActivityInstrumentationTestCase2; import android.widget.Button; import android.widget.EditText; import jp.probsc.simplecalc.MainActivity; public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> { public MainActivityTest() { super(MainActivity.class); } public void testCalc() { final EditText num1 = (EditText)getActivity().findViewById( jp.probsc.simplecalc.R.id.editNum1); final EditText num2 = (EditText)getActivity().findViewById( jp.probsc.simplecalc.R.id.editNum2); final Button btnAdd = (Button)getActivity().findViewById( jp.probsc.simplecalc.R.id.btnAdd);
getInstrumentation() .waitForIdleSync(); getActivity().runOnUiThread( new Runnable() { public void run() { num1.setText("2"); num2.setText("4"); btnAdd.performClick(); } }); getInstrumentation() .waitForIdleSync(); EditText numAnswer = (EditText) getActivity().findViewById( jp.probsc.simplecalc.R.id.editAnswer); assertEquals("6", numAnswer.getText().toString()); } }
結合試験の実施
• テストプロジェクトを選択して [実行 (R)]-[Android JUnit Test] を実行することで、先ほどの単体試験と同時にテストを実施できます
22
②結合試験
UIのテストは実機、またはエミュレータ上で実行されます。
Monkey Runner の利用
23
#!/usr/bin/python # coding: UTF-8 from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice import datetime # 日時文字列の取得 def getDate(): d = datetime.datetime.today() return d.strftime('%Y%m%d_%H%M%S') # スナップショットの取得 # (monkeyrunner.bat と同階層に snapshot フォルダを準備のこと) def snapshot(device): MonkeyRunner.sleep(1) result = device.takeSnapshot() result.writeToFile( 'snapshot/%s.png' % getDate(), 'png') # テスト初期化 def init(): device = MonkeyRunner.waitForConnection() device.startActivity( component='jp.probsc.simplecalc/.MainActivity') return device # 標準出力へメッセージを出力 def printMsg(msg): print '[%s] %s' % (getDate(), msg)
# テストの実施 def doTest(): device = init() snapshot(device) for i in range(1, 1001): printMsg('cnt: %d' % i) # 1つめの数値を入力 MonkeyRunner.sleep(1) device.touch(65, 215, 'DOWN_AND_UP') device.type('%d' % i) # 2つめの数値を入力 MonkeyRunner.sleep(1) device.touch(240, 215, 'DOWN_AND_UP') num = i * 2 device.type('%d' % num) # 「+」ボタンを押下 MonkeyRunner.sleep(1) device.touch(155, 110, 'DOWN_AND_UP') snapshot(device) # テストの開始 printMsg('test start.') doTest() printMsg('test end.')
mr_simplecalc.py
③安定化試験
Pythonスクリプトです
Monkey Runner の実行結果
c:¥> c:¥>C:¥android¥android-sdk-windows¥tools¥monkeyrunner.bat C:¥mr_simplecalc.py [20120318_145102] test start. [20120318_145117] cnt: 1 [20120318_145132] cnt: 2 [20120318_145148] cnt: 3 [20120318_145201] cnt: 4 [20120318_145215] cnt: 5 ...
24
・コマンドプロンプトに下記コマンドを入力してMonkey Runnerを起動します
・monkeryrunner.bat と同階層の snapshot フォルダに下記が保存されます
③安定化試験
今回のスクリプトの場合、1000回の足し算が自動的に実行されます。別途、メモリ使用状況などを監視してアプリの安定化を図ります
よりテストを実施しやすくするために
• 今回ご紹介したUIテストとMonkey Runnerでは、ウィジェットのIDや画面上の座標を知っている必要があり、「OKボタンを押す」などの直観的なテストをかけない...
• これまでJavaのプロジェクトではもっくオブジェクトを使って試験を行えたが、AndroidはVMが異なるため、既存ツールを利用できない...
25
NativeDriver や Scirocco などのツールの利用が便利です!
EasyMock の知識を Android Mock に活かせます!
• イメージビューアの改善
–縦画面と横画面のそれぞれに異なる1つ以上のアニ
メーションを追加しましょう
– メニューに1つ以上の独自の機能を追加しましょう
• SimpleCalc とテストの改善
– SimpleCalc に足し算以外の機能を追加しましょう
– テストプロジェクトに足し算以外の機能のテストケース
を追加しましょう 26
本日の課題