スポンサーリンク

Android SDK の動かないコード(初級編) ダイアログ上の要素にアクセスするとNullPointerExceptionになるエラー


以下のAndroidアプリのコードが意図した動作をしないのは,なぜですか。(制限時間1分)

やりたい事:

  • ダイアログを表示してから,ダイアログ上のボタンの文言を変更する。
package com.example;

import android.app.Activity;
import android.app.AlertDialog;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;

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

        // カスタムレイアウトでダイアログを表示
        ViewGroup dialog_root = (ViewGroup)findViewById(R.id.dialog_root);
        View layout_dialog = getLayoutInflater()
            .inflate(R.layout.my_dialog, dialog_root);
        AlertDialog.Builder dlg = new AlertDialog.Builder(this)
            .setTitle("タイトル")
            .setView(layout_dialog)
        ;
        dlg.show();

        // ダイアログ上のボタンの文言を変更
        Button btn1 = (Button)findViewById(R.id.btn1);
        btn1.setText("押してね");
    }
}


レイアウトXML:my_dialog.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@+id/dialog_root"
              android:orientation="vertical"
              android:layout_width="300dp"
              android:layout_height="200dp"
              >

    <EditText
        android:id="@+id/edit1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
    ></EditText>

    <Button
          android:layout_width="fill_parent"
          android:layout_height="wrap_content"
          android:textSize="20dp"
          android:layout_marginTop="15dp"
          android:id="@+id/btn1"
          android:text="@string/hello"
    />

</LinearLayout>

発生する不具合

btn1をコードから操作しようとした瞬間,NullPointerExceptionになる。

btn1にはnullが代入されてしまっている。


ViewのIDは正しく指定できているのに,なぜなのか。


エラーログ:

ERROR/AndroidRuntime(14729): Uncaught handler: thread main exiting due to uncaught exception
ERROR/AndroidRuntime(14729): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example/com.example.CodeTestActivity}: java.lang.NullPointerException
ERROR/AndroidRuntime(14729): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2401)
ERROR/AndroidRuntime(14729): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2417)
ERROR/AndroidRuntime(14729): at android.app.ActivityThread.access$2100(ActivityThread.java:116)
ERROR/AndroidRuntime(14729): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1794)
ERROR/AndroidRuntime(14729): at android.os.Handler.dispatchMessage(Handler.java:99)
ERROR/AndroidRuntime(14729): at android.os.Looper.loop(Looper.java:123)
ERROR/AndroidRuntime(14729): at android.app.ActivityThread.main(ActivityThread.java:4203)
ERROR/AndroidRuntime(14729): at java.lang.reflect.Method.invokeNative(Native Method)
ERROR/AndroidRuntime(14729): at java.lang.reflect.Method.invoke(Method.java:521)
ERROR/AndroidRuntime(14729): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:799)
ERROR/AndroidRuntime(14729): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:557)
ERROR/AndroidRuntime(14729): at dalvik.system.NativeStart.main(Native Method)
ERROR/AndroidRuntime(14729): Caused by: java.lang.NullPointerException
ERROR/AndroidRuntime(14729): at com.example.CodeTestActivity.onCreate(CodeTestActivity.java:28)
ERROR/AndroidRuntime(14729): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1123)
ERROR/AndroidRuntime(14729): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2364)
ERROR/AndroidRuntime(14729): ... 11 more

原因と解決策

setContentView()したのとは別のレイアウトを扱う場合,findViewById() は,

該当レイアウト要素のメソッドとして呼び出す必要がある。

        Button btn1 = (Button)findViewById(R.id.btn1);
        ↓
        Button btn1 = (Button)layout_dialog.findViewById(R.id.btn1);


参考:

Null pointer exception in android
http://stackoverflow.com/questions/49...

  • findViewByIdの前にdialog用のレイアウトを指定し忘れていた


Spinner in dialog - NullPointerException
http://stackoverflow.com/questions/63...

  • Your Spinner is probably not inflated yet. If you want to manipulate the views, inflate it yourself and then use setContentView on the inflated View. See the docs on Creating Dialogs.
  • 同じく,findViewByIdの前にdialog用のレイアウトを指定し忘れていた


AlertDialog#findViewByIdはshow以降じゃないと使えない
http://d.hatena.ne.jp/Kazzz/20100806/p1

  • ダイアログをshow()した後ならば,ダイアログ上の要素にアクセスできる
  • findViewByIdの前に,ちゃんとレイアウト要素が指定されている
  • 流し込まれたビューの階層構造はshowメソッドを実行しないと完成しない


また,show() してしまった後のAlertDialogは,
そのダイアログ上のLayoutからViewを取り出そうとしても,
ダイアログにアクセサメソッドがないので無理。

layout_dialogへの参照をどっかに保持しておいて,
あとからそれを呼び出してfindViewByIdするしかない。

※まあ,JavaScriptなんかではalert()のダイアログがDOMツリー上に存在せず,ダイアログ上の要素にアクセスする事がそもそもどうやっても無理なので,Androidではそれができるというだけでも恵まれている。

DialogのPositive(Negative)ボタンへのアクセスについて
http://groups.google.com/group/androi...

  • AlertDialog#getButton()メソッドでボタンの参照を取得することは可能

補足

カスタムダイアログ(サブウィンドウ)の作成方法:

Android, AlertDialog のメッセージテキストのレイアウトを変更する
http://www.serendip.ws/archives/5025

  • もっとも簡単なサンプル


ダイアログは永遠に(1) - AlertDialog
http://ichitcltk.hustle.ne.jp/gudon/m...

  • AlertDialogを構築する際に・・・
    • setSingleChoiceItems() とか setMultiChoiceItems() すれば,選択メニューを表示できる
    • LayoutInflaterを介してレイアウト定義XMLを読み込み,setView() すれば,ダイアログをカスタムレイアウトで表示できる


AlertDialogで文字を入力する(2) カスタムダイアログ
http://techbooster.jpn.org/andriod/ui...

  • ダイアログ上で文字入力するサンプル
  • showDialog() と onCreateDialog() を連動させる仕組み


AlertDialog.BuilderとLayoutInflaterによりダイアログにレイアウトを流し込む
http://d.hatena.ne.jp/Kazzz/20100426/p1

  • カレンダーの年月選択UIをダイアログ上に表示するサンプルコード
  • どんなビュー・レイアウトでもダイアログで利用できる訳で、全く持って素晴らしい。今まで数々のGUIツールキットでいろいろなダイアログのためのAPIを見てきたが、これだけの完成度の高い(柔軟性、生産性)ものはあまり見たことがない


カスタムダイアログ(CustomDialog)を作る
http://sites.google.com/site/androida...

  • ダイアログの背景画像(Theme)に9-pach画像を割り当て,画像を9つに分割して画像の伸縮を制御する


6.4 ダイアログの作成
http://www.techdoctranslator.com/andr...