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

Android 動態移除視圖崩潰:findViewById 資源 ID 計算錯誤排查

     編輯:程式設計綜合問答

崩潰日誌

java.lang.NullPointerException
    at com.com.com.addtask.deltask(addtask.java:118)
    at com.com.com.addtask$2.onClick(addtask.java:65)

第 118 行:tasklayout.removeView(taskbutton);

問題代碼

public void deltask(View v) {
    SharedPreferences datatask = getSharedPreferences(filename, 0);
    int tnum = datatask.getInt("tasknumber", 1);
    int layoutnum = 1000 + tnum;      // 動態計算 ID
    int textviewnum = 1100 + tnum;
    int buttonviewnum = 1200 + tnum;

    LinearLayout tasklayout = (LinearLayout) findViewById(layoutnum); // 關鍵錯誤
    TextView tasktv = (TextView) findViewById(textviewnum);
    Button taskbutton = (Button) findViewById(buttonviewnum);

    tasklayout.removeView(taskbutton); // tasklayout 為 null → NPE
    tasklayout.removeView(tasktv);
    layout.removeView(tasklayout);
}

根本原因

findViewById() 參數必須是編譯時生成的 R.id.xxx 整數常數,不能是運行時計算的數字。layoutnum = 1000 + tnum 只是普通整數,對應的視圖在 XML 中未定義此 ID,故 findViewById 返回 null。

正確解決方案

方案一:使用 Tag 標記視圖(推薦)

// 動態創建視圖時設置 tag
LinearLayout taskLayout = new LinearLayout(this);
taskLayout.setId(View.generateViewId()); // API 17+ 生成唯一 ID
taskLayout.setTag("task_layout_" + tnum); // 或直接存物件

Button taskButton = new Button(this);
taskButton.setTag("task_button_" + tnum);
taskLayout.addView(taskButton);

// 刪除時:遍歷父容器查找 tag
for (int i = 0; i < parentLayout.getChildCount(); i++) {
    View child = parentLayout.getChildAt(i);
    if ("task_layout_" + tnum).equals(child.getTag())) {
        parentLayout.removeView(child);
        break;
    }
}

方案二:維護視圖集合

// 類成員
private Map<Integer, LinearLayout> taskLayouts = new HashMap<>();
private Map<Integer, Button> taskButtons = new HashMap<>();

// 創建時存入
taskLayouts.put(tnum, taskLayout);
taskButtons.put(tnum, taskButton);

// 刪除時直接獲取
LinearLayout layout = taskLayouts.remove(tnum);
Button btn = taskButtons.remove(tnum);
if (layout != null) parentLayout.removeView(layout);

方案三:RecyclerView + Adapter(現代最佳實踐)

使用 RecyclerView 配合 ListAdapter,刪除項目只需:

tasks.remove(position);
adapter.notifyItemRemoved(position);
// 自動處理動畫、視圖回收、記憶體管理

核心原則

  • 絕不動態計算 R.id:資源 ID 編譯期固定,運行時不可變
  • 動態視圖用 Tag 或集合管理:而非依賴 ID 查找
  • 拥抱 RecyclerView:列表增刪改查的標準解法
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved