2 Commits
Author SHA1 Message Date
lvmengandClaude Sonnet 4.6 26d9eabc5f feat(activity): 优化图片加载为协程并新增大图左右切换功能
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 14:41:39 +08:00
lvmengandClaude Sonnet 4.6 301b91581b feat(activity): 新增本地图片预览功能及大图查看入口
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 14:22:38 +08:00
4 changed files with 195 additions and 25 deletions
@@ -210,6 +210,11 @@ class FoodCollectionActivity : BaseActivity() {
loadEmptyView() loadEmptyView()
// 点击"图片"按钮,跳转至本地图片预览页
binding.tvImagePreview.setOnClickListener {
startActivity<LocalImagePreviewActivity> { }
}
binding.tvTitle.setOnClickListener { binding.tvTitle.setOnClickListener {
// startActivity<LocalImagePreviewActivity> { } // startActivity<LocalImagePreviewActivity> { }
@@ -1,18 +1,27 @@
package com.sw.inbound.activity package com.sw.inbound.activity
import android.app.Dialog
import android.content.Context import android.content.Context
import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup 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 com.sw.inbound.base.BaseActivity
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import coil.load import coil.load
import com.chad.library.adapter4.BaseQuickAdapter import com.chad.library.adapter4.BaseQuickAdapter
import com.chad.library.adapter4.viewholder.QuickViewHolder import com.chad.library.adapter4.viewholder.QuickViewHolder
import com.sw.inbound.databinding.ActivityLocalImagePreviewBinding import com.sw.inbound.databinding.ActivityLocalImagePreviewBinding
import com.sw.inbound.databinding.DialogImageFullPreviewBinding
import com.sw.inbound.databinding.ListItemImagePreviewBinding import com.sw.inbound.databinding.ListItemImagePreviewBinding
import com.sw.inbound.dialog.Loading import com.sw.inbound.dialog.Loading
import com.sw.inbound.utils.ext.copyText import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File import java.io.File
class LocalImagePreviewActivity : BaseActivity() { class LocalImagePreviewActivity : BaseActivity() {
@@ -32,39 +41,96 @@ class LocalImagePreviewActivity : BaseActivity() {
loadData() loadData()
} }
/** 从缓存 crop 目录加载所有图片文件 */
fun loadData() { fun loadData() {
Thread { lifecycleScope.launch {
runOnUiThread { // 主线程显示加载框
Loading.show(this) Loading.show(this@LocalImagePreviewActivity)
} // 切换到 IO 线程执行文件读取
val cropFile = File(this.cacheDir, "crop") withContext(Dispatchers.IO) {
cropFile.listFiles()?.forEach { val cropFile = File(this@LocalImagePreviewActivity.cacheDir, "crop")
if (it.isDirectory) { cropFile.listFiles()?.forEach {
return@forEach // 跳过子目录,只处理文件
} if (it.isDirectory) return@forEach
val start = "IMG_CROP_".length // 跳过非 jpg 图片
val end = it.name.indexOf(".jpg") if (!it.name.endsWith(".jpg", ignoreCase = true)) return@forEach
val time = it.name.substring(start, end).toLong() val nameWithoutExt = it.nameWithoutExtension
if (time >= 1764086400L) { // 兼容 IMG_CROP_{时间戳} 和其他命名格式
imageList.add(ImageBean(file = it, name = "$time")) val displayName = if (nameWithoutExt.startsWith("IMG_CROP_")) {
nameWithoutExt.removePrefix("IMG_CROP_")
} else {
nameWithoutExt
}
imageList.add(ImageBean(file = it, name = displayName))
} }
// 按文件名升序排列
imageList.sortBy { it.name }
} }
imageList.sortBy { it.name.toLong() } // 回到主线程刷新列表
runOnUiThread { imageAdapter.notifyDataSetChanged()
imageAdapter.notifyDataSetChanged() // 延迟 500ms 后关闭加载框
Loading.dismiss() delay(500)
} Loading.dismiss()
}.start() }
} }
private var isCycle = true /** 弹出全屏大图预览 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)
dialog.setCancelable(true)
dialog.setCanceledOnTouchOutside(true)
dialog.window?.apply {
setBackgroundDrawable(Color.TRANSPARENT.toDrawable())
setLayout(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT
)
}
/** 根据 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()
}
private val imageList = mutableListOf<ImageBean>() private val imageList = mutableListOf<ImageBean>()
private val imageAdapter: ImageAdapter by lazy { private val imageAdapter: ImageAdapter by lazy {
ImageAdapter(imageList).apply { ImageAdapter(imageList).apply {
setOnItemClickListener { _, _, position -> setOnItemClickListener { _, _, position ->
imageList[position].name.copyText(this@LocalImagePreviewActivity) // 点击缩略图,弹出大图预览,传入当前索引以支持左右切换
showFullImagePreview(position)
} }
} }
} }
@@ -20,6 +20,19 @@
android:textColor="#141428" android:textColor="#141428"
android:textSize="36sp" android:textSize="36sp"
android:textStyle="bold" /> android:textStyle="bold" />
<TextView
android:id="@+id/tvImagePreview"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical|end"
android:layout_marginEnd="40dp"
android:gravity="center_vertical"
android:paddingHorizontal="20dp"
android:text="图片"
android:textColor="#141428"
android:textSize="32sp"
android:textStyle="bold" />
</FrameLayout> </FrameLayout>
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#CC000000">
<ImageView
android:id="@+id/ivFullImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="120dp"
android:scaleType="fitCenter" />
<!-- 左侧翻页按钮 -->
<ImageView
android:id="@+id/btnPrev"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_gravity="center_vertical|start"
android:layout_marginBottom="120dp"
android:layout_marginStart="20dp"
android:padding="16dp"
android:src="@mipmap/ic_back_white2"
android:background="#55000000" />
<!-- 右侧翻页按钮 -->
<ImageView
android:id="@+id/btnNext"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_gravity="center_vertical|end"
android:layout_marginBottom="120dp"
android:layout_marginEnd="20dp"
android:padding="16dp"
android:src="@mipmap/ic_back_white2"
android:rotation="180"
android:background="#55000000" />
<!-- 底部工具栏:文件名 + 页码指示器 + 关闭按钮 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="120dp"
android:layout_gravity="bottom"
android:background="#99000000"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingHorizontal="30dp">
<TextView
android:id="@+id/tvFileName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="middle"
android:maxLines="1"
android:textColor="#FFFFFFFF"
android:textSize="26sp" />
<TextView
android:id="@+id/tvPageIndicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="30dp"
android:textColor="#CCFFFFFF"
android:textSize="26sp" />
<FrameLayout
android:id="@+id/btnClose"
android:layout_width="200dp"
android:layout_height="80dp"
android:background="@drawable/bg_blue_ripple">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="关闭"
android:textColor="@color/white"
android:textSize="30sp"
android:textStyle="bold" />
</FrameLayout>
</LinearLayout>
</FrameLayout>