1. 程式人生 > >Android開發中出現Attempt to invoke virtual method...on a null object reference

Android開發中出現Attempt to invoke virtual method...on a null object reference

摘要:

載入Android佈局檔案時出現錯誤:
java.lang.NullPointerException: Attempt to invoke virtual method '........' on a null object reference
出錯原因可能是未能在正確的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);
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setView(R.id.dialog);
/*定義其他按鍵*/
AlertDialog dialog = builder.create();
dialog.show();

為對話方塊新建了一個Layout佈局檔案,這個R.id.photo就放在這個檔案中。

然而執行時老是報錯:

java.lang.NullPointerException: Attempt to invoke virtual method '........' on a null object reference

問題的原因是photo沒有正確初始化。

想了一下,明明我都關聯好了,編譯也沒問題,應該能正確初始化的。

然後想到了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對話方塊專用的新的佈局檔案,因此不能用當前的View物件來初始化ImageView。

解決辦法就是新建一個類繼承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中只需要簡單的兩句:

MyDialog myDialog = new MyDialog(getContext());
myDialog.show();

總之就是要建立一個複雜的Dialog,最好重寫Dialog方法,別把亂七八糟的東西都擠到原來的類裡。

作為一個初學者,有時候吧,想把程式碼寫得OOP一點,但是看到別人的優秀程式碼,感覺其實要不要解耦不取決於程式碼的長短,而取決於具體的功能實現是否需要,這個還是需要經驗的積累吧。

這麼簡單的問題搞了一下午,真的是被自己蠢哭了。