Java迁移Kotlin后Spinner适配器报非空参数为null的NPE问题
问题原因
你遇到的空指针异常是Kotlin与Java互操作时空安全声明不匹配导致的:
BaseAdapter是Java编写的Android框架类,其定义的getView、getDropDownView方法中,用于视图复用的convertView参数是可空类型:列表首次渲染、没有可复用视图时,系统会传入null,要求适配器自行加载布局。- 迁移后的Kotlin代码将这两个方法的复用视图参数声明为非空
View类型,Kotlin编译器会自动在方法入口插入非空校验,系统传入null时就会直接抛出你看到的Intrinsics.checkNotNullParameter异常。
除此之外代码还有几个会触发运行时错误的隐患:
getItem方法的返回值在父类中是可空类型,声明为非空Any还使用TODO占位,调用到该方法时会直接抛出未实现异常。roundCorners方法中直接将parent.parent强转为View,没有做非空判断,父级类型不匹配或为null时会触发类型转换异常。- 构造函数中
Context参数声明为可空,和实际调用时传入非空Activity的场景不匹配,存在空安全隐患。
修复方案
按照以下步骤调整代码,和原Java逻辑保持完全一致即可解决异常:
- 调整
getView、getDropDownView的方法签名,将复用视图参数声明为可空View?,和父类方法签名对齐。 - 修正
getItem方法,将返回值改为可空Any?,直接返回null和原Java逻辑对齐,删除TODO占位。 - 修正
roundCorners方法的类型强转逻辑,使用安全类型转换,保留原Java的非空判断逻辑。 - 调整构造函数参数,将Context声明为非空类型,移除不必要的临时参数映射。
修正后的完整适配器代码
import android.content.Context import android.graphics.Color import android.graphics.drawable.GradientDrawable import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.BaseAdapter import android.widget.TextView // 替换为你自己项目对应类的包路径即可 internal class CustomSubjectSpinnerAdapter( applicationContext: Context, private val subjectOptions: Array<String> ) : BaseAdapter() { private val inflter: LayoutInflater = LayoutInflater.from(applicationContext) override fun getCount(): Int { return subjectOptions.size } override fun getItem(i: Int): Any? { return null } override fun getItemId(i: Int): Long { return 0 } override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View { var realConvertView = convertView if (realConvertView == null) { val vi = parent.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater realConvertView = vi.inflate(R.layout.subject_spinner_row, null) } val llLayout = super.getDropDownView(position, realConvertView, parent) roundCorners(parent) return llLayout } private fun roundCorners(parent: ViewGroup) { val container2Round = parent.parent as? View if (container2Round != null) { val gd = GradientDrawable( GradientDrawable.Orientation.TOP_BOTTOM, intArrayOf(Color.WHITE, Color.WHITE) ) val radius = AWScreen.dp2px(10) gd.cornerRadius = radius.toFloat() container2Round.background = gd } } override fun getView(i: Int, view: View?, viewGroup: ViewGroup): View { // 保留原Java每次inflate新布局的逻辑,和原有行为完全一致 val itemView = inflter.inflate(R.layout.subject_spinner_row, null) val tvSubject = itemView.findViewById<TextView>(R.id.subject) tvSubject.text = subjectOptions[i] if (subjectOptions[i] == "") { tvSubject.visibility = View.GONE } setListItemRoundedCorners(tvSubject, i) if (i == subjectOptions.size - 1) { val padding = AWScreen.sp2px(10f, itemView.context) tvSubject.setPadding(padding, padding, padding, padding) } return itemView } private fun setListItemRoundedCorners(tvSubject: TextView, i: Int) { if (i == 1) { tvSubject.setBackgroundResource(R.drawable.rounded_border_top) } else if (i == 2) { tvSubject.setBackgroundResource(R.drawable.rounded_border_bottom) } } }
注:上述代码完全保留了原Java版本的逻辑(包括每次getView重新inflate布局、圆角设置逻辑、返回值逻辑),不会改变原有UI表现,仅修复了Kotlin互操作导致的空指针问题。
内容的提问来源于stack exchange,提问作者Diego Perez
相关产品推荐
相关产品推荐

