首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Android Spinner 動態添加數據:ArrayAdapter 用法與 NullPointerException 避坑

     編輯:程式設計綜合問答

錯誤現象


ArrayAdapter
adapter = new ArrayAdapter<>( this, android.R.layout.simple_spinner_item, HomePage.NearPlaces); // NearPlaces 可能為 null Place.setAdapter(adapter); // Place 可能未初始化 (findViewById 失敗)

拋出 NullPointerException,堆疊指向 SpotMe.onCreate:42。

根本原因排查

  1. 數據源為 null:HomePage.NearPlaces 靜態字段未初始化或已清空
  2. Spinner 未綁定:Place = findViewById(R.id.spinner_id) 返回 null (ID 錯誤/佈局未設置)
  3. Context 錯誤:非 Activity 上下文傳給 Adapter (如 ApplicationContext 導致主題異常)

正確完整流程


public class SpotMe extends AppCompatActivity {
    private Spinner placeSpinner;
    private ArrayAdapter adapter; // 泛型用 String 或自定義模型
    private List placeList = new ArrayList<>(); // 初始化空列表,避免 null

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_spot_me); // 關鍵:先 setContentView
        
        placeSpinner = findViewById(R.id.spinner_place); // 確保 ID 正確
        if (placeSpinner == null) {
            Log.e(TAG, "Spinner not found in layout");
            return;
        }

        // 1. 從數據源獲取 (異步/線程安全)
        loadPlacesFromSource();

        // 2. 創建 Adapter (使用簡單佈局)
        adapter = new ArrayAdapter<>(
            this, 
            android.R.layout.simple_spinner_item, 
            placeList); // 傳入列表引用
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        
        // 3. 綁定
        placeSpinner.setAdapter(adapter);
        
        // 4. 監聽選擇
        placeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override public void onItemSelected(AdapterView parent, View view, int pos, long id) {
                String selected = placeList.get(pos);
                // 處理選擇...
            }
            @Override public void onNothingSelected(AdapterView parent) {}
        });
    }

    private void loadPlacesFromSource() {
        // 模擬異步加載
        new Thread(() -> {
            List loaded = fetchPlacesFromServerOrLocal(); // 你的數據源
            runOnUiThread(() -> {
                placeList.clear();
                placeList.addAll(loaded);
                adapter.notifyDataSetChanged(); // 關鍵:通知更新
            });
        }).start();
    }
}

XML 佈局定義



進階:自定義模型與下拉項佈局


// 模型
class Place {
    String id, name, address;
    @Override public String toString() { return name; } // Adapter 默認顯示 toString()
}

// 自定義 Adapter (若需複雜下拉項佈局)
class PlaceAdapter extends ArrayAdapter {
    PlaceAdapter(Context ctx, List list) {
        super(ctx, 0, list);
    }
    @NonNull @Override public View getView(int pos, View cv, ViewGroup parent) {
        return createView(pos, cv, parent, android.R.layout.simple_spinner_item);
    }
    @Override public View getDropDownView(int pos, View cv, ViewGroup parent) {
        return createView(pos, cv, parent, R.layout.spinner_dropdown_item); // 自定義下拉項
    }
    View createView(int pos, View cv, ViewGroup parent, int layoutRes) {
        if (cv == null) cv = LayoutInflater.from(getContext()).inflate(layoutRes, parent, false);
        Place p = getItem(pos);
        TextView tv = cv.findViewById(android.R.id.text1); // 或自定義 ID
        tv.setText(p.name);
        return cv;
    }
}

避坑清單

  1. ✅ setContentView 後再 findViewById
  2. ✅ 數據列表初始化 new ArrayList<>(),避免 null
  3. ✅ 異步加載後 adapter.notifyDataSetChanged()
  4. ✅ Spinner ID 與佈局一致
  5. ✅ 使用 this (Activity) 作為 Adapter Context
  6. ✅ 釋放資源:onDestroy 中取消異步任務
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved