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

Android 動態建立 TextView 並正確管理 ID 與點擊事件

     編輯:程式設計綜合問答

問題現象

動態新增 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,可直接移除
    }
});

核心問題

  1. 不需要透過 ID 尋找:點擊事件的 View v 參數就是被點擊的 TextView,直接使用即可
  2. findViewById 作用域錯誤:在 view.findViewById() 中,view 若非根視圖或不包含目標元件會返回 null
  3. 動態 ID 衝突風險:View.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);

方案二:若確實需要透過 ID 尋找(罕見)

int dynamicId = View.generateViewId();
nTv.setId(dynamicId);
linl.addView(nTv);

// 稍後在同一個視圖層級中尋找
TextView found = linl.findViewById(dynamicId); // 在父容器中找,而非 Activity
if (found != null) {
    // 操作 found
}

方案三:使用 Tag 替代 ID 儲存業務資料

// 設定業務資料
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
    }
}

關鍵 API 總結

需求推薦做法避免做法
動態視圖唯一 IDView.generateViewId() (API 17+)硬編碼整數、隨機數
點擊事件取得視圖直接使用 View v 參數findViewById() 尋找
儲存業務資料setTag(Object) / getTag()塞進 ID、用 Map 映射
移除動態視圖parent.removeView(child)設定 GONE、重建整個容器

總結

動態建立視圖時,點擊事件參數即為目標視圖,無需透過 ID 尋找。儲存業務資料請用 setTag(),ID 僅供視圖樹查找使用。善用 View.generateViewId() 避免 ID 衝突,直接操作父容器 removeView() 移除子視圖。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved