Просмотр исходного кода

增加阅读漫画自定义点击区域功能
增加页脚配置预览
更新界面

HapeLee 1 год назад
Родитель
Сommit
82a2aa349e

+ 11 - 0
app/src/main/java/io/legato/kazusa/constant/PreferKey.kt

@@ -192,4 +192,15 @@ object PreferKey {
     const val showBottomView = "showBottomView"
     const val mangaScrollMode = "mangaScrollMode"
     const val webtoonSidePaddingDp = "webtoonSidePaddingDp"
+
+    const val mangaClickActionTL = "mangaClickActionTopLeft"
+    const val mangaClickActionTC = "mangaClickActionTopCenter"
+    const val mangaClickActionTR = "mangaClickActionTopRight"
+    const val mangaClickActionML = "mangaClickActionMiddleLeft"
+    const val mangaClickActionMC = "mangaClickActionMiddleCenter"
+    const val mangaClickActionMR = "mangaClickActionMiddleRight"
+    const val mangaClickActionBL = "mangaClickActionBottomLeft"
+    const val mangaClickActionBC = "mangaClickActionBottomCenter"
+    const val mangaClickActionBR = "mangaClickActionBottomRight"
+
 }

+ 50 - 0
app/src/main/java/io/legato/kazusa/help/config/AppConfig.kt

@@ -36,6 +36,18 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
     var clickActionBL = appCtx.getPrefInt(PreferKey.clickActionBL, 2)
     var clickActionBC = appCtx.getPrefInt(PreferKey.clickActionBC, 1)
     var clickActionBR = appCtx.getPrefInt(PreferKey.clickActionBR, 1)
+
+    //    -1无操作 1下一页 2上一页 0显示菜单
+    var mangaClickActionTL = appCtx.getPrefInt(PreferKey.mangaClickActionTL, -1)
+    var mangaClickActionTC = appCtx.getPrefInt(PreferKey.mangaClickActionTC, -1)
+    var mangaClickActionTR = appCtx.getPrefInt(PreferKey.mangaClickActionTR, 1)
+    var mangaClickActionML = appCtx.getPrefInt(PreferKey.mangaClickActionML, 2)
+    var mangaClickActionMC = appCtx.getPrefInt(PreferKey.mangaClickActionMC, 0)
+    var mangaClickActionMR = appCtx.getPrefInt(PreferKey.mangaClickActionMR, 1)
+    var mangaClickActionBL = appCtx.getPrefInt(PreferKey.mangaClickActionBL, 2)
+    var mangaClickActionBC = appCtx.getPrefInt(PreferKey.mangaClickActionBC, 1)
+    var mangaClickActionBR = appCtx.getPrefInt(PreferKey.mangaClickActionBR, 1)
+
     var themeMode = appCtx.getPrefString(PreferKey.themeMode, "0")
     var useDefaultCover = appCtx.getPrefBoolean(PreferKey.useDefaultCover, false)
     var optimizeRender = CanvasRecorderFactory.isSupport
@@ -76,6 +88,33 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
             PreferKey.clickActionBR -> clickActionBR =
                 appCtx.getPrefInt(PreferKey.clickActionBR, 1)
 
+            PreferKey.mangaClickActionTL -> mangaClickActionTL =
+                appCtx.getPrefInt(PreferKey.mangaClickActionTL, -1)
+
+            PreferKey.mangaClickActionTC -> mangaClickActionTC =
+                appCtx.getPrefInt(PreferKey.mangaClickActionTC, -1)
+
+            PreferKey.mangaClickActionTR -> mangaClickActionTR =
+                appCtx.getPrefInt(PreferKey.mangaClickActionTR, 1)
+
+            PreferKey.mangaClickActionML -> mangaClickActionML =
+                appCtx.getPrefInt(PreferKey.mangaClickActionML, 2)
+
+            PreferKey.mangaClickActionMC -> mangaClickActionMC =
+                appCtx.getPrefInt(PreferKey.mangaClickActionMC, 0)
+
+            PreferKey.mangaClickActionMR -> mangaClickActionMR =
+                appCtx.getPrefInt(PreferKey.mangaClickActionMR, 1)
+
+            PreferKey.mangaClickActionBL -> mangaClickActionBL =
+                appCtx.getPrefInt(PreferKey.mangaClickActionBL, 2)
+
+            PreferKey.mangaClickActionBC -> mangaClickActionBC =
+                appCtx.getPrefInt(PreferKey.mangaClickActionBC, 1)
+
+            PreferKey.mangaClickActionBR -> mangaClickActionBR =
+                appCtx.getPrefInt(PreferKey.mangaClickActionBR, 1)
+
             PreferKey.readBodyToLh -> ReadBookConfig.readBodyToLh =
                 appCtx.getPrefBoolean(PreferKey.readBodyToLh, true)
 
@@ -619,6 +658,17 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
         }
     }
 
+    fun detectMangaClickArea() {
+        if (mangaClickActionTL * mangaClickActionTC * mangaClickActionTR
+            * mangaClickActionML * mangaClickActionMC * mangaClickActionMR
+            * mangaClickActionBL * mangaClickActionBC * mangaClickActionBR != 0
+        ) {
+            appCtx.putPrefInt(PreferKey.mangaClickActionMC, 0)
+            appCtx.toastOnUi("当前没有配置菜单区域,自动恢复中间区域为菜单.")
+        }
+    }
+
+
     //跳转到漫画界面不使用富文本模式
     val showMangaUi: Boolean
         get() = appCtx.getPrefBoolean(PreferKey.showMangaUi, true)

+ 27 - 6
app/src/main/java/io/legato/kazusa/ui/book/manga/ReadMangaActivity.kt

@@ -42,6 +42,7 @@ import io.legato.kazusa.model.ReadManga
 import io.legato.kazusa.receiver.NetworkChangedListener
 import io.legato.kazusa.ui.book.changesource.ChangeBookSourceDialog
 import io.legato.kazusa.ui.book.info.BookInfoActivity
+import io.legato.kazusa.ui.book.manga.config.MangaClickActionConfigDialog
 import io.legato.kazusa.ui.book.manga.config.MangaColorFilterConfig
 import io.legato.kazusa.ui.book.manga.config.MangaColorFilterDialog
 import io.legato.kazusa.ui.book.manga.config.MangaFooterConfig
@@ -53,6 +54,7 @@ import io.legato.kazusa.ui.book.manga.entities.MangaPage
 import io.legato.kazusa.ui.book.manga.recyclerview.MangaAdapter
 import io.legato.kazusa.ui.book.manga.recyclerview.MangaLayoutManager
 import io.legato.kazusa.ui.book.manga.recyclerview.ScrollTimer
+import io.legato.kazusa.ui.book.manga.recyclerview.WebtoonFrame
 import io.legato.kazusa.ui.book.read.MangaMenu
 import io.legato.kazusa.ui.book.read.ReadBookActivity.Companion.RESULT_DELETED
 import io.legato.kazusa.ui.book.toc.TocActivityResult
@@ -121,6 +123,7 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
     }
     private var enableAutoScrollPage = false
     private var enableAutoScroll = false
+    private var enableScroll = false
     private val mLinearInterpolator by lazy {
         LinearInterpolator()
     }
@@ -230,19 +233,31 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
                 }
             }
         }
-        binding.webtoonFrame.run {
-            onTouchMiddle {
+
+        binding.webtoonFrame.actionHandler = object : WebtoonFrame.ClickActionHandler {
+            override fun showMenu() {
                 if (!binding.mangaMenu.isVisible && !loadingViewVisible) {
                     binding.mangaMenu.runMenuIn()
                 }
             }
-            onNextPage {
+
+            override fun nextPage() {
                 scrollToNext()
             }
-            onPrevPage {
+
+            override fun prevPage() {
                 scrollToPrev()
             }
+
+            override fun nextChapter() {
+                ReadManga.moveToNextChapter()
+            }
+
+            override fun prevChapter() {
+                ReadManga.moveToPrevChapter()
+            }
         }
+
     }
 
     override fun onNewIntent(intent: Intent) {
@@ -532,7 +547,6 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
 
     override fun showFooterConfig() {
         showDialogFragment(MangaFooterSettingDialog().apply {
-            initialAutoPageEnabled = enableAutoScrollPage
             callback = this@ReadMangaActivity
         })
     }
@@ -542,8 +556,14 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
         showDialogFragment(MangaColorFilterDialog())
     }
 
+    override fun showClickConfig() {
+        binding.mangaMenu.runMenuOut()
+        showDialogFragment(MangaClickActionConfigDialog())
+    }
+
     override fun showScrollModeDialog() {
         showDialogFragment(MangaScrollModeDialog().apply {
+            initialAutoPageEnabled = enableScroll
             callback = this@ReadMangaActivity
         })
     }
@@ -591,7 +611,7 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
 
     //自动翻页
     override fun onAutoPageToggle(enabled: Boolean) {
-        enableAutoScroll = enabled
+        enableScroll = enabled
         setAutoReadEnabled(enabled)
     }
 
@@ -622,6 +642,7 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
 
     //1 自动翻页 2 自动滚动 0 关闭
     fun setAutoReadEnabled(enabled: Boolean) {
+        enableScroll = enabled
         val isWebtoonMode =
             scrollMode == MangaScrollMode.WEBTOON || scrollMode == MangaScrollMode.WEBTOON_WITH_GAP
         if (enabled) {

+ 145 - 0
app/src/main/java/io/legato/kazusa/ui/book/manga/config/MangaClickActionConfigDialog.kt

@@ -0,0 +1,145 @@
+package io.legato.kazusa.ui.book.manga.config
+
+import android.content.DialogInterface
+import android.os.Bundle
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import io.legato.kazusa.R
+import io.legato.kazusa.base.BaseDialogFragment
+import io.legato.kazusa.constant.PreferKey
+import io.legato.kazusa.databinding.DialogClickActionConfigBinding
+import io.legato.kazusa.help.config.AppConfig
+import io.legato.kazusa.lib.dialogs.selector
+import io.legato.kazusa.utils.getCompatColor
+import io.legato.kazusa.utils.putPrefInt
+import io.legato.kazusa.utils.viewbindingdelegate.viewBinding
+
+class MangaClickActionConfigDialog : BaseDialogFragment(R.layout.dialog_click_action_config) {
+    private val binding by viewBinding(DialogClickActionConfigBinding::bind)
+
+    private val actions by lazy {
+        linkedMapOf(
+            Pair(-1, getString(R.string.non_action)),
+            Pair(0, getString(R.string.menu)),
+            Pair(1, getString(R.string.next_page)),
+            Pair(2, getString(R.string.prev_page)),
+            Pair(3, getString(R.string.next_chapter)),
+            Pair(4, getString(R.string.previous_chapter)),
+        )
+    }
+
+    override fun onStart() {
+        super.onStart()
+        dialog?.window?.run {
+            setBackgroundDrawableResource(R.color.transparent)
+            setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
+        }
+    }
+
+    override fun onDismiss(dialog: DialogInterface) {
+        super.onDismiss(dialog)
+    }
+
+    override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
+        view.setBackgroundColor(getCompatColor(R.color.translucent))
+        initData()
+        initViewEvent()
+    }
+
+    private fun initData() = binding.run {
+        tvTopLeft.text = actions[AppConfig.mangaClickActionTL]
+        tvTopCenter.text = actions[AppConfig.mangaClickActionTC]
+        tvTopRight.text = actions[AppConfig.mangaClickActionTR]
+        tvMiddleLeft.text = actions[AppConfig.mangaClickActionML]
+        tvMiddleCenter.text = actions[AppConfig.mangaClickActionMC]
+        tvMiddleRight.text = actions[AppConfig.mangaClickActionMR]
+        tvBottomLeft.text = actions[AppConfig.mangaClickActionBL]
+        tvBottomCenter.text = actions[AppConfig.mangaClickActionBC]
+        tvBottomRight.text = actions[AppConfig.mangaClickActionBR]
+    }
+
+    private fun initViewEvent() {
+        binding.ivClose.setOnClickListener {
+            dismissAllowingStateLoss()
+        }
+
+        binding.tvTopLeft.setOnClickListener {
+            selectAction { action ->
+                putPrefInt(PreferKey.mangaClickActionTL, action)
+                (it as? TextView)?.text = actions[action]
+            }
+        }
+
+        binding.tvTopCenter.setOnClickListener {
+            selectAction { action ->
+                putPrefInt(PreferKey.mangaClickActionTC, action)
+                (it as? TextView)?.text = actions[action]
+            }
+        }
+
+        binding.tvTopRight.setOnClickListener {
+            selectAction { action ->
+                putPrefInt(PreferKey.mangaClickActionTR, action)
+                (it as? TextView)?.text = actions[action]
+            }
+        }
+
+        binding.tvMiddleLeft.setOnClickListener {
+            selectAction { action ->
+                putPrefInt(PreferKey.mangaClickActionML, action)
+                (it as? TextView)?.text = actions[action]
+            }
+        }
+
+        binding.tvMiddleCenter.setOnClickListener {
+            selectAction { action ->
+                putPrefInt(PreferKey.mangaClickActionMC, action)
+                (it as? TextView)?.text = actions[action]
+            }
+        }
+
+        binding.tvMiddleRight.setOnClickListener {
+            selectAction { action ->
+                putPrefInt(PreferKey.mangaClickActionMR, action)
+                (it as? TextView)?.text = actions[action]
+            }
+        }
+
+        binding.tvBottomLeft.setOnClickListener {
+            selectAction { action ->
+                putPrefInt(PreferKey.mangaClickActionBL, action)
+                (it as? TextView)?.text = actions[action]
+            }
+        }
+
+        binding.tvBottomCenter.setOnClickListener {
+            selectAction { action ->
+                putPrefInt(PreferKey.mangaClickActionBC, action)
+                (it as? TextView)?.text = actions[action]
+            }
+        }
+
+        binding.tvBottomRight.setOnClickListener {
+            selectAction { action ->
+                putPrefInt(PreferKey.mangaClickActionBR, action)
+                (it as? TextView)?.text = actions[action]
+            }
+        }
+    }
+
+    private fun selectAction(success: (action: Int) -> Unit) {
+        context?.selector(
+            getString(R.string.select_action),
+            actions.values.toList()
+        ) { _, index ->
+            success.invoke(actions.keys.toList()[index])
+        }
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+        AppConfig.detectMangaClickArea()
+    }
+}
+

+ 6 - 2
app/src/main/java/io/legato/kazusa/ui/book/manga/config/MangaFooterSettingDialog.kt

@@ -23,8 +23,6 @@ class MangaFooterSettingDialog :
 
     var callback: Callback? = null
 
-    var initialAutoPageEnabled: Boolean = false
-
     private val binding by viewBinding(DialogMangaFooterSettingBinding::bind)
 
     override fun onStart() {
@@ -134,6 +132,11 @@ class MangaFooterSettingDialog :
             dismiss()
         }
 
+        binding.btnClickSet.setOnClickListener {
+            callback?.showClickConfig()
+            dismiss()
+        }
+
         updateChapterText()
     }
 
@@ -183,6 +186,7 @@ class MangaFooterSettingDialog :
         fun onAutoPageToggle(enable: Boolean)
         fun onAutoPageSpeedChanged(speed: Int)
         fun showColorFilterConfig()
+        fun showClickConfig()
     }
 
 }

+ 1 - 0
app/src/main/java/io/legato/kazusa/ui/book/manga/recyclerview/ScrollTimer.kt

@@ -15,6 +15,7 @@ class ScrollTimer(
 ) : RecyclerView.OnScrollListener() {
     private var distance = 1
     private var mScrollPageJob: Job? = null
+
     var isEnabled: Boolean = false
         set(value) {
             if (field != value) {

+ 60 - 27
app/src/main/java/io/legato/kazusa/ui/book/manga/recyclerview/WebtoonFrame.kt

@@ -8,6 +8,7 @@ import android.view.GestureDetector
 import android.view.MotionEvent
 import android.view.ScaleGestureDetector
 import android.widget.FrameLayout
+import io.legato.kazusa.help.config.AppConfig
 
 class WebtoonFrame : FrameLayout {
 
@@ -21,6 +22,7 @@ class WebtoonFrame : FrameLayout {
 
     private val scaleDetector = ScaleGestureDetector(context, ScaleListener())
     private val flingDetector = GestureDetector(context, FlingListener())
+    private val regionRects = mutableMapOf<String, RectF>()
 
     var doubleTapZoom = true
         set(value) {
@@ -34,45 +36,55 @@ class WebtoonFrame : FrameLayout {
     private val recycler: WebtoonRecyclerView?
         get() = getChildAt(0) as? WebtoonRecyclerView
 
-    private val mcRect = RectF()
-    private val blRect = RectF()
-    private val brRect = RectF()
-
-    private var mTouchMiddle: (() -> Unit)? = null
-    fun onTouchMiddle(init: () -> Unit) = apply { this.mTouchMiddle = init }
-    private var mNextPage: (() -> Unit)? = null
-    fun onNextPage(init: () -> Unit) = apply { this.mNextPage = init }
-    private var mPrevPage: (() -> Unit)? = null
-    fun onPrevPage(init: () -> Unit) = apply { this.mPrevPage = init }
-
     var disabledClickScroll = false
 
+    var actionHandler: ClickActionHandler? = null
+
     override fun onAttachedToWindow() {
         super.onAttachedToWindow()
-        recycler?.tapListener = { ev ->
-            when {
-                mcRect.contains(ev.rawX, ev.rawY) -> {
-                    mTouchMiddle?.invoke()
-                }
-
-                blRect.contains(ev.rawX, ev.rawY) && !disabledClickScroll -> {
-                    mPrevPage?.invoke()
-                }
-
-                brRect.contains(ev.rawX, ev.rawY) && !disabledClickScroll -> {
-                    mNextPage?.invoke()
-                }
+        recycler?.tapListener = tapListener@{ ev ->
+            val x = ev.rawX
+            val y = ev.rawY
+            val key =
+                regionRects.entries.find { it.value.contains(x, y) }?.key ?: return@tapListener
+
+            val action = when (key) {
+                "TL" -> AppConfig.mangaClickActionTL
+                "TC" -> AppConfig.mangaClickActionTC
+                "TR" -> AppConfig.mangaClickActionTR
+                "ML" -> AppConfig.mangaClickActionML
+                "MC" -> AppConfig.mangaClickActionMC
+                "MR" -> AppConfig.mangaClickActionMR
+                "BL" -> AppConfig.mangaClickActionBL
+                "BC" -> AppConfig.mangaClickActionBC
+                "BR" -> AppConfig.mangaClickActionBR
+                else -> -1
             }
+
+            performClickAction(action)
         }
+
     }
 
     override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
         super.onSizeChanged(w, h, oldw, oldh)
-        mcRect.set(width * 0.33f, height * 0.33f, width * 0.66f, height * 0.66f)
-        blRect.set(0f, height * 0.66f, width * 0.33f, height.toFloat())
-        brRect.set(width * 0.66f, height * 0.66f, width.toFloat(), height.toFloat())
+        val cellWidth = w / 3f
+        val cellHeight = h / 3f
+        val keys = listOf("TL", "TC", "TR", "ML", "MC", "MR", "BL", "BC", "BR")
+
+        keys.forEachIndexed { index, key ->
+            val row = index / 3
+            val col = index % 3
+            regionRects[key] = RectF(
+                col * cellWidth,
+                row * cellHeight,
+                (col + 1) * cellWidth,
+                (row + 1) * cellHeight
+            )
+        }
     }
 
+
     override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
         if (!disableMangaScale) {
             scaleDetector.onTouchEvent(ev)
@@ -93,6 +105,18 @@ class WebtoonFrame : FrameLayout {
         return super.dispatchTouchEvent(ev)
     }
 
+    private fun performClickAction(action: Int) {
+        when (action) {
+            -1 -> return
+            0 -> actionHandler?.showMenu()
+            1 -> actionHandler?.nextPage()
+            2 -> actionHandler?.prevPage()
+            3 -> actionHandler?.nextChapter()
+            4 -> actionHandler?.prevChapter()
+            else -> {}
+        }
+    }
+
     inner class ScaleListener : ScaleGestureDetector.SimpleOnScaleGestureListener() {
         override fun onScaleBegin(detector: ScaleGestureDetector): Boolean {
             recycler?.onScaleBegin()
@@ -123,4 +147,13 @@ class WebtoonFrame : FrameLayout {
             return recycler?.zoomFling(velocityX.toInt(), velocityY.toInt()) ?: false
         }
     }
+
+    interface ClickActionHandler {
+        fun showMenu()
+        fun nextPage()
+        fun prevPage()
+        fun nextChapter()
+        fun prevChapter()
+    }
+
 }

+ 6 - 14
app/src/main/java/io/legato/kazusa/ui/dict/DictDialog.kt

@@ -3,24 +3,22 @@ package io.legato.kazusa.ui.dict
 import android.os.Bundle
 import android.text.method.LinkMovementMethod
 import android.view.View
-import android.view.ViewGroup
 import androidx.fragment.app.viewModels
 import com.google.android.material.tabs.TabLayout
 import io.legato.kazusa.R
-import io.legato.kazusa.base.BaseDialogFragment
+import io.legato.kazusa.base.BaseBottomSheetDialogFragment
 import io.legato.kazusa.data.entities.DictRule
 import io.legato.kazusa.databinding.DialogDictBinding
-//import io.legado.app.lib.theme.accentColor
-//import io.legado.app.lib.theme.backgroundColor
+import io.legato.kazusa.utils.invisible
 import io.legato.kazusa.utils.setHtml
-import io.legato.kazusa.utils.setLayout
 import io.legato.kazusa.utils.toastOnUi
 import io.legato.kazusa.utils.viewbindingdelegate.viewBinding
+import io.legato.kazusa.utils.visible
 
 /**
  * 词典
  */
-class DictDialog() : BaseDialogFragment(R.layout.dialog_dict) {
+class DictDialog() : BaseBottomSheetDialogFragment(R.layout.dialog_dict) {
 
     constructor(word: String) : this() {
         arguments = Bundle().apply {
@@ -33,11 +31,6 @@ class DictDialog() : BaseDialogFragment(R.layout.dialog_dict) {
 
     private var word: String? = null
 
-    override fun onStart() {
-        super.onStart()
-        setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
-    }
-
     override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
         binding.tvDict.movementMethod = LinkMovementMethod()
         word = arguments?.getString("word")
@@ -46,8 +39,7 @@ class DictDialog() : BaseDialogFragment(R.layout.dialog_dict) {
             dismiss()
             return
         }
-//        binding.tabLayout.setBackgroundColor(backgroundColor)
-//        binding.tabLayout.setSelectedTabIndicatorColor(accentColor)
+
         binding.tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
             override fun onTabReselected(tab: TabLayout.Tab) {
 
@@ -61,7 +53,7 @@ class DictDialog() : BaseDialogFragment(R.layout.dialog_dict) {
                 val dictRule = tab.tag as DictRule
                 binding.rotateLoading.visible()
                 viewModel.dict(dictRule, word!!) {
-                    binding.rotateLoading.inVisible()
+                    binding.rotateLoading.invisible()
                     binding.tvDict.setHtml(it)
                 }
             }

+ 44 - 19
app/src/main/res/layout/dialog_dict.xml

@@ -1,34 +1,59 @@
 <?xml version="1.0" encoding="utf-8"?>
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content">
+    android:layout_height="wrap_content"
+    android:minHeight="320dp"
+    android:orientation="vertical"
+    android:paddingHorizontal="16dp"
+    android:paddingVertical="8dp">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:orientation="vertical"
+        android:paddingHorizontal="16dp"
+        android:paddingVertical="8dp">
+
+        <View
+            android:layout_width="36dp"
+            android:layout_height="4dp"
+            android:layout_gravity="center_horizontal"
+            android:layout_marginBottom="16dp"
+            android:background="@drawable/bg_bottom_sheet_handle" />
+
+        <TextView
+            android:id="@+id/bottom_sheet_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="词典"
+            android:textSize="18sp"
+            android:textStyle="bold" />
+
+    </LinearLayout>
 
     <com.google.android.material.tabs.TabLayout
         android:id="@+id/tab_layout"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:paddingBottom="16dp"
-        app:layout_constraintTop_toTopOf="parent"
-        tools:ignore="SpeakableTextPresentCheck" />
+        android:background="@android:color/transparent" />
 
-    <io.legato.kazusa.ui.widget.text.ScrollTextView
-        android:id="@+id/tv_dict"
+    <androidx.core.widget.NestedScrollView
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:padding="16dp"
-        android:textColor="@color/secondaryText"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/tab_layout" />
+        android:layout_height="wrap_content">
+
+        <io.legato.kazusa.ui.widget.text.ScrollTextView
+            android:id="@+id/tv_dict"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+    </androidx.core.widget.NestedScrollView>
+
 
-    <io.legato.kazusa.ui.widget.anima.RotateLoading
+    <com.google.android.material.progressindicator.CircularProgressIndicator
         android:id="@+id/rotate_loading"
         android:layout_width="60dp"
         android:layout_height="60dp"
         android:layout_gravity="center"
-        app:layout_constraintLeft_toLeftOf="parent"
-        app:layout_constraintRight_toRightOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/tab_layout" />
+        android:indeterminate="true" />
 
-</androidx.constraintlayout.widget.ConstraintLayout>
+</LinearLayout>

+ 8 - 0
app/src/main/res/layout/dialog_manga_footer_setting.xml

@@ -173,6 +173,14 @@
             android:layout_height="wrap_content"
             android:text="@string/manga_color_filter" />
 
+        <com.google.android.material.button.MaterialButton
+            android:id="@+id/btn_click_set"
+            style="@style/Widget.Material3.Button.TonalButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/click_regional_config"
+            app:icon="@drawable/ic_filter" />
+
 
     </LinearLayout>
 

+ 2 - 1
app/src/main/res/layout/dialog_manga_scroll_mode.xml

@@ -102,7 +102,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:paddingHorizontal="16dp"
-            android:paddingVertical="8dp"
+            android:paddingTop="8dp"
             android:text="条漫"
             android:textStyle="bold" />
 
@@ -112,6 +112,7 @@
             android:layout_height="wrap_content"
             app:max="30"
             app:title="侧边留白" />
+
     </LinearLayout>
 
     <LinearLayout

+ 1 - 2
app/src/main/res/layout/popup_action.xml

@@ -3,8 +3,7 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:orientation="vertical"
-    android:padding="16dp">
+    android:orientation="vertical">
 
     <androidx.recyclerview.widget.RecyclerView
         android:id="@+id/recycler_view"

+ 1 - 1
app/src/main/res/layout/popup_action_menu.xml

@@ -11,7 +11,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:animateLayoutChanges="true"
-        android:layout_margin="8dp"
+        android:padding="8dp"
         android:orientation="vertical">
         <LinearLayout
             android:layout_width="wrap_content"