feat: pdf viewer
This commit is contained in:
@@ -49,7 +49,8 @@
|
|||||||
"Bash(while IFS= read -r file)",
|
"Bash(while IFS= read -r file)",
|
||||||
"Bash(do if ! grep -q \"import com.lukouguoji.module_base.ktx.formatDate\" \"$file\")",
|
"Bash(do if ! grep -q \"import com.lukouguoji.module_base.ktx.formatDate\" \"$file\")",
|
||||||
"Bash(then sed -i '' '/import dev.utils.common.DateUtils/a\\\\\nimport com.lukouguoji.module_base.ktx.formatDate\n' \"$file\" echo \"Added formatDate import to: $file\" fi done)",
|
"Bash(then sed -i '' '/import dev.utils.common.DateUtils/a\\\\\nimport com.lukouguoji.module_base.ktx.formatDate\n' \"$file\" echo \"Added formatDate import to: $file\" fi done)",
|
||||||
"Bash(identify:*)"
|
"Bash(identify:*)",
|
||||||
|
"WebFetch(domain:github.com)"
|
||||||
],
|
],
|
||||||
"deny": [],
|
"deny": [],
|
||||||
"ask": []
|
"ask": []
|
||||||
|
|||||||
@@ -138,6 +138,9 @@ dependencies {
|
|||||||
api 'io.github.lucksiege:pictureselector:v3.11.2'
|
api 'io.github.lucksiege:pictureselector:v3.11.2'
|
||||||
api "com.github.bumptech.glide:glide:4.15.1"
|
api "com.github.bumptech.glide:glide:4.15.1"
|
||||||
|
|
||||||
|
// PDF预览库
|
||||||
|
api 'com.github.barteksc:android-pdf-viewer:2.8.2'
|
||||||
|
|
||||||
|
|
||||||
// DevApp - Android 工具类库
|
// DevApp - Android 工具类库
|
||||||
api 'io.github.afkt:DevAppX:2.4.0'
|
api 'io.github.afkt:DevAppX:2.4.0'
|
||||||
|
|||||||
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,6 +27,13 @@
|
|||||||
android:configChanges="orientation|keyboardHidden"
|
android:configChanges="orientation|keyboardHidden"
|
||||||
android:exported="true"/>
|
android:exported="true"/>
|
||||||
|
|
||||||
|
<!-- PDF预览Activity -->
|
||||||
|
<activity
|
||||||
|
android:name=".ui.page.preview.PdfPreviewActivity"
|
||||||
|
android:configChanges="orientation|keyboardHidden"
|
||||||
|
android:exported="false"
|
||||||
|
android:screenOrientation="userLandscape" />
|
||||||
|
|
||||||
<!-- 屏幕适配 -->
|
<!-- 屏幕适配 -->
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="design_width_in_dp"
|
android:name="design_width_in_dp"
|
||||||
|
|||||||
86
module_base/src/main/res/layout/activity_pdf_preview.xml
Normal file
86
module_base/src/main/res/layout/activity_pdf_preview.xml
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<data>
|
||||||
|
<variable
|
||||||
|
name="viewModel"
|
||||||
|
type="com.lukouguoji.module_base.impl.EmptyViewModel" />
|
||||||
|
</data>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/black">
|
||||||
|
|
||||||
|
<!-- PDF视图 -->
|
||||||
|
<com.github.barteksc.pdfviewer.PDFView
|
||||||
|
android:id="@+id/pdfView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
<!-- Loading蒙层 (下载时显示) -->
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/loadingLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/black_tran50"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:background="@color/black_tran70"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="20dp">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:indeterminateTint="@color/white" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvLoadingTip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:text="正在加载PDF..."
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<!-- 页码指示器 -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvPageIndicator"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentBottom="true"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_marginBottom="30dp"
|
||||||
|
android:background="@color/black_tran50"
|
||||||
|
android:paddingLeft="15dp"
|
||||||
|
android:paddingTop="5dp"
|
||||||
|
android:paddingRight="15dp"
|
||||||
|
android:paddingBottom="5dp"
|
||||||
|
android:text="1 / 10"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<!-- 关闭按钮 -->
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/ivClose"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:layout_marginTop="60dp"
|
||||||
|
android:layout_marginRight="30dp"
|
||||||
|
android:background="@color/black_tran30"
|
||||||
|
android:padding="4dp"
|
||||||
|
android:src="@drawable/img_close" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
</layout>
|
||||||
@@ -41,4 +41,9 @@
|
|||||||
|
|
||||||
<color name="bottom_tool_tips_text_color">#797979</color>
|
<color name="bottom_tool_tips_text_color">#797979</color>
|
||||||
|
|
||||||
|
<!-- 半透明黑色 (用于PDF预览) -->
|
||||||
|
<color name="black_tran30">#4D000000</color> <!-- 30%透明度 -->
|
||||||
|
<color name="black_tran50">#80000000</color> <!-- 50%透明度 -->
|
||||||
|
<color name="black_tran70">#B3000000</color> <!-- 70%透明度 -->
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
@@ -17,6 +17,7 @@ import com.lukouguoji.module_base.ktx.showConfirmDialog
|
|||||||
import com.lukouguoji.module_base.ktx.showToast
|
import com.lukouguoji.module_base.ktx.showToast
|
||||||
import com.lukouguoji.module_base.ktx.toRequestBody
|
import com.lukouguoji.module_base.ktx.toRequestBody
|
||||||
import com.lukouguoji.module_base.ui.page.preview.PreviewActivity
|
import com.lukouguoji.module_base.ui.page.preview.PreviewActivity
|
||||||
|
import com.lukouguoji.module_base.ui.page.preview.PdfPreviewActivity
|
||||||
import com.lukouguoji.module_base.util.MediaUtil
|
import com.lukouguoji.module_base.util.MediaUtil
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@@ -154,9 +155,9 @@ class GjcInspectionDetailsViewModel : BaseViewModel() {
|
|||||||
)
|
)
|
||||||
PreviewActivity.start(getTopActivity(), listOf(fileBean))
|
PreviewActivity.start(getTopActivity(), listOf(fileBean))
|
||||||
}
|
}
|
||||||
// PDF格式:提示暂不支持
|
// PDF格式:使用PdfPreviewActivity预览
|
||||||
attach.name.endsWith(".pdf", true) -> {
|
attach.name.endsWith(".pdf", true) -> {
|
||||||
showToast("PDF文件预览功能开发中")
|
PdfPreviewActivity.start(getTopActivity(), attach.path)
|
||||||
}
|
}
|
||||||
// 其他格式
|
// 其他格式
|
||||||
else -> {
|
else -> {
|
||||||
|
|||||||
Reference in New Issue
Block a user