From 26d9eabc5f9b980e60514df0417f5e989aa49d11 Mon Sep 17 00:00:00 2001 From: lvmeng <848755140@qq.com> Date: Fri, 6 Mar 2026 14:41:39 +0800 Subject: [PATCH] =?UTF-8?q?feat(activity):=20=E4=BC=98=E5=8C=96=E5=9B=BE?= =?UTF-8?q?=E7=89=87=E5=8A=A0=E8=BD=BD=E4=B8=BA=E5=8D=8F=E7=A8=8B=E5=B9=B6?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=A4=A7=E5=9B=BE=E5=B7=A6=E5=8F=B3=E5=88=87?= =?UTF-8?q?=E6=8D=A2=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- .../activity/LocalImagePreviewActivity.kt | 104 ++++++++++++------ .../res/layout/dialog_image_full_preview.xml | 35 +++++- 2 files changed, 102 insertions(+), 37 deletions(-) diff --git a/app/src/main/java/com/sw/inbound/activity/LocalImagePreviewActivity.kt b/app/src/main/java/com/sw/inbound/activity/LocalImagePreviewActivity.kt index c0d4bf9..1a9e43a 100644 --- a/app/src/main/java/com/sw/inbound/activity/LocalImagePreviewActivity.kt +++ b/app/src/main/java/com/sw/inbound/activity/LocalImagePreviewActivity.kt @@ -8,6 +8,7 @@ import android.view.LayoutInflater import android.view.ViewGroup import android.view.WindowManager import androidx.core.graphics.drawable.toDrawable +import androidx.lifecycle.lifecycleScope import com.sw.inbound.base.BaseActivity import androidx.recyclerview.widget.GridLayoutManager import coil.load @@ -17,6 +18,10 @@ import com.sw.inbound.databinding.ActivityLocalImagePreviewBinding import com.sw.inbound.databinding.DialogImageFullPreviewBinding import com.sw.inbound.databinding.ListItemImagePreviewBinding import com.sw.inbound.dialog.Loading +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import java.io.File class LocalImagePreviewActivity : BaseActivity() { @@ -38,36 +43,41 @@ class LocalImagePreviewActivity : BaseActivity() { /** 从缓存 crop 目录加载所有图片文件 */ fun loadData() { - Thread { - runOnUiThread { - Loading.show(this) - } - val cropFile = File(this.cacheDir, "crop") - cropFile.listFiles()?.forEach { - // 跳过子目录,只处理文件 - if (it.isDirectory) return@forEach - // 跳过非 jpg 图片 - if (!it.name.endsWith(".jpg", ignoreCase = true)) return@forEach - val nameWithoutExt = it.nameWithoutExtension - // 兼容 IMG_CROP_{时间戳} 和其他命名格式 - val displayName = if (nameWithoutExt.startsWith("IMG_CROP_")) { - nameWithoutExt.removePrefix("IMG_CROP_") - } else { - nameWithoutExt + lifecycleScope.launch { + // 主线程显示加载框 + Loading.show(this@LocalImagePreviewActivity) + // 切换到 IO 线程执行文件读取 + withContext(Dispatchers.IO) { + val cropFile = File(this@LocalImagePreviewActivity.cacheDir, "crop") + cropFile.listFiles()?.forEach { + // 跳过子目录,只处理文件 + if (it.isDirectory) return@forEach + // 跳过非 jpg 图片 + if (!it.name.endsWith(".jpg", ignoreCase = true)) return@forEach + val nameWithoutExt = it.nameWithoutExtension + // 兼容 IMG_CROP_{时间戳} 和其他命名格式 + val displayName = if (nameWithoutExt.startsWith("IMG_CROP_")) { + nameWithoutExt.removePrefix("IMG_CROP_") + } else { + nameWithoutExt + } + imageList.add(ImageBean(file = it, name = displayName)) } - imageList.add(ImageBean(file = it, name = displayName)) + // 按文件名升序排列 + imageList.sortBy { it.name } } - // 按文件名升序排列 - imageList.sortBy { it.name } - runOnUiThread { - imageAdapter.notifyDataSetChanged() - Loading.dismiss() - } - }.start() + // 回到主线程刷新列表 + imageAdapter.notifyDataSetChanged() + // 延迟 500ms 后关闭加载框 + delay(500) + Loading.dismiss() + } } - /** 弹出全屏大图预览 Dialog */ - private fun showFullImagePreview(item: ImageBean) { + /** 弹出全屏大图预览 Dialog,支持左右切换 */ + private fun showFullImagePreview(startPosition: Int) { + var currentIndex = startPosition + val dialogBinding = DialogImageFullPreviewBinding.inflate(layoutInflater) val dialog = Dialog(this, android.R.style.Theme_Black_NoTitleBar_Fullscreen) dialog.setContentView(dialogBinding.root) @@ -81,15 +91,37 @@ class LocalImagePreviewActivity : BaseActivity() { ) } - // 加载大图 - dialogBinding.ivFullImage.load(item.file) - // 显示文件名 - dialogBinding.tvFileName.text = item.file.name - // 点击关闭按钮关闭弹窗 - dialogBinding.btnClose.setOnClickListener { dialog.dismiss() } - // 点击图片本身也关闭弹窗 - dialogBinding.ivFullImage.setOnClickListener { dialog.dismiss() } + /** 根据 currentIndex 刷新图片、文件名、页码及按钮状态 */ + fun updateContent() { + val item = imageList[currentIndex] + dialogBinding.ivFullImage.load(item.file) + dialogBinding.tvFileName.text = item.file.name + dialogBinding.tvPageIndicator.text = "${currentIndex + 1} / ${imageList.size}" + // 到达边界时隐藏对应方向的按钮 + dialogBinding.btnPrev.alpha = if (currentIndex > 0) 1f else 0.2f + dialogBinding.btnNext.alpha = if (currentIndex < imageList.size - 1) 1f else 0.2f + } + // 点击图片本身关闭弹窗 + dialogBinding.ivFullImage.setOnClickListener { dialog.dismiss() } + // 关闭按钮 + dialogBinding.btnClose.setOnClickListener { dialog.dismiss() } + // 上一张 + dialogBinding.btnPrev.setOnClickListener { + if (currentIndex > 0) { + currentIndex-- + updateContent() + } + } + // 下一张 + dialogBinding.btnNext.setOnClickListener { + if (currentIndex < imageList.size - 1) { + currentIndex++ + updateContent() + } + } + + updateContent() dialog.show() } @@ -97,8 +129,8 @@ class LocalImagePreviewActivity : BaseActivity() { private val imageAdapter: ImageAdapter by lazy { ImageAdapter(imageList).apply { setOnItemClickListener { _, _, position -> - // 点击缩略图,弹出大图预览 - showFullImagePreview(imageList[position]) + // 点击缩略图,弹出大图预览,传入当前索引以支持左右切换 + showFullImagePreview(position) } } } diff --git a/app/src/main/res/layout/dialog_image_full_preview.xml b/app/src/main/res/layout/dialog_image_full_preview.xml index d4beed2..cb85b6d 100644 --- a/app/src/main/res/layout/dialog_image_full_preview.xml +++ b/app/src/main/res/layout/dialog_image_full_preview.xml @@ -12,7 +12,32 @@ android:layout_marginBottom="120dp" android:scaleType="fitCenter" /> - + + + + + + + + +