添加护眼模式

优化全屏阅读文档展示
This commit is contained in:
ocean 2025-09-12 17:59:07 +08:00
parent 56ce37d303
commit 5f1990f830
10 changed files with 220 additions and 75 deletions

View File

@ -20,9 +20,14 @@ class AppStore(context: Context) {
key = DOCUMENT_SORT_TYPE, defaultValue = SortConfig.default().toPreferenceString() key = DOCUMENT_SORT_TYPE, defaultValue = SortConfig.default().toPreferenceString()
) )
var isEyeCareMode: Boolean by store.boolean(
key = IS_EYE_CARE_MODE, defaultValue = false
)
companion object { companion object {
private const val FILE_NAME = "prp_sp_name" private const val FILE_NAME = "prp_sp_name"
private const val PERMISSIONS_DIALOG_PROMPT = "permissions_dialog_prompt" private const val PERMISSIONS_DIALOG_PROMPT = "permissions_dialog_prompt"
private const val DOCUMENT_SORT_TYPE = "document_sort_type" private const val DOCUMENT_SORT_TYPE = "document_sort_type"
private const val IS_EYE_CARE_MODE = "is_eye_care_mode"
} }
} }

View File

@ -52,7 +52,7 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback
setContentView(binding.root) setContentView(binding.root)
initObserve() initObserve()
ImmersionBar.with(this).statusBarView(binding.view).statusBarDarkFont(true) ImmersionBar.with(this).statusBarView(binding.view).statusBarDarkFont(true)
.navigationBarColor(R.color.black).init() .navigationBarColor(R.color.white).init()
setupFragments() setupFragments()
setupNavigation() setupNavigation()

View File

@ -3,11 +3,12 @@ package com.all.pdfreader.pro.app.ui.act
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.MotionEvent
import android.view.View
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.all.pdfreader.pro.app.R import com.all.pdfreader.pro.app.R
import com.all.pdfreader.pro.app.databinding.ActivityPdfViewBinding import com.all.pdfreader.pro.app.databinding.ActivityPdfViewBinding
import com.all.pdfreader.pro.app.model.FileActionEvent
import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity
import com.all.pdfreader.pro.app.ui.dialog.PdfPasswordProtectionDialogFragment import com.all.pdfreader.pro.app.ui.dialog.PdfPasswordProtectionDialogFragment
import com.all.pdfreader.pro.app.ui.view.CustomScrollHandle import com.all.pdfreader.pro.app.ui.view.CustomScrollHandle
@ -15,11 +16,14 @@ import com.all.pdfreader.pro.app.viewmodel.PdfViewModel
import com.github.barteksc.pdfviewer.listener.OnErrorListener import com.github.barteksc.pdfviewer.listener.OnErrorListener
import com.github.barteksc.pdfviewer.listener.OnLoadCompleteListener import com.github.barteksc.pdfviewer.listener.OnLoadCompleteListener
import com.github.barteksc.pdfviewer.listener.OnPageChangeListener import com.github.barteksc.pdfviewer.listener.OnPageChangeListener
import com.github.barteksc.pdfviewer.listener.OnTapListener
import com.gyf.immersionbar.BarHide
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.io.File import java.io.File
class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeListener, class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeListener,
OnErrorListener { OnErrorListener, OnTapListener {
override val TAG: String = "PdfViewActivity" override val TAG: String = "PdfViewActivity"
companion object { companion object {
@ -37,10 +41,14 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList
private lateinit var pdfDocument: PdfDocumentEntity private lateinit var pdfDocument: PdfDocumentEntity
private val viewModel by lazy { ViewModelProvider(this)[PdfViewModel::class.java] } private val viewModel by lazy { ViewModelProvider(this)[PdfViewModel::class.java] }
private val repository = getRepository() private val repository = getRepository()
private var isFullScreen = false
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityPdfViewBinding.inflate(layoutInflater) binding = ActivityPdfViewBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
ImmersionBar.with(this).statusBarView(binding.view).statusBarDarkFont(true)
.navigationBarColor(R.color.white).init()
initObserve() initObserve()
val filePath = intent.getStringExtra(EXTRA_PDF_HASH) val filePath = intent.getStringExtra(EXTRA_PDF_HASH)
?: throw IllegalArgumentException("PDF file hash is required") ?: throw IllegalArgumentException("PDF file hash is required")
@ -48,12 +56,14 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList
viewModel.getPDFDocument(filePath) viewModel.getPDFDocument(filePath)
} }
private fun initObserve(){ private fun initObserve() {
// 观察PDF文档数据 // 观察PDF文档数据
viewModel.pdfDocument.observe(this) { document -> viewModel.pdfDocument.observe(this) { document ->
document?.let { document?.let {
pdfDocument = it pdfDocument = it
loadPdf() loadPdf()
setupOnClick()
toggleEyeCareMode(appStore.isEyeCareMode)
} ?: run { } ?: run {
showToast(getString(R.string.file_not)) showToast(getString(R.string.file_not))
finish() finish()
@ -61,6 +71,16 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList
} }
} }
private fun setupOnClick() {
binding.backBtn.setOnClickListener {
finish()
}
binding.eyeCareOverlayBtn.setOnClickListener {
appStore.isEyeCareMode = !appStore.isEyeCareMode
toggleEyeCareMode(appStore.isEyeCareMode)
}
}
private fun loadPdf() { private fun loadPdf() {
logDebug("loadPdf ->${pdfDocument.lastReadPage} ${pdfDocument.readingProgress}") logDebug("loadPdf ->${pdfDocument.lastReadPage} ${pdfDocument.readingProgress}")
// 使用传递的文件路径加载PDF // 使用传递的文件路径加载PDF
@ -72,14 +92,14 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList
return return
} }
// 无密码PDF正常加载 // 无密码PDF正常加载
binding.pdfview.fromFile(file) binding.pdfview.fromFile(file).defaultPage(pdfDocument.lastReadPage) // 从上次阅读页码开始
.defaultPage(pdfDocument.lastReadPage) // 从上次阅读页码开始
.enableDoubletap(true)// 是否允许双击缩放 .enableDoubletap(true)// 是否允许双击缩放
.onLoad(this) .onLoad(this)//加载
.enableAnnotationRendering(true) // 是否渲染注释(如评论、颜色、表单等) .enableAnnotationRendering(true) // 是否渲染注释(如评论、颜色、表单等)
.onError(this) .onError(this)//错误
.onPageChange(this) .onTap(this)//点击
.scrollHandle(CustomScrollHandle(this)) .onPageChange(this)//页面改变
.scrollHandle(CustomScrollHandle(this))//自定义的页数展示,并且可快速拖动
.load() .load()
} else { } else {
showToast(getString(R.string.file_not) + ": ${pdfDocument.fileName}") showToast(getString(R.string.file_not) + ": ${pdfDocument.fileName}")
@ -109,6 +129,11 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList
} }
} }
override fun onTap(e: MotionEvent?): Boolean {
toggleFullScreen()
return true// 表示我们自己处理了单击事件
}
//页面切换时回调 //页面切换时回调
override fun onPageChanged(page: Int, pageCount: Int) { override fun onPageChanged(page: Int, pageCount: Int) {
// 保存阅读进度到内存 // 保存阅读进度到内存
@ -139,15 +164,55 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList
} }
private fun tryLoadPdfWithPassword(file: File, password: String) { private fun tryLoadPdfWithPassword(file: File, password: String) {
binding.pdfview.fromFile(file) binding.pdfview.fromFile(file).password(password) // 使用输入的密码
.password(password) // 使用输入的密码
.defaultPage(pdfDocument.lastReadPage) // 从上次阅读页码开始 .defaultPage(pdfDocument.lastReadPage) // 从上次阅读页码开始
.enableDoubletap(true)// 是否允许双击缩放 .enableDoubletap(true)// 是否允许双击缩放
.onLoad(this) .onLoad(this)//加载
.onTap(this)//单击
.enableAnnotationRendering(true) // 是否渲染注释(如评论、颜色、表单等) .enableAnnotationRendering(true) // 是否渲染注释(如评论、颜色、表单等)
.onError(this) .onError(this)//错误
.onPageChange(this) .onPageChange(this)//页面改变
.scrollHandle(CustomScrollHandle(this)) .scrollHandle(CustomScrollHandle(this))//自定义的页数展示,并且可快速拖动
.load() .load()
} }
private fun toggleFullScreen() {
isFullScreen = !isFullScreen
updateStatusAndNavigationLayout(isFullScreen)
if (isFullScreen) {
ImmersionBar.with(this).hideBar(BarHide.FLAG_HIDE_BAR).init()
} else {
val navColor =
if (appStore.isEyeCareMode) R.color.eye_protection_color else R.color.white
ImmersionBar.with(this).statusBarView(binding.view).statusBarDarkFont(true)
.navigationBarColor(navColor).hideBar(BarHide.FLAG_SHOW_BAR).init()
}
}
private fun updateStatusAndNavigationLayout(b: Boolean) {
if (b) {
// 隐藏顶部和底部
binding.statusLayout.visibility = View.GONE
binding.navigationLayout.visibility = View.GONE
binding.view.visibility = View.GONE
} else {
// 显示顶部和底部
binding.statusLayout.visibility = View.VISIBLE
binding.navigationLayout.visibility = View.VISIBLE
binding.view.visibility = View.VISIBLE
}
}
private fun toggleEyeCareMode(b: Boolean) {
val navColor = if (b) R.color.eye_protection_color else R.color.white
if (b) {
binding.eyeCareOverlay.visibility = View.VISIBLE
binding.eyeProtectIv.setImageResource(R.drawable.eye_protect_active)
} else {
binding.eyeCareOverlay.visibility = View.GONE
binding.eyeProtectIv.setImageResource(R.drawable.eye_protect)
}
ImmersionBar.with(this).statusBarView(binding.view).statusBarDarkFont(true)
.navigationBarColor(navColor).init()
}
} }

View File

@ -3,6 +3,7 @@ package com.all.pdfreader.pro.app.ui.view;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Handler; import android.os.Handler;
import android.util.TypedValue; import android.util.TypedValue;
@ -17,6 +18,7 @@ import com.github.barteksc.pdfviewer.scroll.ScrollHandle;
import com.github.barteksc.pdfviewer.util.Util; import com.github.barteksc.pdfviewer.util.Util;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.core.content.res.ResourcesCompat;
public class CustomScrollHandle extends RelativeLayout implements ScrollHandle { public class CustomScrollHandle extends RelativeLayout implements ScrollHandle {
private final static int HANDLE_LONG = 36; private final static int HANDLE_LONG = 36;
@ -203,6 +205,13 @@ public class CustomScrollHandle extends RelativeLayout implements ScrollHandle {
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, size); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, size);
} }
/**
* @param tf 自定义font字体
*/
public void setTypeface(Typeface tf){
textView.setTypeface(tf);
}
private boolean isPDFViewReady() { private boolean isPDFViewReady() {
return pdfView != null && pdfView.getPageCount() > 0 && !pdfView.documentFitsView(); return pdfView != null && pdfView.getPageCount() > 0 && !pdfView.documentFitsView();
} }

View File

@ -3,10 +3,10 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"> android:shape="rectangle">
<stroke <stroke
android:color="#6C7A89" android:color="@color/line_color"
android:width="1dp" /> android:width="1dp" />
<corners <corners
android:topLeftRadius="16dp" android:topLeftRadius="16dp"
android:topRightRadius="16dp" /> android:topRightRadius="16dp" />
<solid android:color="#DADFE1" /> <solid android:color="@color/white" />
</shape> </shape>

View File

@ -3,10 +3,10 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"> android:shape="rectangle">
<stroke <stroke
android:color="#6C7A89" android:color="@color/line_color"
android:width="1dp" /> android:width="1dp" />
<corners <corners
android:bottomLeftRadius="16dp" android:bottomLeftRadius="16dp"
android:topLeftRadius="16dp" /> android:topLeftRadius="16dp" />
<solid android:color="#DADFE1" /> <solid android:color="@color/white" />
</shape> </shape>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<vector android:height="25.0dip" android:width="25.0dip" android:viewportWidth="24.0" android:viewportHeight="25.0"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#00000000" android:pathData="M3.047,12.953C2.984,12.781 2.984,12.595 3.047,12.423C4.293,8.968 7.831,6.476 12,6.476C16.168,6.476 19.704,8.966 20.953,12.419C21.016,12.591 21.016,12.776 20.953,12.948C19.707,16.404 16.17,18.896 12,18.896C7.832,18.896 4.296,16.406 3.047,12.953Z" android:strokeColor="#ff636366" android:strokeWidth="1.8" android:strokeLineCap="round" android:strokeLineJoin="round" />
<path android:fillColor="#00000000" android:pathData="M14.696,12.686C14.696,13.401 14.412,14.087 13.907,14.592C13.401,15.098 12.715,15.382 12,15.382C11.285,15.382 10.6,15.098 10.094,14.592C9.589,14.087 9.305,13.401 9.305,12.686C9.305,11.971 9.589,11.285 10.094,10.78C10.6,10.274 11.285,9.99 12,9.99C12.715,9.99 13.401,10.274 13.907,10.78C14.412,11.285 14.696,11.971 14.696,12.686Z" android:strokeColor="#ff636366" android:strokeWidth="1.8" android:strokeLineCap="round" android:strokeLineJoin="round" />
</vector>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<vector android:height="24.0dip" android:width="24.0dip" android:viewportWidth="24.0" android:viewportHeight="24.0"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#ff1c1c1c" android:pathData="M2.2,11.432C3.584,7.595 7.478,4.89 12,4.89C16.52,4.89 20.412,7.593 21.799,11.427C21.934,11.798 21.933,12.2 21.799,12.569C20.415,16.406 16.521,19.11 12,19.11C7.48,19.11 3.587,16.407 2.2,12.573L2.2,12.572C2.066,12.203 2.066,11.801 2.2,11.432L2.2,11.432ZM9.74,9.74C10.339,9.141 11.152,8.804 12,8.804C12.847,8.804 13.66,9.141 14.259,9.74C14.859,10.34 15.196,11.152 15.196,12C15.196,12.848 14.859,13.66 14.259,14.26C13.66,14.859 12.847,15.196 12,15.196C11.152,15.196 10.339,14.859 9.74,14.26C9.141,13.66 8.804,12.848 8.804,12C8.804,11.152 9.141,10.34 9.74,9.74Z" android:fillType="evenOdd" />
</vector>

View File

@ -1,77 +1,131 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rootLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
android:background="@color/white"
android:orientation="vertical">
<LinearLayout <LinearLayout
android:id="@+id/statusLayout" android:id="@+id/bgLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="56dp" android:layout_height="match_parent"
android:gravity="center_vertical" android:background="@color/bg_color"
android:orientation="horizontal"> android:orientation="vertical">
<View
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="0dp" />
<LinearLayout <LinearLayout
android:id="@+id/backBtn" android:id="@+id/statusLayout"
android:layout_width="40dp" android:layout_width="match_parent"
android:layout_height="40dp" android:layout_height="48dp"
android:layout_marginStart="8dp" android:gravity="center_vertical"
android:layout_marginEnd="8dp" android:orientation="horizontal">
android:gravity="center">
<ImageView <LinearLayout
android:layout_width="24dp" android:id="@+id/backBtn"
android:layout_height="24dp" android:layout_width="40dp"
android:src="@drawable/back_black" /> android:layout_height="40dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:gravity="center">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/back_black" />
</LinearLayout>
</LinearLayout> </LinearLayout>
</LinearLayout> <LinearLayout
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@color/bg_color">
<com.github.barteksc.pdfviewer.PDFView
android:id="@+id/pdfview"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="0dp"
android:layout_weight="1"
android:background="@color/white">
</LinearLayout> <com.github.barteksc.pdfviewer.PDFView
android:id="@+id/pdfview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<LinearLayout </LinearLayout>
android:id="@+id/navigationLayout"
android:layout_width="match_parent"
android:layout_height="56dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<LinearLayout <LinearLayout
android:id="@+id/lockBtn" android:id="@+id/navigationLayout"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:layout_weight="1" android:background="@color/white"
android:gravity="center"
android:orientation="vertical"> android:orientation="vertical">
<ImageView <View
android:id="@+id/lockIv" android:layout_width="match_parent"
android:layout_width="24dp" android:layout_height="0.5dp"
android:layout_height="24dp" android:background="@color/line_color" />
android:src="@mipmap/ic_launcher" />
<TextView <LinearLayout
android:id="@+id/lockTv" android:layout_width="match_parent"
android:layout_width="wrap_content" android:layout_height="64dp"
android:layout_height="wrap_content" android:gravity="center_vertical"
android:fontFamily="@font/poppins_regular" android:orientation="horizontal">
android:text="@string/home"
android:textColor="@color/black" <LinearLayout
android:textSize="14sp" /> android:id="@+id/eyeCareOverlayBtn"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_horizontal|bottom"
android:orientation="vertical">
<ImageView
android:id="@+id/eyeProtectIv"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/eye_protect" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:fontFamily="@font/poppins_regular"
android:text="@string/eye_protect"
android:textColor="@color/black"
android:textSize="14sp" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_horizontal|bottom"
android:orientation="vertical">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/ic_launcher" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:fontFamily="@font/poppins_regular"
android:text="@string/home"
android:textColor="@color/black"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</LinearLayout> <!-- 护眼模式覆盖层 -->
<View
android:id="@+id/eyeCareOverlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/eye_protection_color"
android:visibility="gone" />
</FrameLayout>

View File

@ -14,4 +14,5 @@
<color name="line_color">#E0E0E0</color> <color name="line_color">#E0E0E0</color>
<color name="black_img_color">#2c2c2c</color> <color name="black_img_color">#2c2c2c</color>
<color name="grey_text_color">#666666</color> <color name="grey_text_color">#666666</color>
<color name="eye_protection_color">#80FFD699</color>
</resources> </resources>