Kotlin中RecyclerView设置LayoutManager触发NullPointerException问题求助
问题原因
- 生命周期调用顺序错误:Fragment 的
onCreate回调执行时机早于onCreateView,你在onCreate中调用loadTrackData()操作 RecyclerView 时,布局还未完成 inflate 加载,kotlinx.android.synthetic生成的控件引用recycler_view为空,直接调用方法就触发了空指针异常。 - 适配器存在类型转换隐患:自定义 ViewHolder 的构造参数错误使用了
ActionMenuItemView类型,实际 inflate 的是普通自定义 item 布局,后续运行会触发类型转换异常。
修复方案
1. 调整加载逻辑的调用时机
将权限校验、列表加载逻辑从onCreate移到onViewCreated回调中,onViewCreated会在布局加载完成后触发,此时控件引用已有效。
修改后的 LoadFragment 核心代码示例:
class LoadFragment : Fragment() { var trackData:ArrayList<TrackData> = ArrayList() var trackListAdapter:TrackListAdapter?=null companion object{ val PERMISSION_REQUEST_CODE = 12 } // 清空原有onCreate里的逻辑 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) } // 新增onViewCreated方法,把逻辑移到这里 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 提前初始化LayoutManager和Adapter,避免重复创建 recycler_view.layoutManager = LinearLayoutManager(requireContext()) trackListAdapter = TrackListAdapter(trackData) recycler_view.adapter = trackListAdapter if (ContextCompat.checkSelfPermission(requireContext(),Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){ requestPermissions(arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE), PERMISSION_REQUEST_CODE) }else{ loadTrackData() } } fun loadTrackData(){ var trackCursor:Cursor? = requireContext().contentResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null,null,null,null) trackData.clear() while (trackCursor!=null && trackCursor.moveToNext()){ var trackTitle = trackCursor.getString(trackCursor.getColumnIndex(MediaStore.Audio.Media.TITLE)) var trackLength = trackCursor.getString(trackCursor.getColumnIndex(MediaStore.Audio.Media.DURATION)) trackData.add(TrackData(trackTitle,trackLength)) } trackCursor?.close() // 数据变更后通知适配器刷新即可,不用重复设置LayoutManager和Adapter trackListAdapter?.notifyDataSetChanged() } override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<out String>, grantResults: IntArray ) { if (requestCode == PERMISSION_REQUEST_CODE){ if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED){ Toast.makeText(requireContext(),"Permission Granted",Toast.LENGTH_SHORT).show() loadTrackData() } } } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { return inflater.inflate(R.layout.fragment_load, container, false) } }
2. 修复适配器类型错误
将 ViewHolder 构造参数改为普通View类型,去掉错误的强转逻辑,修改后的适配器核心代码:
class TrackListAdapter(TrackData:ArrayList<TrackData>):RecyclerView.Adapter<TrackListAdapter.TrackListViewHolder>() { val dtrackData = TrackData // 构造参数改为View类型 class TrackListViewHolder(itemView: View):RecyclerView.ViewHolder(itemView){ var titleTV:TextView = itemView.findViewById(R.id.track_title_view) var lengthTV:TextView = itemView.findViewById(R.id.track_length_view) var artTV:ImageView = itemView.findViewById(R.id.track_art_view) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TrackListViewHolder { var view = LayoutInflater.from(parent.context).inflate(R.layout.load_item,parent,false) // 去掉强转逻辑 return TrackListViewHolder(view) } override fun onBindViewHolder(holder: TrackListViewHolder, position: Int) { var data = dtrackData[position] holder.titleTV.text = data.dTrackTitle holder.lengthTV.text = data.dTrackLength } override fun getItemCount(): Int { return dtrackData.size } }
额外注意点
- 用完 Cursor 记得调用
close()方法释放资源,避免内存泄漏 - Fragment 中尽量用
requireContext()/requireActivity()替代可选类型的context/activity,避免空判断冗余
内容的提问来源于stack exchange,提问作者DD1515
相关产品推荐
相关产品推荐

