動態新增 TextView 到 LinearLayout,設定 setId() 後嘗試透過 findViewById() 取得,卻發生空指標;點擊移除時也找不到對應視圖。
// 動態建立 TextView
TextView nTv = new TextView(view.getContext());
nTv.setId(tag_id); // 設定 ID
nTv.setText(str.substring(0, str.length()-1));
linl.addView(nTv);
// 點擊移除 - 錯誤寫法
nTv.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
EditText et = (EditText) view.findViewById(R.id.edittext_tags); // view 可能非根視圖
TextView t = ((TextView)v);
et.setText(t.getText().toString());
linl.removeView(v); // v 就是被點擊的 TextView,可直接移除
}
});
View v 參數就是被點擊的 TextView,直接使用即可view.findViewById() 中,view 若非根視圖或不包含目標元件會返回 nullView.generateViewId() (API 17+) 產生唯一 ID,避免硬編碼衝突final LinearLayout linl = findViewById(R.id.linear_layout_tags);
EditText et = findViewById(R.id.edittext_tags); // 先取得參考
// 動態新增
TextView nTv = new TextView(this);
nTv.setId(View.generateViewId()); // API 17+ 產生唯一 ID
nTv.setText("動態標籤");
nTv.setTextColor(Color.BLUE);
nTv.setTextSize(20);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.setMargins(10, 0, 0, 0);
nTv.setLayoutParams(params);
nTv.setOnClickListener(v -> {
// v 就是被點擊的 TextView,無需 findViewById
et.setText(((TextView) v).getText().toString());
linl.removeView(v); // 直接移除
});
linl.addView(nTv);
int dynamicId = View.generateViewId();
nTv.setId(dynamicId);
linl.addView(nTv);
// 稍後在同一個視圖層級中尋找
TextView found = linl.findViewById(dynamicId); // 在父容器中找,而非 Activity
if (found != null) {
// 操作 found
}
// 設定業務資料
nTv.setTag(new TagData(itemId, itemName, itemType));
// 取得業務資料
nTv.setOnClickListener(v -> {
TagData data = (TagData) v.getTag();
// 使用 data.itemId 等進行業務邏輯
et.setText(data.itemName);
linl.removeView(v);
});
static class TagData {
long itemId;
String itemName;
String itemType;
TagData(long id, String name, String type) {
this.itemId = id; this.itemName = name; this.itemType = type;
}
}
public class TagInputActivity extends AppCompatActivity {
private LinearLayout tagContainer;
private EditText inputEditText;
private Set tags = new LinkedHashSet<>(); // 保持順序且不重複
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tag_input);
tagContainer = findViewById(R.id.tag_container);
inputEditText = findViewById(R.id.input_edittext);
Button addBtn = findViewById(R.id.add_btn);
addBtn.setOnClickListener(v -> addTag());
loadSavedTags();
}
private void addTag() {
String text = inputEditText.getText().toString().trim();
if (text.isEmpty() || tags.contains(text)) return;
tags.add(text);
createTagView(text);
inputEditText.setText("");
saveTags();
}
private void createTagView(String text) {
TextView tagView = new TextView(this);
tagView.setId(View.generateViewId());
tagView.setText(text);
tagView.setTextColor(Color.WHITE);
tagView.setTextSize(14);
tagView.setBackgroundResource(R.drawable.tag_background);
tagView.setPadding(16, 8, 16, 8);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.setMargins(0, 0, 8, 8);
tagView.setLayoutParams(params);
tagView.setOnClickListener(v -> {
String tagText = ((TextView) v).getText().toString();
tags.remove(tagText);
tagContainer.removeView(v);
saveTags();
});
tagContainer.addView(tagView);
}
private void loadSavedTags() {
// 從 SharedPreferences 載入並為每個建立視圖
}
private void saveTags() {
// 儲存至 SharedPreferences
}
}
| 需求 | 推薦做法 | 避免做法 |
|---|---|---|
| 動態視圖唯一 ID | View.generateViewId() (API 17+) | 硬編碼整數、隨機數 |
| 點擊事件取得視圖 | 直接使用 View v 參數 | findViewById() 尋找 |
| 儲存業務資料 | setTag(Object) / getTag() | 塞進 ID、用 Map 映射 |
| 移除動態視圖 | parent.removeView(child) | 設定 GONE、重建整個容器 |
動態建立視圖時,點擊事件參數即為目標視圖,無需透過 ID 尋找。儲存業務資料請用 setTag(),ID 僅供視圖樹查找使用。善用 View.generateViewId() 避免 ID 衝突,直接操作父容器 removeView() 移除子視圖。