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

This commit is contained in:
HapeLee
2026-03-10 00:12:27 +08:00
parent 12a46e8cae
commit 4438543e4d
6 changed files with 795 additions and 3 deletions
@@ -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"
@@ -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
)
}
}
}
}
}
@@ -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))
}
}
}
}
}
@@ -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,
""
)
}
@@ -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 })
}
}
@@ -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)
}
}