This commit is contained in:
HapeLee
2025-06-04 00:25:14 +08:00
parent d529b41548
commit b86d90021c
85 changed files with 544 additions and 790 deletions
@@ -52,7 +52,7 @@ class AllBookmarkActivity : VMBaseActivity<ActivityAllBookmarkBinding, AllBookma
}
private fun initView() {
binding.recyclerView.addItemDecoration(BookmarkDecoration(adapter))
binding.recyclerView.addItemDecoration(BookmarkDecoration(this,adapter))
binding.recyclerView.adapter = adapter
binding.recyclerView.applyNavigationBarPadding()
}
@@ -1,12 +1,15 @@
package io.legado.app.ui.book.bookmark
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.text.TextPaint
import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.color.MaterialColors
//import io.legado.app.lib.theme.accentColor
//import io.legado.app.lib.theme.backgroundColor
import io.legado.app.utils.dpToPx
@@ -14,19 +17,23 @@ import io.legado.app.utils.spToPx
import splitties.init.appCtx
import kotlin.math.min
class BookmarkDecoration(val adapter: BookmarkAdapter) : RecyclerView.ItemDecoration() {
class BookmarkDecoration(
private val context: Context,
val adapter: BookmarkAdapter) : RecyclerView.ItemDecoration() {
private val headerLeft = 16f.dpToPx()
private val headerHeight = 32f.dpToPx()
private val headerPaint = Paint().apply {
//color = appCtx.backgroundColor
color = MaterialColors.getColor(context, com.google.android.material.R.attr.colorSurfaceContainer, Color.GRAY)
}
private val textPaint = TextPaint().apply {
textSize = 16f.spToPx()
//color = appCtx.accentColor
color = MaterialColors.getColor(context, com.google.android.material.R.attr.colorPrimary, Color.BLACK)
isAntiAlias = true
}
private val textRect = Rect()
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
@@ -5,6 +5,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope
import io.legado.app.R
import io.legado.app.base.BaseBottomSheetDialogFragment
import io.legado.app.base.BaseDialogFragment
import io.legado.app.data.appDb
import io.legado.app.data.entities.Bookmark
@@ -17,7 +18,7 @@ import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class BookmarkDialog() : BaseDialogFragment(R.layout.dialog_bookmark, true) {
class BookmarkDialog() : BaseBottomSheetDialogFragment(R.layout.dialog_bookmark) {
constructor(bookmark: Bookmark, editPos: Int = -1) : this() {
arguments = Bundle().apply {
@@ -40,22 +41,25 @@ class BookmarkDialog() : BaseDialogFragment(R.layout.dialog_bookmark, true) {
return
}
@Suppress("DEPRECATION")
//@Suppress("DEPRECATION")
val bookmark = arguments.getParcelable<Bookmark>("bookmark")
bookmark ?: let {
dismiss()
return
}
val editPos = arguments.getInt("editPos", -1)
binding.tvFooterLeft.visible(editPos >= 0)
binding.btnDelete.visible(editPos >= 0) // 替换 tvFooterLeft
binding.run {
tvChapterName.text = bookmark.chapterName
editBookText.setText(bookmark.bookText)
editContent.setText(bookmark.content)
tvCancel.setOnClickListener {
btnCancel.setOnClickListener {
dismiss()
}
tvOk.setOnClickListener {
btnOk.setOnClickListener {
bookmark.bookText = editBookText.text?.toString() ?: ""
bookmark.content = editContent.text?.toString() ?: ""
lifecycleScope.launch {
@@ -65,7 +69,8 @@ class BookmarkDialog() : BaseDialogFragment(R.layout.dialog_bookmark, true) {
dismiss()
}
}
tvFooterLeft.setOnClickListener {
btnDelete.setOnClickListener {
lifecycleScope.launch {
withContext(IO) {
appDb.bookmarkDao.delete(bookmark)
@@ -90,7 +90,7 @@ class ChangeCoverDialog() : BaseDialogFragment(R.layout.dialog_change_cover),
}
} else {
startStopMenuItem?.let { item ->
item.setIcon(R.drawable.ic_refresh_black_24dp)
item.setIcon(R.drawable.ic_refresh)
item.setTitle(R.string.refresh)
}
}
@@ -34,7 +34,6 @@ import io.legado.app.ui.book.source.edit.BookSourceEditActivity
import io.legado.app.ui.book.source.manage.BookSourceActivity
import io.legado.app.ui.widget.dialog.WaitDialog
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.ColorUtils
import io.legado.app.utils.StartActivityContract
import io.legado.app.utils.applyTint
import io.legado.app.utils.dpToPx
@@ -223,7 +222,7 @@ class ChangeBookSourceDialog() : BaseDialogFragment(R.layout.dialog_book_change_
}
} else {
startStopMenuItem?.let { item ->
item.setIcon(R.drawable.ic_refresh_black_24dp)
item.setIcon(R.drawable.ic_refresh)
item.setTitle(R.string.refresh)
}
}
@@ -224,7 +224,7 @@ class ChangeChapterSourceDialog() : BaseDialogFragment(R.layout.dialog_chapter_c
}
} else {
startStopMenuItem?.let { item ->
item.setIcon(R.drawable.ic_refresh_black_24dp)
item.setIcon(R.drawable.ic_refresh)
item.setTitle(R.string.refresh)
}
}
@@ -14,6 +14,7 @@ import android.widget.CheckBox
import android.widget.LinearLayout
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import io.legado.app.R
import io.legado.app.base.VMBaseActivity
@@ -163,7 +164,7 @@ class BookInfoActivity :
// binding.refreshLayout?.setColorSchemeColors(accentColor)
// binding.llInfo.setBackgroundColor(backgroundColor)
// binding.flAction.setBackgroundColor(bottomBackground)
binding.flAction.applyNavigationBarPadding()
//binding.flAction.applyNavigationBarPadding()
binding.btnShelf?.text = getString(R.string.remove_from_bookshelf)
binding.tvToc.text = getString(R.string.toc_s, getString(R.string.loading))
@@ -415,10 +416,14 @@ class BookInfoActivity :
}
private fun upTvBookshelf() {
if (viewModel.inBookshelf) {
binding.btnShelf?.text = getString(R.string.remove_from_bookshelf)
} else {
binding.btnShelf?.text = getString(R.string.add_to_bookshelf)
binding.btnShelf?.apply {
if (viewModel.inBookshelf) {
text = getString(R.string.remove_from_bookshelf)
icon = ContextCompat.getDrawable(context, R.drawable.ic_clear_all)
} else {
text = getString(R.string.add_to_bookshelf)
icon = ContextCompat.getDrawable(context, R.drawable.ic_star)
}
}
editMenuItem?.isVisible = viewModel.inBookshelf
}
@@ -5,6 +5,7 @@ import android.os.Handler
import android.os.Looper
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import com.google.android.material.color.MaterialColors
import io.legado.app.R
import io.legado.app.base.adapter.DiffRecyclerAdapter
import io.legado.app.base.adapter.ItemViewHolder
@@ -129,14 +130,13 @@ class ChapterListAdapter(context: Context, val callback: Callback) :
|| cacheFileNames.contains(item.getFileName())
if (payloads.isEmpty()) {
if (isDur) {
//tvChapterName.setTextColor(context.accentColor)
tvChapterName.setTextColor(MaterialColors.getColor(tvChapterName, com.google.android.material.R.attr.colorSecondary))
} else {
tvChapterName.setTextColor(context.getCompatColor(R.color.primaryText))
tvChapterName.setTextColor(MaterialColors.getColor(tvChapterName, com.google.android.material.R.attr.colorOnSurface))
}
tvChapterName.text = getDisplayTitle(item)
if (item.isVolume) {
//卷名,如第一卷 突出显示
tvChapterItem.setBackgroundColor(context.getCompatColor(R.color.btn_bg_press))
tvChapterItem.setBackgroundColor(MaterialColors.getColor(tvChapterItem, com.google.android.material.R.attr.colorSurfaceVariant))
} else {
//普通章节 保持不变
tvChapterItem.background =
@@ -64,10 +64,10 @@ class ChapterListFragment : VMBaseFragment<TocViewModel>(R.layout.fragment_chapt
}
private fun initView() = binding.run {
ivChapterTop.setOnClickListener {
btnChapterTop.setOnClickListener {
mLayoutManager.scrollToPositionWithOffset(0, 0)
}
ivChapterBottom.setOnClickListener {
btnChapterBottom.setOnClickListener {
if (adapter.itemCount > 0) {
mLayoutManager.scrollToPositionWithOffset(adapter.itemCount - 1, 0)
}
@@ -57,7 +57,7 @@ class TocActivity : VMBaseActivity<ActivityChapterListBinding, TocViewModel>(),
override fun onActivityCreated(savedInstanceState: Bundle?) {
tabLayout = binding.titleBar.findViewById(R.id.tab_layout)
tabLayout.isTabIndicatorFullWidth = false
//tabLayout.isTabIndicatorFullWidth = false
//tabLayout.setSelectedTabIndicatorColor(accentColor)
binding.viewPager.adapter = TabFragmentPageAdapter()
tabLayout.setupWithViewPager(binding.viewPager)
@@ -5,13 +5,16 @@ import android.view.Menu
import android.view.MenuItem
import android.view.SubMenu
import android.view.View
import androidx.appcompat.widget.SearchView
import android.view.inputmethod.EditorInfo
import androidx.core.view.isGone
import androidx.core.widget.doAfterTextChanged
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.search.SearchBar
import com.google.android.material.search.SearchView
import io.legado.app.R
import io.legado.app.base.VMBaseFragment
import io.legado.app.constant.AppLog
@@ -63,7 +66,8 @@ class ExploreFragment() : VMBaseFragment<ExploreViewModel>(R.layout.fragment_exp
private val binding by viewBinding(FragmentExploreBinding::bind)
private val adapter by lazy { ExploreAdapter(requireContext(), this) }
private val linearLayoutManager by lazy { LinearLayoutManager(context) }
private val searchView: SearchView by lazy { binding.searchLayout.searchView }
private val searchBar: SearchBar by lazy { binding.searchBar }
private val searchView: SearchView by lazy { binding.searchView }
private val diffItemCallBack = ExploreDiffItemCallBack()
private val groups = linkedSetOf<String>()
private var exploreFlowJob: Job? = null
@@ -77,27 +81,47 @@ class ExploreFragment() : VMBaseFragment<ExploreViewModel>(R.layout.fragment_exp
upExploreData()
}
override fun onPause() {
super.onPause()
searchView.clearFocus()
}
private fun initSearchView() {
//searchView.applyTint(primaryTextColor)
searchView.isSubmitButtonEnabled = true
searchView.queryHint = getString(R.string.screen_find)
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
return false
}
searchView.hint = getString(R.string.screen_find)
override fun onQueryTextChange(newText: String?): Boolean {
upExploreData(newText)
return false
searchBar.setOnClickListener {
searchView.show()
if (searchView.text.isEmpty()) {
searchView.hint = getString(R.string.screen_find)
}
})
}
searchView.editText.setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
val query = searchView.text.toString()
upExploreData(query)
searchBar.hint = if (query.isEmpty()) {
getString(R.string.screen_find)
} else {
query
}
searchView.hide()
return@setOnEditorActionListener true
}
false
}
searchView.editText.doAfterTextChanged { editable ->
editable?.let {
upExploreData(it.toString())
if (it.isEmpty()) {
searchBar.hint = getString(R.string.search_rss_source)
}
}
}
searchView.setupWithSearchBar(searchBar)
}
private fun initRecyclerView() {
@@ -157,7 +181,7 @@ class ExploreFragment() : VMBaseFragment<ExploreViewModel>(R.layout.fragment_exp
).catch {
AppLog.put("发现界面更新数据出错", it)
}.conflate().flowOn(IO).collect {
binding.tvEmptyMsg.isGone = it.isNotEmpty() || searchView.query.isNotEmpty()
binding.tvEmptyMsg.isGone = it.isNotEmpty() || searchView.text.isNotEmpty() == true
adapter.setItems(it, diffItemCallBack)
delay(500)
}
@@ -174,10 +198,18 @@ class ExploreFragment() : VMBaseFragment<ExploreViewModel>(R.layout.fragment_exp
override val scope: CoroutineScope
get() = viewLifecycleOwner.lifecycleScope
override fun onCompatCreateOptionsMenu(menu: Menu) {
menuInflater.inflate(R.menu.main_explore, menu)
groupsMenu = menu.findItem(R.id.menu_group)?.subMenu
upGroupsMenu()
}
override fun onCompatOptionsItemSelected(item: MenuItem) {
super.onCompatOptionsItemSelected(item)
if (item.groupId == R.id.menu_group_text) {
searchView.setQuery("group:${item.title}", true)
val query = "group:${item.title}"
searchView.setText(query)
upExploreData(query)
}
}
@@ -5,10 +5,13 @@ import android.view.Menu
import android.view.MenuItem
import android.view.SubMenu
import android.view.View
import androidx.appcompat.widget.SearchView
import android.view.inputmethod.EditorInfo
import androidx.core.widget.doAfterTextChanged
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import com.google.android.material.search.SearchBar
import com.google.android.material.search.SearchView
import io.legado.app.R
import io.legado.app.base.VMBaseFragment
import io.legado.app.constant.AppLog
@@ -62,7 +65,8 @@ class RssFragment() : VMBaseFragment<RssViewModel>(R.layout.fragment_rss),
private val adapter by lazy {
RssAdapter(requireContext(), this, this, viewLifecycleOwner.lifecycle)
}
private val searchView: SearchView by lazy { binding.searchLayout.searchView }
private val searchBar: SearchBar by lazy { binding.searchBar }
private val searchView: SearchView by lazy { binding.searchView }
private var groupsFlowJob: Job? = null
private var rssFlowJob: Job? = null
private val groups = linkedSetOf<String>()
@@ -88,7 +92,8 @@ class RssFragment() : VMBaseFragment<RssViewModel>(R.layout.fragment_rss),
R.id.menu_rss_config -> startActivity<RssSourceActivity>()
R.id.menu_rss_star -> startActivity<RssFavoritesActivity>()
else -> if (item.groupId == R.id.menu_group_text) {
searchView.setQuery("group:${item.title}", true)
searchView.setText("group:${item.title}")
upRssFlowJob("group:${item.title}")
}
}
}
@@ -106,19 +111,41 @@ class RssFragment() : VMBaseFragment<RssViewModel>(R.layout.fragment_rss),
}
private fun initSearchView() {
//searchView.applyTint(primaryTextColor)
searchView.isSubmitButtonEnabled = true
searchView.queryHint = getString(R.string.rss)
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
return false
}
searchView.hint = getString(R.string.search_rss_source)
override fun onQueryTextChange(newText: String?): Boolean {
upRssFlowJob(newText)
return false
searchBar.setOnClickListener {
searchView.show()
if (searchView.text.isEmpty()) {
searchView.hint = getString(R.string.search_rss_source)
}
})
}
searchView.editText.setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
val query = searchView.text.toString()
upRssFlowJob(query)
searchBar.hint = if (query.isEmpty()) {
getString(R.string.search_rss_source)
} else {
query
}
searchView.hide()
return@setOnEditorActionListener true
}
false
}
searchView.editText.doAfterTextChanged { editable ->
editable?.let {
upRssFlowJob(it.toString())
if (it.isEmpty()) {
searchBar.hint = getString(R.string.search_rss_source)
}
}
}
searchView.setupWithSearchBar(searchBar)
}
private fun initRecyclerView() {
@@ -1,6 +1,8 @@
package io.legado.app.ui.widget
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
import android.os.Build
import android.util.AttributeSet
import android.widget.LinearLayout
@@ -8,6 +10,7 @@ import android.widget.TextView
import androidx.annotation.RequiresApi
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import com.google.android.material.color.MaterialColors
import io.legado.app.ui.widget.text.AccentBgTextView
import io.legado.app.utils.dpToPx
@@ -19,13 +22,7 @@ class LabelsBar @JvmOverloads constructor(
private val unUsedChips = arrayListOf<Chip>()
private val usedChips = arrayListOf<Chip>()
//var textSizeSp = 12f
init {
// isSingleLine = false
// chipSpacingHorizontal = 8.dpToPx()
// chipSpacingVertical = 4.dpToPx()
}
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
fun setLabels(labels: List<String>) {
@@ -46,7 +43,6 @@ class LabelsBar @JvmOverloads constructor(
removeAllViews()
}
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
fun addLabel(label: String) {
val chip = if (unUsedChips.isEmpty()) {
Chip(context).apply {
@@ -55,10 +51,18 @@ class LabelsBar @JvmOverloads constructor(
chipStartPadding = 8f
chipEndPadding = 8f
chipStrokeWidth = 0f
chipBackgroundColor = ColorStateList.valueOf(
MaterialColors.getColor(this, com.google.android.material.R.attr.colorPrimaryContainer, Color.LTGRAY)
)
usedChips.add(this)
}
} else {
unUsedChips.removeLast().also { usedChips.add(it) }
unUsedChips.removeAt(unUsedChips.lastIndex).also {
usedChips.add(it)
}
}
chip.text = label
addView(chip)
@@ -17,6 +17,7 @@ import android.widget.TextView
import androidx.appcompat.widget.SearchView
import io.legado.app.R
import io.legado.app.utils.printOnDebug
import androidx.core.graphics.withTranslation
class SearchView @JvmOverloads constructor(
@@ -101,10 +102,9 @@ class SearchView @JvmOverloads constructor(
val fm = paint.fontMetricsInt
val transY = ((y + fm.descent + y + fm.ascent) / 2
- b.bounds.bottom / 2)
canvas.save()
canvas.translate(x, transY.toFloat())
b.draw(canvas)
canvas.restore()
canvas.withTranslation(x, transY.toFloat()) {
b.draw(this)
}
}
}
}
@@ -4,49 +4,55 @@ import android.content.Context
import android.graphics.Color
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatTextView
import com.google.android.material.color.MaterialColors
import com.google.android.material.textview.MaterialTextView
import io.legado.app.R
import io.legado.app.lib.theme.Selector
import io.legado.app.lib.theme.ThemeStore
import io.legado.app.utils.ColorUtils
import io.legado.app.utils.dpToPx
import io.legado.app.utils.getCompatColor
import androidx.core.content.withStyledAttributes
class AccentBgTextView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : AppCompatTextView(context, attrs) {
) : MaterialTextView(context, attrs) {
// private var radius = 0
//
// init {
// val typedArray = context.obtainStyledAttributes(attrs, R.styleable.AccentBgTextView)
// radius = typedArray.getDimensionPixelOffset(R.styleable.AccentBgTextView_radius, radius)
// typedArray.recycle()
// upBackground()
// }
//
// fun setRadius(radius: Int) {
// this.radius = radius.dpToPx()
// upBackground()
// }
//
// private fun upBackground() {
// val accentColor = if (isInEditMode) {
// context.getCompatColor(R.color.accent)
// } else {
// ThemeStore.accentColor(context)
// }
// background = Selector.shapeBuild()
// .setCornerRadius(radius)
// .setDefaultBgColor(accentColor)
// .setPressedBgColor(ColorUtils.darkenColor(accentColor))
// .create()
// setTextColor(
// if (ColorUtils.isColorLight(accentColor)) {
// Color.BLACK
// } else {
// Color.WHITE
// }
// )
// }
private var radiusPx = 0
init {
context.withStyledAttributes(attrs, R.styleable.AccentBgTextView) {
val radiusDp = getDimensionPixelOffset(R.styleable.AccentBgTextView_radius, 0)
radiusPx = radiusDp
}
updateBackground()
}
fun setRadius(dp: Int) {
radiusPx = dp.dpToPx()
updateBackground()
}
private fun updateBackground() {
val backgroundColor = MaterialColors.getColor(
this,
com.google.android.material.R.attr.colorPrimaryContainer,
//context.getColorCompat(R.color.fallback_bg)
)
val textColor = MaterialColors.getColor(
this,
com.google.android.material.R.attr.colorOnPrimaryContainer,
//context.getColorCompat(R.color.fallback_text)
)
background = Selector.shapeBuild()
.setCornerRadius(radiusPx)
.setDefaultBgColor(backgroundColor)
.setPressedBgColor(ColorUtils.darkenColor(backgroundColor))
.create()
setTextColor(textColor)
}
}
@@ -2,40 +2,24 @@
package io.legado.app.utils
import android.annotation.SuppressLint
import android.content.Context
import android.widget.Toast
import androidx.fragment.app.Fragment
import io.legado.app.BuildConfig
import io.legado.app.databinding.ViewToastBinding
import io.legado.app.help.config.AppConfig
//import io.legado.app.lib.theme.bottomBackground
//import io.legado.app.lib.theme.getPrimaryTextColor
import splitties.systemservices.layoutInflater
private var toast: Toast? = null
private var toastLegacy: Toast? = null
fun Context.toastOnUi(message: Int, duration: Int = Toast.LENGTH_SHORT) {
toastOnUi(getString(message), duration)
}
@SuppressLint("InflateParams")
@Suppress("DEPRECATION")
fun Context.toastOnUi(message: CharSequence?, duration: Int = Toast.LENGTH_SHORT) {
runOnUI {
kotlin.runCatching {
toast?.cancel()
toast = Toast(this)
//val isLight = ColorUtils.isColorLight(bottomBackground)
ViewToastBinding.inflate(layoutInflater).run {
toast?.view = root
// cvToast.setCardBackgroundColor(bottomBackground)
// tvText.setTextColor(getPrimaryTextColor(isLight))
tvText.text = message
}
toast?.duration = duration
toast = Toast.makeText(this, message, duration)
toast?.show()
}
}
@@ -84,3 +68,4 @@ fun Fragment.toastOnUi(message: CharSequence) = requireActivity().toastOnUi(mess
fun Fragment.longToast(message: Int) = requireContext().longToastOnUi(message)
fun Fragment.longToast(message: CharSequence) = requireContext().longToastOnUi(message)