[修复] 订阅收藏页图片解析错误问题 #700
This commit is contained in:
@@ -1,86 +0,0 @@
|
|||||||
package io.legado.app.ui.rss.favorites
|
|
||||||
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.View
|
|
||||||
import androidx.fragment.app.viewModels
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import io.legado.app.R
|
|
||||||
import io.legado.app.base.VMBaseFragment
|
|
||||||
import io.legado.app.constant.AppLog
|
|
||||||
import io.legado.app.data.appDb
|
|
||||||
import io.legado.app.data.entities.RssStar
|
|
||||||
import io.legado.app.databinding.FragmentRssArticlesBinding
|
|
||||||
import io.legado.app.lib.dialogs.alert
|
|
||||||
//import io.legado.app.lib.theme.primaryColor
|
|
||||||
import io.legado.app.ui.rss.read.ReadRssActivity
|
|
||||||
import io.legado.app.ui.widget.recycler.VerticalDivider
|
|
||||||
import io.legado.app.utils.applyNavigationBarPadding
|
|
||||||
import io.legado.app.utils.startActivity
|
|
||||||
import io.legado.app.utils.viewbindingdelegate.viewBinding
|
|
||||||
import kotlinx.coroutines.Dispatchers.IO
|
|
||||||
import kotlinx.coroutines.flow.catch
|
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
class RssFavoritesFragment() : VMBaseFragment<RssFavoritesViewModel>(R.layout.fragment_rss_articles),
|
|
||||||
RssFavoritesAdapter.CallBack {
|
|
||||||
|
|
||||||
constructor(group: String) : this() {
|
|
||||||
arguments = Bundle().apply {
|
|
||||||
putString("group", group)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val binding by viewBinding(FragmentRssArticlesBinding::bind)
|
|
||||||
override val viewModel by viewModels<RssFavoritesViewModel>()
|
|
||||||
private val adapter: RssFavoritesAdapter by lazy {
|
|
||||||
RssFavoritesAdapter(requireContext(), this@RssFavoritesFragment)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
initView()
|
|
||||||
loadArticles()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initView() = binding.run {
|
|
||||||
refreshLayout.isEnabled = false
|
|
||||||
// recyclerView.setEdgeEffectColor(primaryColor)
|
|
||||||
recyclerView.layoutManager = run {
|
|
||||||
recyclerView.addItemDecoration(VerticalDivider(requireContext()))
|
|
||||||
LinearLayoutManager(requireContext())
|
|
||||||
}
|
|
||||||
recyclerView.adapter = adapter
|
|
||||||
recyclerView.applyNavigationBarPadding()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadArticles() {
|
|
||||||
lifecycleScope.launch {
|
|
||||||
val group = arguments?.getString("group") ?: "默认分组"
|
|
||||||
appDb.rssStarDao.flowByGroup(group).catch {
|
|
||||||
AppLog.put("订阅文章界面获取数据失败\n${it.localizedMessage}", it)
|
|
||||||
}.flowOn(IO).collect {
|
|
||||||
adapter.setItems(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun readRss(rssStar: RssStar) {
|
|
||||||
startActivity<ReadRssActivity> {
|
|
||||||
putExtra("title", rssStar.title)
|
|
||||||
putExtra("origin", rssStar.origin)
|
|
||||||
putExtra("link", rssStar.link)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun delStar(rssStar: RssStar) {
|
|
||||||
alert(R.string.draw) {
|
|
||||||
setMessage(getString(R.string.sure_del) + "\n<" + rssStar.title + ">")
|
|
||||||
noButton()
|
|
||||||
yesButton {
|
|
||||||
appDb.rssStarDao.delete(rssStar.origin, rssStar.link)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,36 +11,37 @@ import androidx.compose.foundation.layout.width
|
|||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.OpenInNew
|
||||||
import androidx.compose.material.icons.automirrored.outlined.Label
|
import androidx.compose.material.icons.automirrored.outlined.Label
|
||||||
import androidx.compose.material.icons.filled.DeleteForever
|
import androidx.compose.material.icons.filled.DeleteForever
|
||||||
import androidx.compose.material.icons.filled.DeleteSweep
|
import androidx.compose.material.icons.filled.DeleteSweep
|
||||||
import androidx.compose.material.icons.filled.Group
|
import androidx.compose.material.icons.filled.Group
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.PrimaryScrollableTabRow
|
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.material3.Tab
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import coil.compose.AsyncImage
|
|
||||||
import coil.request.ImageRequest
|
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
|
import io.legado.app.data.entities.RssStar
|
||||||
import io.legado.app.ui.rss.read.ReadRssActivity
|
import io.legado.app.ui.rss.read.ReadRssActivity
|
||||||
|
import io.legado.app.ui.widget.components.ActionItem
|
||||||
import io.legado.app.ui.widget.components.EmptyMessageView
|
import io.legado.app.ui.widget.components.EmptyMessageView
|
||||||
|
import io.legado.app.ui.widget.components.SourceIcon
|
||||||
|
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||||
import io.legado.app.ui.widget.components.card.SelectionItemCard
|
import io.legado.app.ui.widget.components.card.SelectionItemCard
|
||||||
|
import io.legado.app.ui.widget.components.dialog.TextListInputDialog
|
||||||
import io.legado.app.ui.widget.components.divider.PillDivider
|
import io.legado.app.ui.widget.components.divider.PillDivider
|
||||||
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
|
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
|
||||||
import io.legado.app.ui.widget.components.rules.RuleListScaffold
|
import io.legado.app.ui.widget.components.rules.RuleListScaffold
|
||||||
@@ -57,14 +58,55 @@ fun RssFavoritesScreen(
|
|||||||
val groups by viewModel.groups.collectAsState()
|
val groups by viewModel.groups.collectAsState()
|
||||||
val snackbarHostState = remember { SnackbarHostState() }
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
|
|
||||||
LaunchedEffect(groups) {
|
var showAddToGroupDialog by remember { mutableStateOf(false) }
|
||||||
if (state.currentGroup.isEmpty() && groups.isNotEmpty()) {
|
var showRemoveFromGroupDialog by remember { mutableStateOf(false) }
|
||||||
viewModel.onGroupChange(groups.first())
|
var showSetGroupDialog by remember { mutableStateOf<RssStar?>(null) }
|
||||||
}
|
|
||||||
|
if (showAddToGroupDialog) {
|
||||||
|
TextListInputDialog(
|
||||||
|
title = stringResource(R.string.add_group),
|
||||||
|
hint = stringResource(R.string.group_name),
|
||||||
|
suggestions = groups,
|
||||||
|
onDismissRequest = { showAddToGroupDialog = false },
|
||||||
|
onConfirm = {
|
||||||
|
viewModel.selectionAddToGroups(state.selectedIds, it)
|
||||||
|
showAddToGroupDialog = false
|
||||||
|
viewModel.clearSelection()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showRemoveFromGroupDialog) {
|
||||||
|
TextListInputDialog(
|
||||||
|
title = stringResource(R.string.remove_group),
|
||||||
|
hint = stringResource(R.string.group_name),
|
||||||
|
suggestions = groups,
|
||||||
|
onDismissRequest = { showRemoveFromGroupDialog = false },
|
||||||
|
onConfirm = {
|
||||||
|
viewModel.selectionRemoveFromGroups(state.selectedIds, it)
|
||||||
|
showRemoveFromGroupDialog = false
|
||||||
|
viewModel.clearSelection()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
showSetGroupDialog?.let { rssStar ->
|
||||||
|
TextListInputDialog(
|
||||||
|
title = stringResource(R.string.change_group),
|
||||||
|
hint = stringResource(R.string.group_name),
|
||||||
|
initialValue = rssStar.group,
|
||||||
|
suggestions = groups,
|
||||||
|
onDismissRequest = { showSetGroupDialog = null },
|
||||||
|
onConfirm = {
|
||||||
|
viewModel.updateGroup(rssStar, it)
|
||||||
|
showSetGroupDialog = null
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
RuleListScaffold(
|
RuleListScaffold(
|
||||||
title = stringResource(R.string.favorite),
|
title = stringResource(R.string.favorite),
|
||||||
|
subtitle = state.currentGroup.ifEmpty { stringResource(R.string.all) },
|
||||||
state = state,
|
state = state,
|
||||||
onBackClick = onBackClick,
|
onBackClick = onBackClick,
|
||||||
onSearchToggle = viewModel::onSearchToggle,
|
onSearchToggle = viewModel::onSearchToggle,
|
||||||
@@ -74,7 +116,16 @@ fun RssFavoritesScreen(
|
|||||||
onSelectInvert = viewModel::selectInvert,
|
onSelectInvert = viewModel::selectInvert,
|
||||||
onDeleteSelected = { viewModel.deleteSelected() },
|
onDeleteSelected = { viewModel.deleteSelected() },
|
||||||
snackbarHostState = snackbarHostState,
|
snackbarHostState = snackbarHostState,
|
||||||
selectionSecondaryActions = emptyList(),
|
selectionSecondaryActions = listOf(
|
||||||
|
ActionItem(
|
||||||
|
text = stringResource(R.string.add_group),
|
||||||
|
onClick = { showAddToGroupDialog = true }
|
||||||
|
),
|
||||||
|
ActionItem(
|
||||||
|
text = stringResource(R.string.remove_group),
|
||||||
|
onClick = { showRemoveFromGroupDialog = true }
|
||||||
|
)
|
||||||
|
),
|
||||||
topBarActions = {},
|
topBarActions = {},
|
||||||
dropDownMenuContent = { dismiss ->
|
dropDownMenuContent = { dismiss ->
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
@@ -128,7 +179,7 @@ fun RssFavoritesScreen(
|
|||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
Icon(Icons.Default.DeleteForever, null, modifier = Modifier.size(18.dp))
|
Icon(Icons.Default.DeleteForever, null, modifier = Modifier.size(18.dp))
|
||||||
Spacer(Modifier.width(12.dp))
|
Spacer(Modifier.width(12.dp))
|
||||||
Text(stringResource(R.string.delete_all))
|
Text(stringResource(R.string.all))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
@@ -136,23 +187,6 @@ fun RssFavoritesScreen(
|
|||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
},
|
|
||||||
bottomContent = {
|
|
||||||
if (groups.size > 1) {
|
|
||||||
PrimaryScrollableTabRow(
|
|
||||||
selectedTabIndex = groups.indexOf(state.currentGroup).coerceAtLeast(0),
|
|
||||||
edgePadding = 16.dp,
|
|
||||||
divider = {}
|
|
||||||
) {
|
|
||||||
groups.forEach { group ->
|
|
||||||
Tab(
|
|
||||||
selected = state.currentGroup == group,
|
|
||||||
onClick = { viewModel.onGroupChange(group) },
|
|
||||||
text = { Text(group) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
if (state.items.isEmpty()) {
|
if (state.items.isEmpty()) {
|
||||||
@@ -178,38 +212,48 @@ fun RssFavoritesScreen(
|
|||||||
val isSelected = state.selectedIds.contains(id)
|
val isSelected = state.selectedIds.contains(id)
|
||||||
SelectionItemCard(
|
SelectionItemCard(
|
||||||
title = rssStar.title,
|
title = rssStar.title,
|
||||||
subtitle = rssStar.pubDate,
|
subtitle = if (rssStar.group.isNotBlank()) {
|
||||||
|
"${rssStar.group} • ${rssStar.pubDate ?: ""}"
|
||||||
|
} else {
|
||||||
|
rssStar.pubDate
|
||||||
|
},
|
||||||
isSelected = isSelected,
|
isSelected = isSelected,
|
||||||
inSelectionMode = state.selectedIds.isNotEmpty(),
|
inSelectionMode = state.selectedIds.isNotEmpty(),
|
||||||
onToggleSelection = {
|
onToggleSelection = {
|
||||||
if (state.selectedIds.isNotEmpty()) {
|
viewModel.toggleSelection(rssStar)
|
||||||
viewModel.toggleSelection(rssStar)
|
},
|
||||||
} else {
|
leadingContent = if (!rssStar.image.isNullOrBlank()) {
|
||||||
|
{
|
||||||
|
SourceIcon(
|
||||||
|
path = rssStar.image,
|
||||||
|
sourceOrigin = rssStar.origin,
|
||||||
|
contentScale = ContentScale.Crop,
|
||||||
|
modifier = Modifier.size(54.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else null,
|
||||||
|
trailingAction = {
|
||||||
|
val openAction = {
|
||||||
context.startActivity<ReadRssActivity> {
|
context.startActivity<ReadRssActivity> {
|
||||||
putExtra("title", rssStar.title)
|
putExtra("title", rssStar.title)
|
||||||
putExtra("origin", rssStar.origin)
|
putExtra("origin", rssStar.origin)
|
||||||
putExtra("link", rssStar.link)
|
putExtra("link", rssStar.link)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
SmallIconButton(
|
||||||
trailingAction = {
|
onClick = openAction,
|
||||||
if (!rssStar.image.isNullOrBlank()) {
|
icon = Icons.AutoMirrored.Filled.OpenInNew,
|
||||||
AsyncImage(
|
contentDescription = "Open"
|
||||||
model = ImageRequest.Builder(LocalContext.current)
|
)
|
||||||
.data(rssStar.image)
|
|
||||||
.setHeader("sourceOrigin", rssStar.origin)
|
|
||||||
.crossfade(true)
|
|
||||||
.build(),
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier
|
|
||||||
.size(width = 80.dp, height = 50.dp)
|
|
||||||
.padding(start = 8.dp)
|
|
||||||
.clip(MaterialTheme.shapes.small),
|
|
||||||
contentScale = ContentScale.Crop
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
dropdownContent = { dismiss ->
|
dropdownContent = { dismiss ->
|
||||||
|
RoundDropdownMenuItem(
|
||||||
|
text = { Text(stringResource(R.string.change_group)) },
|
||||||
|
onClick = {
|
||||||
|
showSetGroupDialog = rssStar
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { Text(stringResource(R.string.delete)) },
|
text = { Text(stringResource(R.string.delete)) },
|
||||||
onClick = {
|
onClick = {
|
||||||
|
|||||||
@@ -37,8 +37,6 @@ class RssFavoritesViewModel(application: Application) : BaseViewModel(applicatio
|
|||||||
_selectedIds,
|
_selectedIds,
|
||||||
_currentGroup.flatMapLatest { group ->
|
_currentGroup.flatMapLatest { group ->
|
||||||
if (group.isEmpty()) {
|
if (group.isEmpty()) {
|
||||||
// If group is empty, we might want to wait for the first group from 'groups' flow
|
|
||||||
// Or just show nothing/default. The Activity/Screen should set the first group.
|
|
||||||
appDb.rssStarDao.liveAll()
|
appDb.rssStarDao.liveAll()
|
||||||
} else {
|
} else {
|
||||||
appDb.rssStarDao.flowByGroup(group)
|
appDb.rssStarDao.flowByGroup(group)
|
||||||
@@ -73,6 +71,10 @@ class RssFavoritesViewModel(application: Application) : BaseViewModel(applicatio
|
|||||||
_selectedIds.value = emptySet()
|
_selectedIds.value = emptySet()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setSelection(ids: Set<String>) {
|
||||||
|
_selectedIds.value = ids
|
||||||
|
}
|
||||||
|
|
||||||
fun toggleSelection(rssStar: RssStar) {
|
fun toggleSelection(rssStar: RssStar) {
|
||||||
val id = "${rssStar.origin}|${rssStar.link}"
|
val id = "${rssStar.origin}|${rssStar.link}"
|
||||||
_selectedIds.update {
|
_selectedIds.update {
|
||||||
@@ -108,6 +110,44 @@ class RssFavoritesViewModel(application: Application) : BaseViewModel(applicatio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun selectionAddToGroups(selectedIds: Set<String>, groupName: String) {
|
||||||
|
viewModelScope.launch(IO) {
|
||||||
|
state.value.items.forEach {
|
||||||
|
val id = "${it.origin}|${it.link}"
|
||||||
|
if (selectedIds.contains(id)) {
|
||||||
|
val groups = it.group.split(",").toMutableList()
|
||||||
|
if (!groups.contains(groupName)) {
|
||||||
|
groups.add(groupName)
|
||||||
|
appDb.rssStarDao.update(it.copy(group = groups.filter { g -> g.isNotBlank() }
|
||||||
|
.joinToString(",")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun selectionRemoveFromGroups(selectedIds: Set<String>, groupName: String) {
|
||||||
|
viewModelScope.launch(IO) {
|
||||||
|
state.value.items.forEach {
|
||||||
|
val id = "${it.origin}|${it.link}"
|
||||||
|
if (selectedIds.contains(id)) {
|
||||||
|
val groups = it.group.split(",").toMutableList()
|
||||||
|
if (groups.contains(groupName)) {
|
||||||
|
groups.remove(groupName)
|
||||||
|
appDb.rssStarDao.update(it.copy(group = groups.filter { g -> g.isNotBlank() }
|
||||||
|
.joinToString(",")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateGroup(rssStar: RssStar, group: String) {
|
||||||
|
viewModelScope.launch(IO) {
|
||||||
|
appDb.rssStarDao.update(rssStar.copy(group = group))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun deleteGroup(group: String) {
|
fun deleteGroup(group: String) {
|
||||||
viewModelScope.launch(IO) {
|
viewModelScope.launch(IO) {
|
||||||
appDb.rssStarDao.deleteByGroup(group)
|
appDb.rssStarDao.deleteByGroup(group)
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ fun RssSourceScreen(
|
|||||||
|
|
||||||
RuleListScaffold(
|
RuleListScaffold(
|
||||||
title = stringResource(R.string.rss_source),
|
title = stringResource(R.string.rss_source),
|
||||||
subtitle = uiState.groupFilterName,
|
subtitle = uiState.groupFilterName ?: stringResource(R.string.all),
|
||||||
state = uiState,
|
state = uiState,
|
||||||
onBackClick = { onBackClick() },
|
onBackClick = { onBackClick() },
|
||||||
onSearchToggle = { active -> viewModel.setSearchMode(active) },
|
onSearchToggle = { active -> viewModel.setSearchMode(active) },
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ fun SourceIcon(
|
|||||||
modifier: Modifier = Modifier.size(32.dp),
|
modifier: Modifier = Modifier.size(32.dp),
|
||||||
sourceOrigin: String? = null,
|
sourceOrigin: String? = null,
|
||||||
loadOnlyWifi: Boolean = false,
|
loadOnlyWifi: Boolean = false,
|
||||||
|
contentScale: ContentScale = ContentScale.Fit,
|
||||||
imageLoader: ImageLoader = koinInject(),
|
imageLoader: ImageLoader = koinInject(),
|
||||||
placeholderIcon: @Composable () -> Unit = {
|
placeholderIcon: @Composable () -> Unit = {
|
||||||
Icon(
|
Icon(
|
||||||
@@ -59,7 +60,7 @@ fun SourceIcon(
|
|||||||
.build(),
|
.build(),
|
||||||
imageLoader = imageLoader,
|
imageLoader = imageLoader,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
contentScale = ContentScale.Fit, // 不裁切
|
contentScale = contentScale, // 不裁切
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
package io.legado.app.ui.widget.components.card
|
package io.legado.app.ui.widget.components.card
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedContent
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.animateColorAsState
|
import androidx.compose.animation.animateColorAsState
|
||||||
import androidx.compose.animation.animateContentSize
|
import androidx.compose.animation.animateContentSize
|
||||||
import androidx.compose.animation.core.FastOutSlowInEasing
|
import androidx.compose.animation.core.FastOutSlowInEasing
|
||||||
import androidx.compose.animation.core.animateDpAsState
|
import androidx.compose.animation.core.animateDpAsState
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.animation.expandHorizontally
|
|
||||||
import androidx.compose.animation.fadeIn
|
|
||||||
import androidx.compose.animation.fadeOut
|
|
||||||
import androidx.compose.animation.shrinkHorizontally
|
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@@ -24,7 +21,6 @@ import androidx.compose.material.icons.filled.Edit
|
|||||||
import androidx.compose.material.icons.filled.MoreVert
|
import androidx.compose.material.icons.filled.MoreVert
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
import androidx.compose.material3.Checkbox
|
import androidx.compose.material3.Checkbox
|
||||||
import androidx.compose.material3.DropdownMenu
|
|
||||||
import androidx.compose.material3.ListItem
|
import androidx.compose.material3.ListItem
|
||||||
import androidx.compose.material3.ListItemDefaults
|
import androidx.compose.material3.ListItemDefaults
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@@ -46,6 +42,7 @@ import androidx.compose.ui.unit.Dp
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.zIndex
|
import androidx.compose.ui.zIndex
|
||||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||||
|
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
|
||||||
import sh.calvin.reorderable.ReorderableItem
|
import sh.calvin.reorderable.ReorderableItem
|
||||||
import sh.calvin.reorderable.ReorderableLazyListState
|
import sh.calvin.reorderable.ReorderableLazyListState
|
||||||
|
|
||||||
@@ -59,6 +56,7 @@ fun SelectionItemCard(
|
|||||||
inSelectionMode: Boolean = false,
|
inSelectionMode: Boolean = false,
|
||||||
elevation: Dp = 0.dp,
|
elevation: Dp = 0.dp,
|
||||||
onToggleSelection: () -> Unit = {},
|
onToggleSelection: () -> Unit = {},
|
||||||
|
leadingContent: @Composable (() -> Unit)? = null,
|
||||||
onEnabledChange: ((Boolean) -> Unit)? = null,
|
onEnabledChange: ((Boolean) -> Unit)? = null,
|
||||||
onClickEdit: (() -> Unit)? = null,
|
onClickEdit: (() -> Unit)? = null,
|
||||||
trailingAction: @Composable (RowScope.() -> Unit)? = null,
|
trailingAction: @Composable (RowScope.() -> Unit)? = null,
|
||||||
@@ -87,20 +85,30 @@ fun SelectionItemCard(
|
|||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 4.dp)
|
|
||||||
.animateContentSize(),
|
.animateContentSize(),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = inSelectionMode,
|
visible = inSelectionMode || leadingContent != null
|
||||||
enter = fadeIn() + expandHorizontally(),
|
|
||||||
exit = fadeOut() + shrinkHorizontally()
|
|
||||||
) {
|
) {
|
||||||
Checkbox(
|
Box(
|
||||||
checked = isSelected,
|
modifier = Modifier.padding(start = 12.dp),
|
||||||
onCheckedChange = null,
|
contentAlignment = Alignment.Center
|
||||||
modifier = Modifier.padding(start = 12.dp)
|
) {
|
||||||
)
|
AnimatedContent(
|
||||||
|
targetState = inSelectionMode,
|
||||||
|
label = "LeadingContent"
|
||||||
|
) { selectionMode ->
|
||||||
|
if (selectionMode) {
|
||||||
|
Checkbox(
|
||||||
|
checked = isSelected,
|
||||||
|
onCheckedChange = null
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
leadingContent?.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ListItem(
|
ListItem(
|
||||||
@@ -159,7 +167,7 @@ fun SelectionItemCard(
|
|||||||
icon = Icons.Default.MoreVert,
|
icon = Icons.Default.MoreVert,
|
||||||
contentDescription = "More"
|
contentDescription = "More"
|
||||||
)
|
)
|
||||||
DropdownMenu(
|
RoundDropdownMenu(
|
||||||
expanded = showMenu,
|
expanded = showMenu,
|
||||||
onDismissRequest = { showMenu = false }
|
onDismissRequest = { showMenu = false }
|
||||||
) {
|
) {
|
||||||
@@ -185,6 +193,7 @@ fun LazyItemScope.ReorderableSelectionItem(
|
|||||||
inSelectionMode: Boolean = false,
|
inSelectionMode: Boolean = false,
|
||||||
canReorder: Boolean = true,
|
canReorder: Boolean = true,
|
||||||
onToggleSelection: () -> Unit = {},
|
onToggleSelection: () -> Unit = {},
|
||||||
|
leadingContent: @Composable (() -> Unit)? = null,
|
||||||
onEnabledChange: ((Boolean) -> Unit)? = null,
|
onEnabledChange: ((Boolean) -> Unit)? = null,
|
||||||
onClickEdit: (() -> Unit)? = null,
|
onClickEdit: (() -> Unit)? = null,
|
||||||
trailingAction: @Composable (RowScope.() -> Unit)? = null,
|
trailingAction: @Composable (RowScope.() -> Unit)? = null,
|
||||||
@@ -206,6 +215,7 @@ fun LazyItemScope.ReorderableSelectionItem(
|
|||||||
inSelectionMode = inSelectionMode,
|
inSelectionMode = inSelectionMode,
|
||||||
elevation = elevation,
|
elevation = elevation,
|
||||||
onToggleSelection = onToggleSelection,
|
onToggleSelection = onToggleSelection,
|
||||||
|
leadingContent = leadingContent,
|
||||||
onEnabledChange = onEnabledChange,
|
onEnabledChange = onEnabledChange,
|
||||||
onClickEdit = onClickEdit,
|
onClickEdit = onClickEdit,
|
||||||
trailingAction = trailingAction,
|
trailingAction = trailingAction,
|
||||||
|
|||||||
Reference in New Issue
Block a user