如何使用Glide将Firebase图片下载至设备内部存储并解决报错
功能需求
现有基于Android的RecyclerView组件,通过Glide加载Firebase实时数据库存储的图片。RecyclerView每个列表项内置下载按钮,需要实现点击按钮时,将对应位置的Firebase图片下载并保存到设备本地存储的功能。
现有代码
适配器类(Adapter class)
class NatureAdapter(private val mContext: Context, private val natureList: ArrayList<Nature>) : RecyclerView.Adapter<NatureAdapter.ViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.nature_image_view, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { Glide.with(mContext) .load(natureList[position].space) .into(holder.imageView) } override fun getItemCount(): Int { return natureList.size } inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { var imageView: ImageView = itemView.findViewById(R.id.NatureView) } companion object { private const val Tag = "RecyclerView" } }
数据实体类(Data list class)
class Nature (var space: String? = null) { }
现存编译报错
- 第一个报错
e: C:\Users\Khumo Kwezi Mashapa\AndroidStudioProjects\MyNotepad\app\src\main\java\com\khumomashapa\notes\fragments\NatureWallpapers.kt: (65, 17): Unresolved reference: viewModel
- 第二个报错
e: C:\Users\Khumo Kwezi Mashapa\AndroidStudioProjects\MyNotepad\app\src\main\java\com\khumomashapa\notes\fragments\NatureWallpapers.kt: (120, 93): No value passed for parameter 'downloadImage'
实现方案与报错解决
报错根因
Unresolved reference: viewModel:NatureWallpapersFragment中未初始化viewModel变量就直接调用,编译器找不到对应引用。No value passed for parameter 'downloadImage':调用函数/类构造方法时,没有给名为downloadImage的必传参数传入对应值。
完整功能实现
1. 补全适配器逻辑
首先在列表项布局中确认下载按钮存在,id设为downloadBtn,修改适配器代码补充点击下载逻辑,同时适配不同Android版本的存储权限规则:
class NatureAdapter( private val mContext: Context, private val natureList: ArrayList<Nature> ) : RecyclerView.Adapter<NatureAdapter.ViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val view = LayoutInflater.from(parent.context) .inflate(R.layout.nature_image_view, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val currentImageUrl = natureList[position].space Glide.with(mContext) .load(currentImageUrl) .into(holder.imageView) holder.downloadBtn.setOnClickListener { currentImageUrl?.let { url -> if (checkStoragePermission(mContext)) { saveImageToLocal(mContext, url) } else { (mContext as? AppCompatActivity)?.let { ActivityCompat.requestPermissions( it, getRequiredPermissions(), REQUEST_STORAGE_CODE ) } } } } } override fun getItemCount() = natureList.size inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val imageView: ImageView = itemView.findViewById(R.id.NatureView) val downloadBtn: Button = itemView.findViewById(R.id.downloadBtn) } companion object { private const val Tag = "RecyclerView" private const val REQUEST_STORAGE_CODE = 1001 } }
2. 添加工具方法
在适配器同级或公共工具类中添加权限检查、图片下载相关方法:
// 获取当前系统版本需要的存储权限 private fun getRequiredPermissions(): Array<String> { return when { Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> arrayOf(Manifest.permission.READ_MEDIA_IMAGES) Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> emptyArray() // Android10+使用分区存储,无需写存储权限 else -> arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE) } } // 存储权限检查 private fun checkStoragePermission(context: Context): Boolean { return getRequiredPermissions().all { ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED } } // 下载图片到本地并通知系统相册更新 private fun saveImageToLocal(context: Context, imageUrl: String) { CoroutineScope(Dispatchers.IO).launch { runCatching { val saveDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) val targetFile = File(saveDir, "Nature_${System.currentTimeMillis()}.jpg") // 用Glide下载原图 Glide.with(context) .asFile() .load(imageUrl) .submit(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL) .get() .copyTo(targetFile, overwrite = true) // 扫描文件让相册识别 MediaScannerConnection.scanFile( context, arrayOf(targetFile.absolutePath), arrayOf("image/jpeg"), null ) withContext(Dispatchers.Main) { Toast.makeText(context, "图片下载完成", Toast.LENGTH_SHORT).show() } }.onFailure { e -> withContext(Dispatchers.Main) { Toast.makeText(context, "下载失败: ${e.message}", Toast.LENGTH_SHORT).show() } } } }
3. 修复编译报错
- 修复
Unresolved reference: viewModel- 如果需要使用ViewModel,在
NatureWallpapersFragment中先初始化对应ViewModel实例:// 在Fragment类内部声明 private val viewModel: NatureWallpapersViewModel by viewModels() - 如果不需要使用ViewModel,直接删除Fragment第65行调用
viewModel的代码即可。
- 如果需要使用ViewModel,在
- 修复
No value passed for parameter 'downloadImage'
定位到Fragment第120行代码,找到调用时缺少downloadImage参数的位置:- 如果该参数是多余的,直接在对应函数/构造方法定义中删除该必传参数,或给参数设置默认值
= {} - 如果该参数是需要的,传入对应实现即可,示例:
// 错误写法(漏传参数) val adapter = NatureAdapter(requireContext(), natureList) // 正确写法(传入downloadImage参数实现) val adapter = NatureAdapter( mContext = requireContext(), natureList = natureList, downloadImage = { url -> saveImageToLocal(requireContext(), url) } )
- 如果该参数是多余的,直接在对应函数/构造方法定义中删除该必传参数,或给参数设置默认值
注意:需要在AndroidManifest.xml中补充对应存储权限声明,否则下载功能无法正常使用。
内容的提问来源于stack exchange,提问作者Kwezi Mashapa
相关产品推荐
相关产品推荐

