Android 開発において、null オブジェクトの参照で仮想メソッドを呼び出そうとする。
概要を説明します。
Androidレイアウトファイルの読み込み中にエラーが発生しました。
java.lang.NullPointerException: Null オブジェクト参照で仮想メソッド '.........' を呼び出そうとしました。
このエラーは、正しい View 環境で findViewById() メソッドを使用しなかったことが原因である可能性があります。
Androidは初めてで、何もわからない。
ListViewを作成し、ItemをクリックするとImageViewを含むダイアログボックスが表示されるという機能を、以下のコードで実装したいと思います。
ImageView photo;
Bitmap bitmap = BitmapFactory.decodeStream(
getContext().getContentResolver().openInputStream(uri));
ImageView photo = getView().findViewById(R.id.photo);
photo.setImageBitmap(bitmap);
Builder builder = new AlertDialog.Builder(getContext());
builder.setView(R.id.dialog);
/* Define other buttons */
AlertDialog dialog = builder.create();
dialog.show();
ダイアログ用に新しいレイアウトファイルが作成され、このR.id.photoはそのファイルに配置されます。
しかし、ランタイムはエラーを報告し続けます。
java.lang.NullPointerException: NULLオブジェクトの参照に対して仮想メソッド「............」を呼び出そうとしています。
原因は、写真が正しく初期化されていないためです。
しばらく考えてから、明らかに関連付けしているし、コンパイルも問題ないので、正しく初期化されているはずです。
そこで、今まで無差別に使っていたfindViewById()の原理を考えてみた。このエラーで、もしかしたらfindViewById()が正しく設定されていないのかもしれないと気づきました。
通常、findViewById()の使用はActivityのOnCreate()メソッド内などです。
button = (Button)findViewById(R.id.button);
ここで、(Button)は省略可能です。
ここでのfindViewById()関数は、Activityクラスのものです。
public <T extends View> T findViewById(@IdRes int id) {
return getWindow().findViewById(id);
}
Fragment の中で findViewById() を使う場合、Fragment の View オブジェクトを作成し、View オブジェクトの FindViewById() メソッドを呼び出す必要があります。
View v = inflater.inflate(R.layout.fragment, container, false);
listView = v.findViewById(R.id.list_view);
ViewクラスのfindViewById()メソッドの場所は以下の通りです。
@Nullable
public final <T extends View> T findViewById(@IdRes int id) {
if (id == NO_ID) {
return null;
}
return findViewTraversal(id);
}
細かいことは調べない、(どうせ私のレベルではわからないから)
上のエラーが出たのは、どのfindViewById()を使えばいいのかが分からなかったからです。なぜなら、getView()の呼び出しは現在表示されているViewを取得することになっており、ここではDialogダイアログに固有の新しいレイアウトファイルを表示しようとしているので、ImageViewを初期化するために現在のViewオブジェクトを使用することができないからです。
解決策としては、Dialog クラスを継承した新しいクラスを作成し、そのクラスで表示する要素を定義し、OnCreate() メソッドを以下のように再初期化することです。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LayoutInflater inflater = (LayoutInflater) mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View layout = inflater.inflate(R.layout.photo_dialog, null);
photo = layout.findViewById(R.id.photo);
try{
Bitmap bitmap = BitmapFactory.decodeStream(
getContext().getContentResolver().openInputStream(uri));
photo.setImageBitmap(bitmap);
}catch (Exception e){
e.printStackTrace();
}
this.setContentView(layout);
}
}
オリジナルのFragmentは、2つのシンプルなセンテンスで構成されていました。
MyDialog myDialog = new MyDialog(getContext());
myDialog.show();
要するに、複雑なDialogを作るには、Dialogのメソッドをオーバーライドして、元のクラスにゴチャゴチャ詰め込まない方が良いということです。
初心者の私は、自分のコードをOOPにしたいと思うこともありますが、他の方の素晴らしいコードを見ると、デカップリングはコードの長さではなく、特定の機能が必要かどうかで決まるので、経験が必要だと感じています。
関連
-
Android プロジェクトのライブラリに armeabi-v7a、armeabi、x86 が追加されました。
-
コンフィギュレーション 'compile' は廃止され、'implementati solution' に置き換わりました。
-
ADBサーバーがACKしなかった
-
AndroidManifest.xml は、アプリが Google 検索でインデックス化されていないことを警告しています。
-
root化されているのですが、adb shellの後、suを入力するとpermission deniedと表示されます。
-
AndroidのEditTextにデフォルト値を設定する方法とヒントを設定する方法
-
AndroidでデータをExcelファイルに書き出す方法
-
AndroidのRadioButtonの中央寄せ問題(解決済み)
-
Android android-support-multidexを使用すると、Dexがメソッドの制限を超える問題を解決し、アプリケーションがバーストしなくなります。
-
Android AVDで "このターゲットにはシステムイメージがインストールされていません "と表示される
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
Android Studio + Gradle またはコマンドラインを使用した Android apk の署名とパッケージング
-
ADBサーバーがACKしない問題を解決しました。
-
ProcessBuilderExceptionCreateProcess error=2, ϵͳÕҲ "μ½ָ¶".
-
JSONException: java.lang.String は JSONObject ソリューションに変換できません。
-
Androidリストウィジェット開発詳細
-
ConstraintLayoutにおけるChainとGuidelineの利用について
-
Android動的ブロードキャストの追加許可
-
Android TextViewの自動改行問題
-
android.content.res.Resources$NotFoundException: 文字列リソースID #0x1
-
AndroidManifestのuses-permissionの設定