// Java 代碼
public class MyRadioGroup extends RadioGroup { ... }
// Activity 中
MyRadioGroup group = findViewById(R.id.radioGroup); // ❌ ClassCastException
// 錯誤信息:android.widget.RadioGroup cannot be cast to com.xxx.MyRadioGroup
XML 布局文件中聲明的是父類標籤 `
<RadioGroup
android:id="@+id/radioGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<com.xxx.widget.MyRadioGroup
android:id="@+id/radioGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
關鍵點: 標籤名必須為 完整包名 + 類名,系統會反射實例化你的子類,`findViewById` 直接返回子類型,無需強轉。
<!-- res/values/attrs.xml -->
<declare-styleable name="MyRadioGroup">
<attr name="customColor" format="color" />
<attr name="orientation" format="enum">
<enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
</attr>
</declare-styleable>
<!-- 布局中使用 -->
<com.xxx.widget.MyRadioGroup
xmlns:app="https://schemas.android.com/apk/res-auto"
android:id="@+id/radioGroup"
app:customColor="#FF5722"
app:orientation="horizontal" />
// Java 構造方法讀取屬性
public MyRadioGroup(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyRadioGroup);
int color = a.getColor(R.styleable.MyRadioGroup_customColor, Color.BLACK);
int orientation = a.getInt(R.styleable.MyRadioGroup_orientation, HORIZONTAL);
a.recycle();
// 初始化邏輯...
}
| 誤區 | 正確做法 |
|---|---|
| Java 代碼強轉 `(MyRadioGroup) findViewById(...)` | XML 標籤改為子類完整類名 |
| 只寫類名 ` | 必須寫完整包名 ` |
| 自定義 View 未提供 `Context, AttributeSet` 構造器 | 必須實現雙參構造器,否則 XML 膨脹失敗 |
| 庫模組中自定義 View,主模組引用 | 包名以庫模組 `package` 為準,不可用主模組包名 |
Android 視圖膨脹機制按 XML 標籤名實例化類。**自定義控件必須在 XML 中聲明完整類名**,系統才會創建子類實例。這是 View 系統的基礎契約,同理適用於所有繼承 `View`/`ViewGroup` 的自定義組件。