فهرست منبع

[新增] 简单重写了阅读界面设置页

HapeLee 8 ماه پیش
والد
کامیت
4438543e4d

+ 2 - 0
app/src/main/java/io/legado/app/constant/PreferKey.kt

@@ -236,6 +236,8 @@ object PreferKey {
     const val isPredictiveBackEnabled = "isPredictiveBackEnabled"
     const val replaceSortMode = "desc"
     const val readBarStyle = "readBarStyle"
+    const val disableReturnKey = "disableReturnKey"
+    const val selectText = "selectText"
     //我在干什么
 
     const val disableMangaScrollAnimation = "disableMangaScrollAnimation"

+ 98 - 2
app/src/main/java/io/legado/app/ui/config/TestConfigActivity.kt

@@ -1,13 +1,109 @@
 package io.legado.app.ui.config
 
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBarDefaults
 import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.rememberNavController
+import io.legado.app.R
 import io.legado.app.base.BaseComposeActivity
 import io.legado.app.ui.config.otherConfig.OtherConfigScreen
+import io.legado.app.ui.config.readConfig.ReadConfigScreen
+import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
+import io.legado.app.ui.widget.components.SplicedColumnGroup
+import io.legado.app.ui.widget.components.button.TopbarNavigationButton
+import io.legado.app.ui.widget.components.settingItem.ClickableSettingItem
+import kotlinx.serialization.Serializable
 
 class TestConfigActivity : BaseComposeActivity() {
+
+    @Serializable
+    object ConfigNavRoute
+
+    @Serializable
+    object OtherConfigRoute
+
+    @Serializable
+    object ReadConfigRoute
+
     @Composable
     override fun Content() {
-        OtherConfigScreen(onBackClick = { finish() })
+        val navController = rememberNavController()
+
+        NavHost(
+            navController = navController,
+            startDestination = ConfigNavRoute
+        ) {
+            composable<ConfigNavRoute> {
+                ConfigNavScreen(
+                    onBackClick = { finish() },
+                    onNavigateToOther = { navController.navigate(OtherConfigRoute) },
+                    onNavigateToRead = { navController.navigate(ReadConfigRoute) }
+                )
+            }
+
+            composable<OtherConfigRoute> {
+                OtherConfigScreen(onBackClick = { navController.popBackStack() })
+            }
+
+            composable<ReadConfigRoute> {
+                ReadConfigScreen(onBackClick = { navController.popBackStack() })
+            }
+        }
+    }
+
+    @OptIn(ExperimentalMaterial3Api::class)
+    @Composable
+    fun ConfigNavScreen(
+        onBackClick: () -> Unit,
+        onNavigateToOther: () -> Unit,
+        onNavigateToRead: () -> Unit
+    ) {
+        val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
+
+        Scaffold(
+            modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
+            topBar = {
+                GlassMediumFlexibleTopAppBar(
+                    title = { Text("设置") },
+                    scrollBehavior = scrollBehavior,
+                    navigationIcon = {
+                        TopbarNavigationButton(onClick = onBackClick)
+                    }
+                )
+            }
+        ) { paddingValues ->
+            Column(
+                modifier = Modifier
+                    .fillMaxSize()
+                    .padding(paddingValues)
+                    .verticalScroll(rememberScrollState())
+                    .padding(16.dp)
+            ) {
+                SplicedColumnGroup {
+                    ClickableSettingItem(
+                        title = stringResource(R.string.other_setting),
+                        onClick = onNavigateToOther
+                    )
+                    ClickableSettingItem(
+                        title = stringResource(R.string.read_config),
+                        onClick = onNavigateToRead
+                    )
+                }
+            }
+        }
     }
 
-}
+}

+ 128 - 0
app/src/main/java/io/legado/app/ui/config/readConfig/PageKeySheet.kt

@@ -0,0 +1,128 @@
+package io.legado.app.ui.config.readConfig
+
+import android.view.KeyEvent
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Button
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedButton
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.key.onPreviewKeyEvent
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import io.legado.app.R
+import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun PageKeySheet(
+    onDismissRequest: () -> Unit
+) {
+    var prevKeys by remember { mutableStateOf(ReadConfig.prevKeys) }
+    var nextKeys by remember { mutableStateOf(ReadConfig.nextKeys) }
+
+    GlassModalBottomSheet(onDismissRequest = onDismissRequest) {
+        Column(
+            modifier = Modifier.Companion
+                .fillMaxWidth()
+                .padding(16.dp),
+            verticalArrangement = Arrangement.spacedBy(16.dp)
+        ) {
+            Text(
+                text = stringResource(R.string.custom_page_key),
+                style = MaterialTheme.typography.titleLarge,
+                fontWeight = FontWeight.Companion.Bold
+            )
+
+            OutlinedTextField(
+                value = prevKeys,
+                onValueChange = { prevKeys = it },
+                label = { Text(stringResource(R.string.prev_page_key)) },
+                modifier = Modifier.Companion
+                    .fillMaxWidth()
+                    .onPreviewKeyEvent { event ->
+                        if (event.nativeKeyEvent.action == KeyEvent.ACTION_DOWN) {
+                            val keyCode = event.nativeKeyEvent.keyCode
+                            if (keyCode != KeyEvent.KEYCODE_BACK && keyCode != KeyEvent.KEYCODE_DEL) {
+                                prevKeys = if (prevKeys.isEmpty() || prevKeys.endsWith(",")) {
+                                    prevKeys + keyCode.toString()
+                                } else {
+                                    "$prevKeys,$keyCode"
+                                }
+                                return@onPreviewKeyEvent true
+                            }
+                        }
+                        false
+                    },
+                singleLine = true
+            )
+
+            OutlinedTextField(
+                value = nextKeys,
+                onValueChange = { nextKeys = it },
+                label = { Text(stringResource(R.string.next_page_key)) },
+                modifier = Modifier.Companion
+                    .fillMaxWidth()
+                    .onPreviewKeyEvent { event ->
+                        if (event.nativeKeyEvent.action == KeyEvent.ACTION_DOWN) {
+                            val keyCode = event.nativeKeyEvent.keyCode
+                            if (keyCode != KeyEvent.KEYCODE_BACK && keyCode != KeyEvent.KEYCODE_DEL) {
+                                nextKeys = if (nextKeys.isEmpty() || nextKeys.endsWith(",")) {
+                                    nextKeys + keyCode.toString()
+                                } else {
+                                    "$nextKeys,$keyCode"
+                                }
+                                return@onPreviewKeyEvent true
+                            }
+                        }
+                        false
+                    },
+                singleLine = true
+            )
+
+            Text(
+                text = stringResource(R.string.page_key_set_help),
+                style = MaterialTheme.typography.bodyMedium,
+                color = MaterialTheme.colorScheme.onSurfaceVariant
+            )
+
+            Row(
+                modifier = Modifier.Companion.fillMaxWidth(),
+                horizontalArrangement = Arrangement.spacedBy(8.dp)
+            ) {
+                OutlinedButton(
+                    onClick = {
+                        prevKeys = ""
+                        nextKeys = ""
+                    },
+                    modifier = Modifier.Companion.weight(1f)
+                ) {
+                    Text(stringResource(R.string.reset))
+                }
+
+                Button(
+                    onClick = {
+                        ReadConfig.prevKeys = prevKeys
+                        ReadConfig.nextKeys = nextKeys
+                        onDismissRequest()
+                    },
+                    modifier = Modifier.Companion.weight(1f)
+                ) {
+                    Text(stringResource(R.string.ok))
+                }
+            }
+        }
+    }
+}

+ 175 - 1
app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfig.kt

@@ -15,4 +15,178 @@ object ReadConfig {
         true
     )
 
-}
+    var screenOrientation by prefDelegate(
+        PreferKey.screenOrientation,
+        "0"
+    )
+
+    var keepLight by prefDelegate(
+        PreferKey.keepLight,
+        "0"
+    )
+
+    var hideStatusBar by prefDelegate(
+        PreferKey.hideStatusBar,
+        false
+    )
+
+    var hideNavigationBar by prefDelegate(
+        PreferKey.hideNavigationBar,
+        false
+    )
+
+    var paddingDisplayCutouts by prefDelegate(
+        PreferKey.paddingDisplayCutouts,
+        false
+    )
+
+    var titleBarMode by prefDelegate(
+        PreferKey.titleBarMode,
+        "1"
+    )
+
+    var menuAlpha by prefDelegate(
+        PreferKey.menuAlpha,
+        100
+    )
+
+    var readBodyToLh by prefDelegate(
+        PreferKey.readBodyToLh,
+        true
+    )
+
+    var defaultSourceChangeAll by prefDelegate(
+        PreferKey.defaultSourceChangeAll,
+        true
+    )
+
+    var textFullJustify by prefDelegate(
+        PreferKey.textFullJustify,
+        true
+    )
+
+    var textBottomJustify by prefDelegate(
+        PreferKey.textBottomJustify,
+        true
+    )
+
+    var adaptSpecialStyle by prefDelegate(
+        PreferKey.adaptSpecialStyle,
+        true
+    )
+
+    var useZhLayout by prefDelegate(
+        PreferKey.useZhLayout,
+        false
+    )
+
+    var showBrightnessView by prefDelegate(
+        PreferKey.showBrightnessView,
+        true
+    )
+
+    var useUnderline by prefDelegate(
+        PreferKey.useUnderline,
+        false
+    )
+
+    var readSliderMode by prefDelegate(
+        PreferKey.readSliderMode,
+        "0"
+    )
+
+    var doubleHorizontalPage by prefDelegate(
+        PreferKey.doublePageHorizontal,
+        "0"
+    )
+
+    var progressBarBehavior by prefDelegate(
+        PreferKey.progressBarBehavior,
+        "page"
+    )
+
+    var mouseWheelPage by prefDelegate(
+        PreferKey.mouseWheelPage,
+        true
+    )
+
+    var volumeKeyPage by prefDelegate(
+        PreferKey.volumeKeyPage,
+        true
+    )
+
+    var volumeKeyPageOnPlay by prefDelegate(
+        PreferKey.volumeKeyPageOnPlay,
+        true
+    )
+
+    var keyPageOnLongPress by prefDelegate(
+        PreferKey.keyPageOnLongPress,
+        false
+    )
+
+    var pageTouchSlop by prefDelegate(
+        PreferKey.pageTouchSlop,
+        0
+    )
+
+    var sliderVibrator by prefDelegate(
+        PreferKey.sliderVibrator,
+        false
+    )
+
+    var selectVibrator by prefDelegate(
+        PreferKey.selectVibrator,
+        false
+    )
+
+    var autoChangeSource by prefDelegate(
+        PreferKey.autoChangeSource,
+        true
+    )
+
+    var selectText by prefDelegate(
+        PreferKey.selectText,
+        true
+    )
+
+    var noAnimScrollPage by prefDelegate(
+        PreferKey.noAnimScrollPage,
+        false
+    )
+
+    var clickImgWay by prefDelegate(
+        PreferKey.clickImgWay,
+        "2"
+    )
+
+    var optimizeRender by prefDelegate(
+        PreferKey.optimizeRender,
+        false
+    )
+
+    var disableReturnKey by prefDelegate(
+        PreferKey.disableReturnKey,
+        false
+    )
+
+    var expandTextMenu by prefDelegate(
+        PreferKey.expandTextMenu,
+        false
+    )
+
+    var showReadTitleAddition by prefDelegate(
+        PreferKey.showReadTitleAddition,
+        true
+    )
+
+    var prevKeys by prefDelegate(
+        PreferKey.prevKeys,
+        ""
+    )
+
+    var nextKeys by prefDelegate(
+        PreferKey.nextKeys,
+        ""
+    )
+}

+ 329 - 0
app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigScreen.kt

@@ -0,0 +1,329 @@
+package io.legado.app.ui.config.readConfig
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringArrayResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import io.legado.app.R
+import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
+import io.legado.app.ui.widget.components.SplicedColumnGroup
+import io.legado.app.ui.widget.components.button.TopbarNavigationButton
+import io.legado.app.ui.widget.components.settingItem.ClickableSettingItem
+import io.legado.app.ui.widget.components.settingItem.DropdownListSettingItem
+import io.legado.app.ui.widget.components.settingItem.SliderSettingItem
+import io.legado.app.ui.widget.components.settingItem.SwitchSettingItem
+import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
+import org.koin.androidx.compose.koinViewModel
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun ReadConfigScreen(
+    onBackClick: () -> Unit,
+    viewModel: ReadConfigViewModel = koinViewModel()
+) {
+    val context = LocalContext.current
+    val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
+    var showPageKeySheet by remember { mutableStateOf(false) }
+
+    Scaffold(
+        modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
+        topBar = {
+            GlassMediumFlexibleTopAppBar(
+                title = {
+                    Text(stringResource(R.string.read_config))
+                },
+                scrollBehavior = scrollBehavior,
+                navigationIcon = {
+                    TopbarNavigationButton(onClick = onBackClick)
+                }
+            )
+        }
+    ) { paddingValues ->
+        Column(
+            modifier = Modifier
+                .fillMaxSize()
+                .padding(paddingValues)
+                .verticalScroll(rememberScrollState())
+                .padding(16.dp)
+        ) {
+
+            SplicedColumnGroup(title = stringResource(R.string.screen_settings)) {
+                DropdownListSettingItem(
+                    title = stringResource(R.string.screen_direction),
+                    selectedValue = ReadConfig.screenOrientation,
+                    displayEntries = stringArrayResource(R.array.screen_direction_title),
+                    entryValues = stringArrayResource(R.array.screen_direction_value),
+                    onValueChange = { ReadConfig.screenOrientation = it }
+                )
+
+                DropdownListSettingItem(
+                    title = stringResource(R.string.keep_light),
+                    selectedValue = ReadConfig.keepLight,
+                    displayEntries = stringArrayResource(R.array.screen_time_out),
+                    entryValues = stringArrayResource(R.array.screen_time_out_value),
+                    onValueChange = { ReadConfig.keepLight = it }
+                )
+
+                SwitchSettingItem(
+                    title = stringResource(R.string.pt_hide_status_bar),
+                    checked = ReadConfig.hideStatusBar,
+                    onCheckedChange = { viewModel.updateHideStatusBar(it) }
+                )
+
+                SwitchSettingItem(
+                    title = stringResource(R.string.pt_hide_navigation_bar),
+                    checked = ReadConfig.hideNavigationBar,
+                    onCheckedChange = { viewModel.updateHideNavigationBar(it) }
+                )
+
+                SwitchSettingItem(
+                    title = stringResource(R.string.padding_display_cutouts),
+                    checked = ReadConfig.paddingDisplayCutouts,
+                    onCheckedChange = { ReadConfig.paddingDisplayCutouts = it }
+                )
+
+                DropdownListSettingItem(
+                    title = stringResource(R.string.title_bar_mode),
+                    selectedValue = ReadConfig.titleBarMode,
+                    displayEntries = stringArrayResource(R.array.title_bar_mode),
+                    entryValues = stringArrayResource(R.array.title_bar_mode_value),
+                    onValueChange = { ReadConfig.titleBarMode = it }
+                )
+
+                SliderSettingItem(
+                    title = stringResource(R.string.menu_alpha),
+                    description = stringResource(R.string.menu_alpha_sum, ReadConfig.menuAlpha),
+                    value = ReadConfig.menuAlpha.toFloat(),
+                    defaultValue = 100f,
+                    valueRange = 0f..100f,
+                    onValueChange = { viewModel.updateMenuAlpha(it.toInt()) }
+                )
+
+                SwitchSettingItem(
+                    title = stringResource(R.string.read_body_to_lh),
+                    checked = ReadConfig.readBodyToLh,
+                    onCheckedChange = { ReadConfig.readBodyToLh = it }
+                )
+
+                SwitchSettingItem(
+                    title = stringResource(R.string.read_change_all),
+                    description = stringResource(R.string.read_change_all_s),
+                    checked = ReadConfig.defaultSourceChangeAll,
+                    onCheckedChange = { ReadConfig.defaultSourceChangeAll = it }
+                )
+
+                SwitchSettingItem(
+                    title = stringResource(R.string.text_full_justify),
+                    checked = ReadConfig.textFullJustify,
+                    onCheckedChange = {
+                        ReadConfig.textFullJustify = it
+                        viewModel.upLayout()
+                    }
+                )
+
+                SwitchSettingItem(
+                    title = stringResource(R.string.text_bottom_justify),
+                    checked = ReadConfig.textBottomJustify,
+                    onCheckedChange = {
+                        ReadConfig.textBottomJustify = it
+                        viewModel.upLayout()
+                    }
+                )
+
+                SwitchSettingItem(
+                    title = stringResource(R.string.adapt_special_style),
+                    checked = ReadConfig.adaptSpecialStyle,
+                    onCheckedChange = { ReadConfig.adaptSpecialStyle = it }
+                )
+
+                SwitchSettingItem(
+                    title = stringResource(R.string.use_zh_layout),
+                    checked = ReadConfig.useZhLayout,
+                    onCheckedChange = {
+                        ReadConfig.useZhLayout = it
+                        viewModel.upLayout()
+                    }
+                )
+
+                SwitchSettingItem(
+                    title = stringResource(R.string.show_brightness_view),
+                    checked = ReadConfig.showBrightnessView,
+                    onCheckedChange = { ReadConfig.showBrightnessView = it }
+                )
+
+                SwitchSettingItem(
+                    title = stringResource(R.string.use_underline),
+                    checked = ReadConfig.useUnderline,
+                    onCheckedChange = { ReadConfig.useUnderline = it }
+                )
+            }
+
+            SplicedColumnGroup(title = stringResource(R.string.page_control)) {
+                DropdownListSettingItem(
+                    title = stringResource(R.string.read_slider_mode),
+                    selectedValue = ReadConfig.readSliderMode,
+                    displayEntries = stringArrayResource(R.array.read_slider_mode),
+                    entryValues = stringArrayResource(R.array.read_slider_mode_value),
+                    onValueChange = { viewModel.updateReadSliderMode(it) }
+                )
+
+                DropdownListSettingItem(
+                    title = stringResource(R.string.double_page_horizontal),
+                    selectedValue = ReadConfig.doubleHorizontalPage,
+                    displayEntries = stringArrayResource(R.array.double_page_title),
+                    entryValues = stringArrayResource(R.array.double_page_value),
+                    onValueChange = {
+                        ReadConfig.doubleHorizontalPage = it
+                        viewModel.upLayout()
+                    }
+                )
+
+                DropdownListSettingItem(
+                    title = stringResource(R.string.progress_bar_behavior),
+                    selectedValue = ReadConfig.progressBarBehavior,
+                    displayEntries = stringArrayResource(R.array.progress_bar_behavior_title),
+                    entryValues = stringArrayResource(R.array.progress_bar_behavior_value),
+                    onValueChange = { viewModel.updateProgressBarBehavior(it) }
+                )
+
+                SwitchSettingItem(
+                    title = stringResource(R.string.mouse_wheel_page),
+                    checked = ReadConfig.mouseWheelPage,
+                    onCheckedChange = { ReadConfig.mouseWheelPage = it }
+                )
+
+                SwitchSettingItem(
+                    title = stringResource(R.string.volume_key_page),
+                    checked = ReadConfig.volumeKeyPage,
+                    onCheckedChange = { ReadConfig.volumeKeyPage = it }
+                )
+
+                SwitchSettingItem(
+                    title = stringResource(R.string.volume_key_page_on_play),
+                    checked = ReadConfig.volumeKeyPageOnPlay,
+                    onCheckedChange = { ReadConfig.volumeKeyPageOnPlay = it }
+                )
+
+                SwitchSettingItem(
+                    title = stringResource(R.string.key_page_on_long_press),
+                    checked = ReadConfig.keyPageOnLongPress,
+                    onCheckedChange = { ReadConfig.keyPageOnLongPress = it }
+                )
+
+                SliderSettingItem(
+                    title = stringResource(R.string.page_touch_slop_title),
+                    description = stringResource(
+                        R.string.page_touch_slop_summary,
+                        ReadConfig.pageTouchSlop
+                    ),
+                    value = ReadConfig.pageTouchSlop.toFloat(),
+                    defaultValue = 0f,
+                    valueRange = 0f..1000f,
+                    onValueChange = { viewModel.updatePageTouchSlop(it.toInt()) }
+                )
+            }
+
+            SplicedColumnGroup(title = stringResource(R.string.other)) {
+                SwitchSettingItem(
+                    title = stringResource(R.string.enable_slider_vibrator),
+                    checked = ReadConfig.sliderVibrator,
+                    onCheckedChange = { ReadConfig.sliderVibrator = it }
+                )
+
+                SwitchSettingItem(
+                    title = stringResource(R.string.enable_select_vibrator),
+                    checked = ReadConfig.selectVibrator,
+                    onCheckedChange = { ReadConfig.selectVibrator = it }
+                )
+
+                SwitchSettingItem(
+                    title = stringResource(R.string.auto_change_source),
+                    checked = ReadConfig.autoChangeSource,
+                    onCheckedChange = { ReadConfig.autoChangeSource = it }
+                )
+
+                SwitchSettingItem(
+                    title = stringResource(R.string.selectText),
+                    checked = ReadConfig.selectText,
+                    onCheckedChange = { ReadConfig.selectText = it }
+                )
+
+                SwitchSettingItem(
+                    title = stringResource(R.string.no_anim_scroll_page),
+                    checked = ReadConfig.noAnimScrollPage,
+                    onCheckedChange = {
+                        ReadConfig.noAnimScrollPage = it
+                        viewModel.upPageAnim()
+                    }
+                )
+
+                DropdownListSettingItem(
+                    title = stringResource(R.string.click_image_way),
+                    selectedValue = ReadConfig.clickImgWay,
+                    displayEntries = stringArrayResource(R.array.click_image_way_title),
+                    entryValues = stringArrayResource(R.array.click_image_way_value),
+                    onValueChange = { ReadConfig.clickImgWay = it }
+                )
+
+                if (CanvasRecorderFactory.isSupport) {
+                    SwitchSettingItem(
+                        title = stringResource(R.string.enable_optimize_render),
+                        checked = ReadConfig.optimizeRender,
+                        onCheckedChange = {
+                            ReadConfig.optimizeRender = it
+                            viewModel.upStyle()
+                        }
+                    )
+                }
+
+                ClickableSettingItem(
+                    title = stringResource(R.string.click_regional_config),
+                    onClick = { /* 暂时留空 */ }
+                )
+
+                SwitchSettingItem(
+                    title = stringResource(R.string.disable_return_key),
+                    checked = ReadConfig.disableReturnKey,
+                    onCheckedChange = { ReadConfig.disableReturnKey = it }
+                )
+
+                ClickableSettingItem(
+                    title = stringResource(R.string.custom_page_key),
+                    onClick = { showPageKeySheet = true }
+                )
+
+                SwitchSettingItem(
+                    title = stringResource(R.string.expand_text_menu),
+                    checked = ReadConfig.expandTextMenu,
+                    onCheckedChange = { ReadConfig.expandTextMenu = it }
+                )
+
+                SwitchSettingItem(
+                    title = stringResource(R.string.show_read_title_addition),
+                    checked = ReadConfig.showReadTitleAddition,
+                    onCheckedChange = { ReadConfig.showReadTitleAddition = it }
+                )
+            }
+        }
+    }
+
+    if (showPageKeySheet) {
+        PageKeySheet(onDismissRequest = { showPageKeySheet = false })
+    }
+}

+ 63 - 0
app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigViewModel.kt

@@ -0,0 +1,63 @@
+package io.legado.app.ui.config.readConfig
+
+import androidx.lifecycle.ViewModel
+import io.legado.app.constant.EventBus
+import io.legado.app.help.config.ReadBookConfig
+import io.legado.app.model.ReadBook
+import io.legado.app.ui.book.read.page.provider.ChapterProvider
+import io.legado.app.utils.postEvent
+
+class ReadConfigViewModel : ViewModel() {
+
+    fun updateHideStatusBar(hide: Boolean) {
+        ReadConfig.hideStatusBar = hide
+        ReadBookConfig.hideStatusBar = hide
+        postEvent(EventBus.UP_CONFIG, arrayListOf(0, 2))
+    }
+
+    fun updateHideNavigationBar(hide: Boolean) {
+        ReadConfig.hideNavigationBar = hide
+        ReadBookConfig.hideNavigationBar = hide
+        postEvent(EventBus.UP_CONFIG, arrayListOf(0, 2))
+    }
+
+    fun upLayout() {
+        ChapterProvider.upLayout()
+        ReadBook.loadContent(false)
+    }
+
+    fun upStyle() {
+        ChapterProvider.upStyle()
+        ReadBook.callBack?.upPageAnim(true)
+        ReadBook.loadContent(false)
+    }
+
+    fun upPageAnim() {
+        ReadBook.callBack?.upPageAnim()
+    }
+
+    fun updateMenuAlpha(alpha: Int) {
+        ReadConfig.menuAlpha = alpha
+        postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
+    }
+
+    fun updatePageTouchSlop(slop: Int) {
+        ReadConfig.pageTouchSlop = slop
+        postEvent(EventBus.UP_CONFIG, arrayListOf(4))
+    }
+
+    fun updateReadSliderMode(mode: String) {
+        ReadConfig.readSliderMode = mode
+        postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
+    }
+
+    fun updateProgressBarBehavior(behavior: String) {
+        ReadConfig.progressBarBehavior = behavior
+        postEvent(EventBus.UP_SEEK_BAR, true)
+    }
+
+    fun updateShowReadTitleAddition(show: Boolean) {
+        ReadConfig.showReadTitleAddition = show
+        postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
+    }
+}