feat: pdf viewer
This commit is contained in:
18
module_base/src/main/java/com/lukouguoji/module_base/cache/PdfCacheModel.kt
vendored
Normal file
18
module_base/src/main/java/com/lukouguoji/module_base/cache/PdfCacheModel.kt
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
package com.lukouguoji.module_base.cache
|
||||
|
||||
import dev.DevUtils
|
||||
|
||||
/**
|
||||
* PDF文件缓存管理
|
||||
* 负责PDF文件的下载和本地缓存
|
||||
*/
|
||||
object PdfCacheModel : FileCacheModel() {
|
||||
|
||||
/**
|
||||
* PDF文件缓存目录
|
||||
* 路径: /data/data/com.lukouguoji.aerologic/files/cacheFiles/pdf/
|
||||
*/
|
||||
override fun folderPath(): String {
|
||||
return DevUtils.getContext().filesDir.toString() + "/cacheFiles/pdf/"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
package com.lukouguoji.module_base.ui.page.preview
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.github.barteksc.pdfviewer.listener.OnLoadCompleteListener
|
||||
import com.github.barteksc.pdfviewer.listener.OnPageChangeListener
|
||||
import com.github.barteksc.pdfviewer.listener.OnErrorListener
|
||||
import com.lukouguoji.module_base.R
|
||||
import com.lukouguoji.module_base.base.BaseBindingActivity
|
||||
import com.lukouguoji.module_base.cache.PdfCacheModel
|
||||
import com.lukouguoji.module_base.common.Constant
|
||||
import com.lukouguoji.module_base.databinding.ActivityPdfPreviewBinding
|
||||
import com.lukouguoji.module_base.impl.EmptyViewModel
|
||||
import com.lukouguoji.module_base.ktx.showToast
|
||||
import com.lukouguoji.module_base.util.MediaUtil
|
||||
import dev.utils.app.BarUtils
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* PDF预览界面
|
||||
* 支持在线PDF下载缓存和本地PDF文件预览
|
||||
*/
|
||||
class PdfPreviewActivity : BaseBindingActivity<ActivityPdfPreviewBinding, EmptyViewModel>() {
|
||||
|
||||
// PDF文件路径 (可能是在线URL或本地路径)
|
||||
private var pdfPath: String = ""
|
||||
|
||||
// 当前页码 (从0开始)
|
||||
private var currentPage = 0
|
||||
|
||||
// 总页数
|
||||
private var totalPages = 0
|
||||
|
||||
override fun layoutId() = R.layout.activity_pdf_preview
|
||||
override fun viewModelClass() = EmptyViewModel::class.java
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// 强制横屏显示 (与项目其他页面保持一致)
|
||||
resources?.let {
|
||||
if (it.displayMetrics.widthPixels < it.displayMetrics.heightPixels) {
|
||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||
} else {
|
||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SourceLockedOrientationActivity")
|
||||
override fun initOnCreate(savedInstanceState: Bundle?) {
|
||||
// 设置沉浸式状态栏
|
||||
BarUtils.transparentStatusBar(this)
|
||||
|
||||
// 获取传递的PDF路径
|
||||
pdfPath = intent.getStringExtra(Constant.Key.DATA) ?: ""
|
||||
if (pdfPath.isEmpty()) {
|
||||
showToast("PDF路径为空")
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
// 设置关闭按钮
|
||||
binding.ivClose.setOnClickListener {
|
||||
finish()
|
||||
}
|
||||
|
||||
// 加载PDF文件
|
||||
loadPdfFile()
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载PDF文件 (支持在线和本地)
|
||||
*/
|
||||
private fun loadPdfFile() {
|
||||
// val fullUrl = MediaUtil.fillUrl(pdfPath)
|
||||
val fullUrl = pdfPath
|
||||
|
||||
// 判断是否为在线资源
|
||||
if (isOnlineResource(fullUrl)) {
|
||||
// 在线PDF: 先下载再加载
|
||||
downloadAndLoadPdf(fullUrl)
|
||||
} else {
|
||||
// 本地PDF: 直接加载
|
||||
loadLocalPdf(File(pdfPath))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为在线资源
|
||||
*/
|
||||
private fun isOnlineResource(url: String): Boolean {
|
||||
return url.startsWith("http://") || url.startsWith("https://")
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载在线PDF并加载
|
||||
*/
|
||||
private fun downloadAndLoadPdf(url: String) {
|
||||
// 显示Loading
|
||||
showLoading()
|
||||
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
// 使用PdfCacheModel下载PDF
|
||||
PdfCacheModel.saveFile(
|
||||
url = url,
|
||||
onProgress = { progress ->
|
||||
// 更新下载进度
|
||||
lifecycleScope.launch(Dispatchers.Main) {
|
||||
binding.tvLoadingTip.text = "正在下载PDF... $progress%"
|
||||
}
|
||||
},
|
||||
onSuccess = { file ->
|
||||
// 下载成功,切换到主线程加载PDF
|
||||
lifecycleScope.launch(Dispatchers.Main) {
|
||||
hideLoading()
|
||||
loadLocalPdf(file)
|
||||
}
|
||||
},
|
||||
onError = { errorMsg ->
|
||||
// 下载失败
|
||||
lifecycleScope.launch(Dispatchers.Main) {
|
||||
hideLoading()
|
||||
showToast("PDF下载失败: $errorMsg")
|
||||
finish()
|
||||
}
|
||||
}
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
hideLoading()
|
||||
showToast("PDF下载异常: ${e.message}")
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载本地PDF文件
|
||||
*/
|
||||
private fun loadLocalPdf(file: File) {
|
||||
if (!file.exists()) {
|
||||
showToast("PDF文件不存在")
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
binding.pdfView.fromFile(file)
|
||||
.defaultPage(0) // 默认显示第一页
|
||||
.enableSwipe(true) // 启用滑动翻页
|
||||
.swipeHorizontal(false) // 垂直滑动
|
||||
.enableDoubletap(true) // 启用双击缩放
|
||||
.enableAnnotationRendering(false) // 禁用注释渲染(提升性能)
|
||||
.password(null) // PDF密码(如需要)
|
||||
.scrollHandle(null) // 禁用默认滚动条
|
||||
.onLoad(OnLoadCompleteListener { nbPages ->
|
||||
// PDF加载完成
|
||||
totalPages = nbPages
|
||||
updatePageIndicator()
|
||||
})
|
||||
.onPageChange(OnPageChangeListener { page, pageCount ->
|
||||
// 页面切换
|
||||
currentPage = page
|
||||
updatePageIndicator()
|
||||
})
|
||||
.onError(OnErrorListener { t ->
|
||||
// 加载错误
|
||||
showToast("PDF加载失败: ${t.message}")
|
||||
finish()
|
||||
})
|
||||
.load()
|
||||
} catch (e: Exception) {
|
||||
showToast("PDF渲染异常: ${e.message}")
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新页码指示器
|
||||
*/
|
||||
private fun updatePageIndicator() {
|
||||
binding.tvPageIndicator.text = "${currentPage + 1} / $totalPages"
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示Loading
|
||||
*/
|
||||
private fun showLoading() {
|
||||
binding.loadingLayout.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏Loading
|
||||
*/
|
||||
private fun hideLoading() {
|
||||
binding.loadingLayout.visibility = View.GONE
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* 启动PDF预览
|
||||
* @param context 上下文
|
||||
* @param pdfPath PDF路径 (支持在线URL或本地路径)
|
||||
*/
|
||||
@JvmStatic
|
||||
fun start(context: Context, pdfPath: String) {
|
||||
val starter = Intent(context, PdfPreviewActivity::class.java)
|
||||
.putExtra(Constant.Key.DATA, pdfPath)
|
||||
context.startActivity(starter)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user