Let's Re⇒move -30ページ目

Androidの授業 17 [ デバッグ② ]

【 デバッグ その2 】


R.javaでバグが起こったときの対処方です。

layoutフォルダに新規にtest.xmlファイルを追加します。そして、Rを見てみると
Rの中に自動的に作成したファイル名で値が追加されています。

たとえば、Test.xmlというファイルを作ると、これは無効なファイル名として扱われます。
このとき、コンソールには、

[2011-06-14 15:48:15 - Debug2] res\layout\Text.xml: 無効なファイル名: must contain only [a-z0-9_.]
と表示されています。
実は、xmlファイルを指定する際、大文字を使用してはいけませn。

この状態でR.javaを削除すると、Rは削除されたまま、生成されません。


正しい状態のときは、Rを消したとしてもすぐに復活します。
R関連でエラーが出る場合、Rを消しても自動生成されません。
これに関連して、
コンパイルしたファイルと今見ているソースが一致しているとは限りません。
その場合は
プロジェクト>クリーン
より、クリーンを行い、再度コンパイルするようにしてください。

今触っていないプロジェクトは基本的に閉じてください。閉じ方は
プロジェクトフォルダを選択>右クリック>「プロジェクトを閉じる」
です。まとめて閉じたいときはShift+上下カーソルにより選択対象を増やしてください


Rを使うにあたって注意点があります。

Rというのは二つあります。
1つめは今までの話の中で出てきていた

sample.debug2.R.layout.main

もう一つは

android.R.id.~

と、アンドロイド本体が持つRです。


新しくパッケージを追加してください。そして、パッケージの中にViewを継承したクラスを作成してください。

新しいクラスに以下のように記述してください

packagetest.debug2.test;

importandroid.app.Activity;
importandroid.content.Context;
importandroid.view.View;

publicclassTestView extendsView {

publicTestView(Context context) {
super(context);

(Activity)context).findViewById(R.id.test);
}

}

この場合、エラーとなる、もしくはandroid.Rがインポートされます。(エラーは取れませんが)

Rはパッケージをまたぐとインポートが必要になってきます。
なので使用するためには


packagetest.debug2.test;

importandroid.app.Activity;
importandroid.content.Context;
importandroid.view.View;
importtest.debug2.*;

publicclassTestView extendsView {

publicTestView(Context context) {
super(context);

((Activity)context).findViewById(R.id.testtest);
}

}


Rに関しては、ファイル名を数字、大文字にしなければ、エラーになりまくるということは無いと思います。


後からやる内容をコメントする場合は


package test.debug2;

import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

//TODO テキストビューの内容を変更する
}
}

と、TODOをつけるとよいです。
この状態でタスクビュー
ウィンドウ>ビュー>その他>一般>タスクで追加できます。
タスクビューにはTODOのコメント内容が表示されるので、作業漏れということが少なくなると思います。



質問
各フォルダは整理するために作られてるのか。layoutなど、決められたフォルダには決められたファイルしか入らないのか。命名規則など


グーグルで「リソースの提供 android」と検索して一番上のページを見てください。
いろいろ詳しく書いてあるので読んでください


次に、前回と同様実際にソースを書いてコンパイルしていきます。
中身にあまり意味はありません。テスト用です

main.xmlを以下のように記述してください。
内容はデフォルトのそーすにidを加えただけです(水色

<?xmlversion="1.0"encoding="utf-8"?>
<LinearLayoutxmlns:android="
http://schemas.android.com/apk/res/android "
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:id="@+id/tv_1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
</LinearLayout>


package test.debug2;

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

TextView tv = (TextView)findViewById(R.id.tv_1);
setTextViewText(tv);
}

private void setTextViewText(TextView tv) {
tv.setText("変更");
tv.setBackgroundColor(Color.BLUE);
tv.setTextColor(Color.WHITE);
}
}



昨日の授業ではブレークポイント、ステップイン、ステップオーバー、変数の確認、式の確認
を覚えました。


package test.debug2;

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

TextView tv = (TextView)findViewById(R.id.tv_1);
setTextViewText(tv);
}

private void setTextViewText(TextView tv) {
tv = null;
tv.setText("変更");
tv.setBackgroundColor(Color.BLUE);
tv.setTextColor(Color.WHITE);
}
}





package test.debug2;

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

TextView tv = (TextView)findViewById(R.id.tv_1);
setTextViewText(tv);
}

private void setTextViewText(TextView tv) {
int a = 100;
tv.setText("変更"+ (a / 0));
tv.setBackgroundColor(Color.BLUE);
tv.setTextColor(Color.WHITE);
}
}



06-14 07:55:08.002: ERROR/AndroidRuntime(540): FATAL EXCEPTION: main
06-14 07:55:08.002: ERROR/AndroidRuntime(540): java.lang.RuntimeException: Unable to start activity ComponentInfo{test.debug2/test.debug2.MainActivity}: java.lang.ArithmeticException: divide by zero
06-14 07:55:08.002: ERROR/AndroidRuntime(540): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1647)
06-14 07:55:08.002: ERROR/AndroidRuntime(540): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663)
06-14 07:55:08.002: ERROR/AndroidRuntime(540): at android.app.ActivityThread.access$1500(ActivityThread.java:117)
06-14 07:55:08.002: ERROR/AndroidRuntime(540): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:931)
06-14 07:55:08.002: ERROR/AndroidRuntime(540): at android.os.Handler.dispatchMessage(Handler.java:99)
06-14 07:55:08.002: ERROR/AndroidRuntime(540): at android.os.Looper.loop(Looper.java:123)
06-14 07:55:08.002: ERROR/AndroidRuntime(540): at android.app.ActivityThread.main(ActivityThread.java:3683)
06-14 07:55:08.002: ERROR/AndroidRuntime(540): at java.lang.reflect.Method.invokeNative(Native Method)
06-14 07:55:08.002: ERROR/AndroidRuntime(540): at java.lang.reflect.Method.invoke(Method.java:507)
06-14 07:55:08.002: ERROR/AndroidRuntime(540): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
06-14 07:55:08.002: ERROR/AndroidRuntime(540): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
06-14 07:55:08.002: ERROR/AndroidRuntime(540): at dalvik.system.NativeStart.main(Native Method)
06-14 07:55:08.002: ERROR/AndroidRuntime(540): Caused by: java.lang.ArithmeticException: divide by zero
06-14 07:55:08.002: ERROR/AndroidRuntime(540): at test.debug2.MainActivity.setTextViewText(MainActivity.java:21)
06-14 07:55:08.002: ERROR/AndroidRuntime(540): at test.debug2.MainActivity.onCreate(MainActivity.java:16)
06-14 07:55:08.002: ERROR/AndroidRuntime(540): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
06-14 07:55:08.002: ERROR/AndroidRuntime(540): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1611)
06-14 07:55:08.002: ERROR/AndroidRuntime(540): ... 11 more


package test.debug2;

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

public class MainActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

TextView tv = (TextView)findViewById(R.id.tv_1);
setTextViewText(tv);
Log.d("test", "test");
}

private void setTextViewText(TextView tv) {
int a = 100;
try {
tv.setText("変更"+ (a / 0));
} catch (Exception e) {
Log.d("Exception", "例外発生");
}
tv.setBackgroundColor(Color.BLUE);
tv.setTextColor(Color.WHITE);
}
}


小技
使っていないインポートを消す場合、「インポートの編成」を行うと一度に全部消えるので便利です。

参考になるものとしては、SDKのサンプルプロジェクトがあります。

ソースを元のファイルまで全部追うのはお勧めできないです。

Androidの授業 16 [ デバッグ ]

【 デバッグについて 】


main.xmlを以下のように記述します。

<?xmlversion="1.0"encoding="utf-8"?>
<LinearLayoutxmlns:android="
http://schemas.android.com/apk/res/android "
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:id="@+id/tv_1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
</LinearLayout>


MainActivity.javaを以下のように記述します。

package test.debugtest;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

View view = findViewById(R.id.tv_1);
setTextView(view);

Log.d("TEXT", ((TextView) view).getText().toString());
}

private void setTextView(View view) {
TextView tv = (TextView) view;
tv.setText("変更しました");
}
}



ブレークポイントを

View view = findViewById(R.id.tv_1);
に設定する
行番号は右クリック→行番号の表示を選択

ビューに式・変数を追加する

デバッグモードで実行する

F6を押すと1行進む


package test.debugtest;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

View view = findViewById(R.id.tv_1);
setTextView(view);

Log.d("TEXT", ((TextView) view).getText().toString());
}

private void setTextView(View view) {
TextView tv = (TextView) view;
tv.setText("変更しました");
}
}

上の水色の箇所でF5を押すとメソッドの中に飛んでいきます。
メソッドにエラーがないならF6で飛ばす。
メソッドが怪しくて中の状況を確認したいのであればF5を押していくとよいです。
再開はF8が早いです。


このソースの中でsetTextメソッド内の
TextView tv = (TextView) view;
にブレークポイントを設定すると、

実行されているはずなのに、どうにも反映されていないとなるとブレークポイントを設定してみてブレークしなければ「実行されていない」ということになります。

今回覚える点は

ブレークポイント、ステップイン、ステップオーバー、再開です。


無理やりエラーを起こしてみます
ソースを赤色箇所のように変更してみます。


package test.debugtest;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

View view = findViewById(R.id.tv_1);
setTextView(view);

Log.d("TEXT", ((TextView) view).getText().toString());
}

private void setTextView(View view) {
TextView tv = null;
tv.setText("変更しました");
}
}


これをデバッグでなく「実行」で起動すると落ちます。


LogCatを確認していきます。

06-13 07:12:29.428: ERROR/AndroidRuntime(413): java.lang.RuntimeException: Unable to start activity ComponentInfo{test.debugtest/test.debugtest.MainActivity}: java.lang.NullPointerException
06-13 07:12:29.428: ERROR/AndroidRuntime(413): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1647)
06-13 07:12:29.428: ERROR/AndroidRuntime(413): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663)
06-13 07:12:29.428: ERROR/AndroidRuntime(413): at android.app.ActivityThread.access$1500(ActivityThread.java:117)
06-13 07:12:29.428: ERROR/AndroidRuntime(413): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:931)
06-13 07:12:29.428: ERROR/AndroidRuntime(413): at android.os.Handler.dispatchMessage(Handler.java:99)
06-13 07:12:29.428: ERROR/AndroidRuntime(413): at android.os.Looper.loop(Looper.java:123)
06-13 07:12:29.428: ERROR/AndroidRuntime(413): at android.app.ActivityThread.main(ActivityThread.java:3683)
06-13 07:12:29.428: ERROR/AndroidRuntime(413): at java.lang.reflect.Method.invokeNative(Native Method)
06-13 07:12:29.428: ERROR/AndroidRuntime(413): at java.lang.reflect.Method.invoke(Method.java:507)
06-13 07:12:29.428: ERROR/AndroidRuntime(413): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
06-13 07:12:29.428: ERROR/AndroidRuntime(413): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
06-13 07:12:29.428: ERROR/AndroidRuntime(413): at dalvik.system.NativeStart.main(Native Method)
06-13 07:12:29.428: ERROR/AndroidRuntime(413): Caused by: java.lang.NullPointerException
06-13 07:12:29.428: ERROR/AndroidRuntime(413): at test.debugtest.MainActivity.setTextView(MainActivity.java:24)
06-13 07:12:29.428: ERROR/AndroidRuntime(413): at test.debugtest.MainActivity.onCreate(MainActivity.java:17)
06-13 07:12:29.428: ERROR/AndroidRuntime(413): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
06-13 07:12:29.428: ERROR/AndroidRuntime(413): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1611)
06-13 07:12:29.428: ERROR/AndroidRuntime(413): ... 11 more

「E」は動作がとまる致命的なエラーです。
一番最新の行に、

エラーがあるときに何をするか

エラーが起きたとき、LogCatを見に行って、現在作っているアプリのパッケージ名があるかどうかを見に行きます。
今作っているパッケージがあれば、それをダブルクリックするとそこに飛んでくれます。
エラーが起きたときにやる第一歩です。
何が原因でというのは
「Caused by ・・・」以下に表示されます。

これで解決できない場合。
エラーになれば必ず落ちてしまう。


デバッグ画面の使い方を見ていきます。

式タブ→右クリック→式の追加

APIが分からない時に、いくつか書いて試してみて欲しい戻り値のものを採用するときにも使えます。

ブレークポイントタブは

ブレークポイントを追加していくと増えていき、消すとその項目も消えます。

タブの中のアイコンは用途によって使っていくと便利です。

次に左上「デバッグ」タブ
アプリを停止したいときは赤四角のアイコンを押します。

次に右下「LogCat」


MoveViewプロジェクトを開いてみてください。

この中のタッチイベントを拾うメソッドの中にブレークポイントを設定した場合
動いた瞬間に止まってしまうのでデバッグができない場合があります。

そんなときにはLogを仕込んであげるとデバッグが可能となります。

たとえば、ソースに
Log.d(“ACTION_MOVE”, “X =” + x + “ y=” + y);
とすると
LogCatに実行しながら現在進行形で値が表示されます。


コネタとして、何も間違っていないのになぜ動かないのかという場合、
adb kill-server
adb start-server

とやると、動く場合があります。何故かインストールもできない、何故かデバッグもできない場合
上記のコマンドを試してみてください。

LogCatが起動しているにもかかわらず、Logが表示されない場合
DDMSを開いて、LogCat対象としたい実機もしくはエミュレータを選択してみてください。


いろいろ試して、使い方に慣れていくのがよいと思います。

Androidの授業 15 [ 簡単なアプリ③-2 ]

【○×ゲーム続き】

MainActivity.javaの編集続き


・各メソッドの準備
------------------------------------
/**
* メニューボタンの設定
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
return true;
}

/**
* メニューボタンを押した時の処理
*/
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
return true;
}
/**
* メニュー:新規選択時の処理
*/
private void resetGame() {

}

/**
* 画面への設定、○×が揃っているかチェック
* @param rect
* @throws GameOverException
*/
private void occupy(View rect)
throws GameOverException {

}

/**
* 先手・後手の交代
*/
private void turnOver() {

}
/**
* ○・×のラインが揃っているかチェック
* @param buttons
* @param a
* @param b
* @param c
* @return
*/
private boolean check(List<Integer>buttons,
int a, int b, int c) {
return true;
}

}
-----------------------------------------------
上記を埋めていく
授業の順番
・onClick
・occupy
・check
・turnOver
・onCreateOptionsMenu
・onMenuItemSelected
-----------------------------------------------
package test.easymarubatu;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.animation.ScaleAnimation;
import android.widget.ImageButton;
import android.widget.Toast;

public class MainActivity extends Activity {

/** イメージボタンをリストで保持する*/
private ImageButton[] rectButtons =new ImageButton[9];

/** ゲームが完了しているかどうか */
private boolean isGameOver = false;

/** 現在のターン(先手か後手か)*/
private Flag currentTurn = Flag.Black;

/** 先手のチェック用リスト */
private List<Integer> blackTerritories =
new ArrayList<Integer>();

/** 後手のチェック用リスト */
private List<Integer> whiteTerritories =
new ArrayList<Integer>();

/** ボタンを押した時の処理 */
private View.OnClickListener onClickListener=new View.OnClickListener(){
@Override
public void onClick(View v){
//既にゲームオーバーなら何もしない
if(isGameOver){
return;
}
//既に○か×どちらかが占領している時は何もしない
if(blackTerritories.contains(v.getId())
|| whiteTerritories.contains(v.getId())) {
return;
}
try {
//アニメーションの設定
ScaleAnimation scale =
new ScaleAnimation(1.15f,0.95f,1.15f,0.95f,50,50);
scale.setDuration(150);
v.setAnimation(scale);

//先手・後手チェック
//○×が揃っているかチェック
occupy(v);
}catch (GameOverException ex) {
//例外発生はゲーム時に起こる
Toast.makeText(
MainActivity.this,
ex.getMessage(),
Toast.LENGTH_LONG ).show();
}

}
};

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

//イメージボタンインスタンスの取得をし、配列に設定
rectButtons[0] =
(ImageButton) findViewById(R.id.left_top);
rectButtons[1] =
(ImageButton) findViewById(R.id.left);
rectButtons[2] =
(ImageButton) findViewById(R.id.left_bottom);
rectButtons[3] =
(ImageButton) findViewById(R.id.top);
rectButtons[4] =
(ImageButton) findViewById(R.id.center);
rectButtons[5] =
(ImageButton) findViewById(R.id.bottom);
rectButtons[6] =
(ImageButton) findViewById(R.id.right_top);
rectButtons[7] =
(ImageButton) findViewById(R.id.right);
rectButtons[8] =
(ImageButton) findViewById(R.id.right_bottom);

//リスナーの設定
for (ImageButton ib : rectButtons) {
ib.setOnClickListener(onClickListener);
}
}
/**
* メニューボタンの設定
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
menu.add(0,0,0,"新規ゲーム");
menu.add(0,1,1,"終了");
return true;
}

/**
* メニューボタンを押した時の処理
*/
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
switch (item.getItemId()) {
case 0: //新規ゲーム
resetGame();
break;
case 1: //終了
finish();
break;
}
return true;
}

/**
* メニュー:新規選択時の処理
*/
private void resetGame() {
//イメージボタンの設定を全てデフォルトに戻す
for (ImageButton ib : rectButtons) {
ib.setBackgroundResource(R.drawable.none);
}

}

/**
* 画面への設定、○×が揃っているかチェック
* @param rect
* @throws GameOverException
*/
private void occupy(View rect)
throws GameOverException {
//先手・後手のチェック
boolean isBlack = currentTurn == Flag.Black;

//対象のイメージボタンに○・×の設定
rect.setBackgroundResource
(isBlack ? R.drawable.black
:R.drawable.white);

//先手・後手のリスト設定
List<Integer> territories =
isBlack ? blackTerritories : whiteTerritories;

//リストに選択したViewのIDを追加
territories.add(rect.getId());

//メッセージの設定
String message =
String.format(
"%sの勝ち!",isBlack ? "○":"×");

//水平ライン
if (
check(territories,
R.id.left_top,R.id.top,R.id.right_top)
||check(territories,
R.id.left,R.id.center,R.id.right)
||check(territories,
R.id.left_bottom,R.id.bottom,R.id.right_bottom)) {
isGameOver = true;

}

//垂直ライン
if (
check(territories,
R.id.left_top,R.id.left,R.id.left_bottom)
||check(territories,
R.id.top,R.id.center,R.id.bottom)
||check(territories,
R.id.right_top,R.id.right,R.id.right_bottom)) {
isGameOver = true;
}

//クロスライン
if (
check(territories,
R.id.left_top,R.id.center,R.id.right_bottom)
||check(territories,
R.id.right_top,R.id.center,R.id.left_bottom)) {
isGameOver = true;
}

//引き分け
if(blackTerritories.size()
+ whiteTerritories.size() >= 9
&& !isGameOver)
{
message = "引き分け!";
isGameOver = true;
}

//終了判定時はエラーを投げてメッセージを通知する
if (isGameOver) {
throw new GameOverException(message);
}

//先手・後手の交替
turnOver();
}

/**
* 先手・後手の交代
*/
private void turnOver() {
if (currentTurn == Flag.Black) {
currentTurn = Flag.White;
} else {
currentTurn = Flag.Black;
}

}

/**
* ○・×のラインが揃っているかチェック
* @param buttons
* @param a
* @param b
* @param c
* @return
*/
private boolean check(List<Integer>buttons,
int a, int b, int c) {
boolean ret =
buttons.contains(a) &&
buttons.contains(b) &&
buttons.contains(c);

return ret;
}

}
--------------------------------------------------