Android自定义View如何包含TextView?ListItem加载报错求助
解决自定义ListItem的Inflate错误与TextView空指针问题
你的核心问题是subjectNameTextView从未被初始化,始终为null,导致调用setText()或addView()时触发空指针异常。不管继承View还是RelativeLayout,都需要先正确初始化这个TextView。
方案一:使用XML布局定义ListItem结构(推荐)
这种方式将布局与逻辑分离,更易维护。
步骤1:创建ListItem的布局文件
新建res/layout/list_item_layout.xml,定义ListItem的UI结构:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView android:id="@+id/subject_name_tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="16sp" android:padding="16dp"/> </RelativeLayout>
步骤2:修改ListItem类
继承RelativeLayout,在构造方法中加载布局并初始化TextView,同时处理自定义属性(比如你XML里的app:title):
public class ListItem extends RelativeLayout { private String subjectName; private int backgroundColor; private TextView subjectNameTextView; // 必须实现三个构造方法,适配XML inflate场景 public ListItem(Context context) { super(context); init(context, null); } public ListItem(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public ListItem(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs); } private void init(Context context, AttributeSet attrs) { // 加载布局到当前ViewGroup LayoutInflater.from(context).inflate(R.layout.list_item_layout, this, true); // 初始化TextView subjectNameTextView = findViewById(R.id.subject_name_tv); // 读取XML中的自定义属性 if (attrs != null) { TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ListItem); subjectName = ta.getString(R.styleable.ListItem_title); if (subjectName != null) { subjectNameTextView.setText(subjectName); } ta.recycle(); } } public void setSubjectName(String subjectName) { this.subjectName = subjectName; if (subjectNameTextView != null) { subjectNameTextView.setText(subjectName); } } public void setColor(int backgroundColor) { this.backgroundColor = backgroundColor; setBackgroundColor(backgroundColor); } }
步骤3:定义自定义属性
新建res/values/attrs.xml,添加自定义属性:
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="ListItem"> <attr name="title" format="string"/> </declare-styleable> </resources>
方案二:纯代码创建TextView
如果不想用XML布局,直接在代码中创建TextView并添加到ViewGroup:
public class ListItem extends RelativeLayout { private String subjectName; private int backgroundColor; private TextView subjectNameTextView; public ListItem(Context context) { super(context); init(context, null); } public ListItem(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public ListItem(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs); } private void init(Context context, AttributeSet attrs) { // 创建TextView并设置布局参数 subjectNameTextView = new TextView(context); RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); params.setMargins(16, 16, 16, 16); subjectNameTextView.setLayoutParams(params); subjectNameTextView.setTextSize(16); // 添加到当前ViewGroup addView(subjectNameTextView); // 读取XML中的title属性 if (attrs != null) { TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ListItem); subjectName = ta.getString(R.styleable.ListItem_title); if (subjectName != null) { subjectNameTextView.setText(subjectName); } ta.recycle(); } } public void setSubjectName(String subjectName) { this.subjectName = subjectName; if (subjectNameTextView != null) { subjectNameTextView.setText(subjectName); } } public void setColor(int backgroundColor) { this.backgroundColor = backgroundColor; setBackgroundColor(backgroundColor); } }
关键注意点
- 自定义View必须实现三个构造方法,因为XML inflate时会调用带
AttributeSet的构造方法。 - 必须在
init方法中完成TextView的初始化(不管是通过findViewById还是new),确保调用setText()或addView()时对象不为null。 - 自定义属性需要在
attrs.xml中声明,才能在XML布局中使用app:title这类属性。
内容的提问来源于stack exchange,提问作者Atharv Nadkarni
相关产品推荐
相关产品推荐

