You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.13 22:15:02