You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android音乐播放器MainScreenFragment显示空白问题求助

排查Android音乐播放器Fragment空白问题

我帮你梳理了代码里的几个关键问题,这些都是导致页面空白的核心原因:

1. 核心问题:歌曲列表未填充数据

你的getSongsFromPhone()方法里,虽然遍历了媒体库游标,但没有把创建的歌曲对象添加到返回的ArrayList中,所以最终传给Adapter的是空列表,RecyclerView自然显示空白。

修复这个方法:

fun getSongsFromPhone(): ArrayList<Songs>{
    val arrayList = ArrayList<Songs>()
    val contentResolver = myActivity?.contentResolver ?: return arrayList
    val songUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
    val songCursor = contentResolver.query(songUri, null, null, null, null)

    songCursor?.use { cursor -> // use函数自动关闭游标,避免内存泄漏
        val songId = cursor.getColumnIndex(MediaStore.Audio.Media._ID)
        val songTitle = cursor.getColumnIndex(MediaStore.Audio.Media.TITLE)
        val songArtist = cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST)
        val songData = cursor.getColumnIndex(MediaStore.Audio.Media.DATA)
        val dateIndex = cursor.getColumnIndex(MediaStore.Audio.Media.DATE_ADDED)

        while(cursor.moveToNext()){
            val currentId = cursor.getLong(songId)
            val currentTitle = cursor.getString(songTitle)
            val currentArtist = cursor.getString(songArtist)
            val currentData = cursor.getString(songData)
            val currentDate = cursor.getString(dateIndex)
            // 关键:创建Songs对象并添加到列表
            arrayList.add(Songs(currentId, currentTitle, currentArtist, currentData, currentDate))
        }
    }
    return arrayList
}

2. 缺少动态权限申请

Android 6.0(API 23)及以上版本,读取外部存储需要动态申请READ_EXTERNAL_STORAGE权限,否则无法访问媒体库,游标会返回null,拿不到任何歌曲数据。

在MainActivity的onCreate中添加权限检查:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    
    // 先检查存储权限
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
        != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(
            this,
            arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
            1001 // 自定义请求码
        )
    }
    
    // 其余原有代码...
}

// 处理权限申请结果
override fun onRequestPermissionsResult(
    requestCode: Int,
    permissions: Array<out String>,
    grantResults: IntArray
) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    if (requestCode == 1001 && grantResults.isNotEmpty() 
        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        // 权限通过后刷新Fragment数据
        val fragment = supportFragmentManager.findFragmentByTag("MainScreenFragment") as? MainScreenFragment
        fragment?.refreshSongList()
    }
}

然后在MainScreenFragment中添加刷新方法:

fun refreshSongList() {
    getSongList = getSongsFromPhone()
    _mainScreenAdapter?.notifyDataSetChanged()
    // 处理无歌曲的情况
    if (getSongList.isNullOrEmpty()) {
        visibleLayout?.visibility = View.INVISIBLE
        noSongs?.visibility = View.VISIBLE
    } else {
        visibleLayout?.visibility = View.VISIBLE
        noSongs?.visibility = View.INVISIBLE
    }
}

3. Fragment中重复初始化逻辑

你的onCreateViewonActivityCreated都做了Adapter和RecyclerView的初始化,这会导致重复操作,甚至覆盖数据。建议统一在onViewCreated中处理:

修改MainScreenFragment的生命周期方法:

override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    val view = inflater?.inflate(R.layout.content_main, container, false) ?: return null
    setHasOptionsMenu(true)
    activity?.title = "All songs"
    
    // 初始化控件
    visibleLayout = view.findViewById(R.id.visibleLayout)
    noSongs = view.findViewById(R.id.noSongs)
    nowPlayingBottomBar = view.findViewById(R.id.hiddenBarMainScreen)
    songTitle = view.findViewById(R.id.songTitleMainScreen)
    playPauseButton = view.findViewById(R.id.playpauseButton)
    recyclerView = view.findViewById(R.id.contentMain)
    
    (nowPlayingBottomBar as RelativeLayout).isClickable = false
    return view
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    // 统一初始化列表数据和Adapter
    setupRecyclerView()
}

private fun setupRecyclerView() {
    getSongList = getSongsFromPhone()
    if (getSongList.isNullOrEmpty()) {
        visibleLayout?.visibility = View.INVISIBLE
        noSongs?.visibility = View.VISIBLE
        return
    }
    visibleLayout?.visibility = View.VISIBLE
    noSongs?.visibility = View.INVISIBLE
    
    _mainScreenAdapter = MainScreenAdapter(getSongList as ArrayList<Songs>, requireActivity())
    recyclerView?.apply {
        layoutManager = LinearLayoutManager(requireActivity())
        itemAnimator = DefaultItemAnimator()
        adapter = _mainScreenAdapter
    }
}

4. 其他小问题

  • 冗余的onAttach方法:可以合并成一个适配最新API的版本,避免重复:
override fun onAttach(context: Context) {
    super.onAttach(context)
    myActivity = context as Activity
}
  • 布局加载确认:确保R.layout.content_main中确实包含id为contentMain的RecyclerView,以及visibleLayoutnoSongs等控件,否则findViewById会返回null,导致控件不显示。

最后验证步骤

  1. 确保Songs数据类的构造函数参数和你创建对象时的参数完全匹配。
  2. 首次启动应用时,务必授予存储权限。
  3. 确认设备内存在音频文件(如果没有,noSongs布局应该正常显示)。

内容的提问来源于stack exchange,提问作者Pankaj Chaudhary

火山引擎 最新活动