feat: 支持无图模式

This commit is contained in:
HuanCheng65 2023-07-24 09:23:06 +08:00
parent 39b1619ad8
commit ad2ea7eefd
No known key found for this signature in database
GPG Key ID: 5EC9DD60A32C7360
9 changed files with 106 additions and 154 deletions

View File

@ -656,6 +656,7 @@ class App : Application(), IApp, SketchFactory {
context.getColorCompat(R.color.theme_color_nav_bar_surface_light) context.getColorCompat(R.color.theme_color_nav_bar_surface_light)
} }
} }
R.attr.colorOnNavBarSurface -> { R.attr.colorOnNavBarSurface -> {
return if (ThemeUtil.isNightMode(theme)) { return if (ThemeUtil.isNightMode(theme)) {
context.getColorCompat(R.color.theme_color_on_nav_bar_surface_dark) context.getColorCompat(R.color.theme_color_on_nav_bar_surface_dark)
@ -663,6 +664,20 @@ class App : Application(), IApp, SketchFactory {
context.getColorCompat(R.color.theme_color_on_nav_bar_surface_light) context.getColorCompat(R.color.theme_color_on_nav_bar_surface_light)
} }
} }
R.attr.colorPlaceholder -> {
return if (ThemeUtil.isTranslucentTheme(theme) || ThemeUtil.isNightMode(theme)) {
context.getColorCompat(
resources.getIdentifier(
"theme_color_placeholder_$theme",
"color",
packageName
)
)
} else {
context.getColorCompat(R.color.theme_color_placeholder_light)
}
}
} }
return Util.getColorByAttr(context, attrId, R.color.transparent) return Util.getColorByAttr(context, attrId, R.color.transparent)
} }

View File

@ -1,5 +1,6 @@
package com.huanchengfly.tieba.post.ui.common.theme.compose package com.huanchengfly.tieba.post.ui.common.theme.compose
import android.annotation.SuppressLint
import androidx.compose.material.Colors import androidx.compose.material.Colors
import androidx.compose.material.ContentAlpha import androidx.compose.material.ContentAlpha
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
@ -22,67 +23,40 @@ import com.huanchengfly.tieba.post.utils.compose.darken
data class ExtendedColors( data class ExtendedColors(
val theme: String, val theme: String,
val isNightMode: Boolean, val isNightMode: Boolean,
val primary: Color, val primary: Color = Color.Unspecified,
val accent: Color, val accent: Color = Color.Unspecified,
val onAccent: Color, val onAccent: Color = Color.Unspecified,
val topBar: Color, val topBar: Color = Color.Unspecified,
val onTopBar: Color, val onTopBar: Color = Color.Unspecified,
val onTopBarSecondary: Color, val onTopBarSecondary: Color = Color.Unspecified,
val onTopBarActive: Color, val onTopBarActive: Color = Color.Unspecified,
val topBarSurface: Color, val topBarSurface: Color = Color.Unspecified,
val onTopBarSurface: Color, val onTopBarSurface: Color = Color.Unspecified,
val bottomBar: Color, val bottomBar: Color = Color.Unspecified,
val bottomBarSurface: Color, val bottomBarSurface: Color = Color.Unspecified,
val onBottomBarSurface: Color, val onBottomBarSurface: Color = Color.Unspecified,
val text: Color, val text: Color = Color.Unspecified,
val textSecondary: Color, val textSecondary: Color = Color.Unspecified,
val textOnPrimary: Color, val textOnPrimary: Color = Color.Unspecified,
val textDisabled: Color, val textDisabled: Color = Color.Unspecified,
val background: Color, val background: Color = Color.Unspecified,
val chip: Color, val chip: Color = Color.Unspecified,
val onChip: Color, val onChip: Color = Color.Unspecified,
val unselected: Color, val unselected: Color = Color.Unspecified,
val card: Color, val card: Color = Color.Unspecified,
val floorCard: Color, val floorCard: Color = Color.Unspecified,
val divider: Color, val divider: Color = Color.Unspecified,
val shadow: Color, val shadow: Color = Color.Unspecified,
val indicator: Color, val indicator: Color = Color.Unspecified,
val windowBackground: Color, val windowBackground: Color = Color.Unspecified,
val placeholder: Color = Color.Unspecified,
) )
val LocalExtendedColors = staticCompositionLocalOf { val LocalExtendedColors = staticCompositionLocalOf {
ExtendedColors( ExtendedColors("", false)
"",
false,
Color.Unspecified,
Color.Unspecified,
Color.Unspecified,
Color.Unspecified,
Color.Unspecified,
Color.Unspecified,
Color.Unspecified,
Color.Unspecified,
Color.Unspecified,
Color.Unspecified,
Color.Unspecified,
Color.Unspecified,
Color.Unspecified,
Color.Unspecified,
Color.Unspecified,
Color.Unspecified,
Color.Unspecified,
Color.Unspecified,
Color.Unspecified,
Color.Unspecified,
Color.Unspecified,
Color.Unspecified,
Color.Unspecified,
Color.Unspecified,
Color.Unspecified,
Color.Unspecified,
)
} }
@SuppressLint("ConflictingOnColor")
fun getColorPalette( fun getColorPalette(
darkTheme: Boolean, darkTheme: Boolean,
extendedColors: ExtendedColors extendedColors: ExtendedColors
@ -172,13 +146,7 @@ private fun getThemeColorForTheme(theme: String): ExtendedColors {
Color(App.ThemeDelegate.getColorByAttr(App.INSTANCE, R.attr.colorFloorCard, nowTheme)), Color(App.ThemeDelegate.getColorByAttr(App.INSTANCE, R.attr.colorFloorCard, nowTheme)),
Color(App.ThemeDelegate.getColorByAttr(App.INSTANCE, R.attr.colorDivider, nowTheme)), Color(App.ThemeDelegate.getColorByAttr(App.INSTANCE, R.attr.colorDivider, nowTheme)),
Color(App.ThemeDelegate.getColorByAttr(App.INSTANCE, R.attr.shadow_color, nowTheme)), Color(App.ThemeDelegate.getColorByAttr(App.INSTANCE, R.attr.shadow_color, nowTheme)),
Color( Color(App.ThemeDelegate.getColorByAttr(App.INSTANCE, R.attr.colorIndicator, nowTheme)),
App.ThemeDelegate.getColorByAttr(
App.INSTANCE,
R.attr.colorIndicator,
nowTheme
)
),
Color( Color(
App.ThemeDelegate.getColorByAttr( App.ThemeDelegate.getColorByAttr(
App.INSTANCE, App.INSTANCE,
@ -186,6 +154,7 @@ private fun getThemeColorForTheme(theme: String): ExtendedColors {
nowTheme nowTheme
) )
), ),
Color(App.ThemeDelegate.getColorByAttr(App.INSTANCE, R.attr.colorPlaceholder, nowTheme)),
) )
} }

View File

@ -8,10 +8,7 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.layout.wrapContentSize
@ -30,11 +27,9 @@ import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
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.draw.clip
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.text.font.FontFamily import androidx.compose.ui.text.font.FontFamily
@ -45,10 +40,8 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.google.accompanist.placeholder.material.placeholder import com.google.accompanist.placeholder.material.placeholder
import com.huanchengfly.tieba.post.R import com.huanchengfly.tieba.post.R
import com.huanchengfly.tieba.post.api.models.protos.ThreadInfo
import com.huanchengfly.tieba.post.api.models.protos.hasAgree import com.huanchengfly.tieba.post.api.models.protos.hasAgree
import com.huanchengfly.tieba.post.arch.GlobalEvent import com.huanchengfly.tieba.post.arch.GlobalEvent
import com.huanchengfly.tieba.post.arch.ImmutableHolder
import com.huanchengfly.tieba.post.arch.collectPartialAsState import com.huanchengfly.tieba.post.arch.collectPartialAsState
import com.huanchengfly.tieba.post.arch.onGlobalEvent import com.huanchengfly.tieba.post.arch.onGlobalEvent
import com.huanchengfly.tieba.post.arch.pageViewModel import com.huanchengfly.tieba.post.arch.pageViewModel
@ -63,7 +56,6 @@ import com.huanchengfly.tieba.post.ui.page.destinations.HotTopicListPageDestinat
import com.huanchengfly.tieba.post.ui.page.destinations.ThreadPageDestination import com.huanchengfly.tieba.post.ui.page.destinations.ThreadPageDestination
import com.huanchengfly.tieba.post.ui.widgets.compose.FeedCard import com.huanchengfly.tieba.post.ui.widgets.compose.FeedCard
import com.huanchengfly.tieba.post.ui.widgets.compose.LazyLoad import com.huanchengfly.tieba.post.ui.widgets.compose.LazyLoad
import com.huanchengfly.tieba.post.ui.widgets.compose.NetworkImage
import com.huanchengfly.tieba.post.ui.widgets.compose.ProvideContentColor import com.huanchengfly.tieba.post.ui.widgets.compose.ProvideContentColor
import com.huanchengfly.tieba.post.ui.widgets.compose.VerticalDivider import com.huanchengfly.tieba.post.ui.widgets.compose.VerticalDivider
import com.huanchengfly.tieba.post.ui.widgets.compose.VerticalGrid import com.huanchengfly.tieba.post.ui.widgets.compose.VerticalGrid
@ -394,74 +386,6 @@ private fun ThreadListItemPlaceholder() {
} }
} }
@Composable
private fun ThreadListItem(
index: Int,
itemHolder: ImmutableHolder<ThreadInfo>,
onClick: (ThreadInfo) -> Unit = {}
) {
val item = remember(itemHolder) { itemHolder.get() }
val heightModifier = if (item.media.isEmpty()) Modifier else Modifier.height(80.dp)
Row(
modifier = Modifier
.clickable { onClick(item) }
.padding(all = 16.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
Row(
modifier = heightModifier.weight(1f),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.Top
) {
Text(
text = "${index + 1}",
fontWeight = FontWeight.Bold,
fontSize = 10.sp,
color = ExtendedTheme.colors.background,
modifier = Modifier
.padding(top = 2.dp)
.clip(RoundedCornerShape(4.dp))
.wrapContentSize()
.background(
color = when (index) {
0 -> RedA700
1 -> OrangeA700
2 -> Yellow
else -> MaterialTheme.colors.onBackground.copy(
ContentAlpha.medium
)
}
)
.padding(vertical = 1.dp, horizontal = 4.dp)
)
Column(verticalArrangement = Arrangement.SpaceBetween, modifier = heightModifier) {
Text(text = item.title, maxLines = 2, overflow = TextOverflow.Ellipsis)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = stringResource(id = R.string.hot_num, item.hotNum.getShortNumString()),
style = MaterialTheme.typography.caption,
color = ExtendedTheme.colors.textSecondary
)
}
}
if (!item.videoInfo?.thumbnailUrl.isNullOrBlank()) {
NetworkImage(
imageUri = item.videoInfo?.thumbnailUrl!!,
contentDescription = null,
modifier = heightModifier.aspectRatio(16f / 9),
contentScale = ContentScale.Crop
)
} else if (!item.media.firstOrNull()?.dynamicPic.isNullOrBlank()) {
NetworkImage(
imageUri = item.media.first().dynamicPic,
contentDescription = null,
modifier = heightModifier.aspectRatio(16f / 9),
contentScale = ContentScale.Crop
)
}
}
}
@Composable @Composable
private fun ThreadListTab( private fun ThreadListTab(
text: String, text: String,

View File

@ -81,8 +81,14 @@ fun Avatar(
size: Dp, size: Dp,
contentDescription: String?, contentDescription: String?,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
shape: Shape = CircleShape,
) { ) {
Avatar(data = data, contentDescription = contentDescription, modifier = modifier.size(size)) Avatar(
data = data,
contentDescription = contentDescription,
modifier = modifier.size(size),
shape = shape,
)
} }
@Composable @Composable
@ -90,6 +96,7 @@ fun Avatar(
data: String?, data: String?,
contentDescription: String?, contentDescription: String?,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
shape: Shape = CircleShape,
) { ) {
val context = LocalContext.current val context = LocalContext.current
@ -100,8 +107,7 @@ fun Avatar(
}, },
contentDescription = contentDescription, contentDescription = contentDescription,
contentScale = ContentScale.Crop, contentScale = ContentScale.Crop,
modifier = modifier modifier = modifier.clip(shape),
.clip(CircleShape),
) )
} }

View File

@ -311,13 +311,13 @@ private fun ForumInfoChip(
.padding(4.dp), .padding(4.dp),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
NetworkImage( Avatar(
imageUri = imageUri, data = imageUri,
contentDescription = null, contentDescription = null,
modifier = Modifier modifier = Modifier
.fillMaxHeight() .fillMaxHeight()
.aspectRatio(1f) .aspectRatio(1f),
.clip(RoundedCornerShape(4.dp)) shape = RoundedCornerShape(4.dp)
) )
Spacer(modifier = Modifier.width(8.dp)) Spacer(modifier = Modifier.width(8.dp))
Text( Text(
@ -391,7 +391,9 @@ private fun ThreadMedia(
NetworkImage( NetworkImage(
imageUri = remember(media) { media.url }, imageUri = remember(media) { media.url },
contentDescription = null, contentDescription = null,
modifier = Modifier.weight(1f), modifier = Modifier
.fillMaxHeight()
.weight(1f),
photoViewData = photoViewData, photoViewData = photoViewData,
contentScale = ContentScale.Crop contentScale = ContentScale.Crop
) )

View File

@ -1,16 +1,20 @@
package com.huanchengfly.tieba.post.ui.widgets.compose package com.huanchengfly.tieba.post.ui.widgets.compose
import android.content.Context
import android.os.Parcelable import android.os.Parcelable
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
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.rememberUpdatedState import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
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 com.github.panpf.sketch.compose.AsyncImage import com.github.panpf.sketch.compose.AsyncImage
import com.github.panpf.sketch.request.Depth
import com.github.panpf.sketch.request.DisplayRequest import com.github.panpf.sketch.request.DisplayRequest
import com.huanchengfly.tieba.post.arch.ImmutableHolder import com.huanchengfly.tieba.post.arch.ImmutableHolder
import com.huanchengfly.tieba.post.goToActivity import com.huanchengfly.tieba.post.goToActivity
@ -18,6 +22,19 @@ import com.huanchengfly.tieba.post.models.protos.PhotoViewData
import com.huanchengfly.tieba.post.ui.page.photoview.PhotoViewActivity import com.huanchengfly.tieba.post.ui.page.photoview.PhotoViewActivity
import com.huanchengfly.tieba.post.ui.page.photoview.PhotoViewActivity.Companion.EXTRA_PHOTO_VIEW_DATA import com.huanchengfly.tieba.post.ui.page.photoview.PhotoViewActivity.Companion.EXTRA_PHOTO_VIEW_DATA
import com.huanchengfly.tieba.post.utils.ImageUtil import com.huanchengfly.tieba.post.utils.ImageUtil
import com.huanchengfly.tieba.post.utils.NetworkUtil
import com.huanchengfly.tieba.post.utils.appPreferences
fun shouldLoadImage(context: Context, skipNetworkCheck: Boolean): Boolean {
val imageLoadSettings =
context.appPreferences.imageLoadType?.toIntOrNull() ?: ImageUtil.SETTINGS_SMART_ORIGIN
return skipNetworkCheck
|| imageLoadSettings == ImageUtil.SETTINGS_SMART_ORIGIN
|| imageLoadSettings == ImageUtil.SETTINGS_ALL_ORIGIN
|| (imageLoadSettings == ImageUtil.SETTINGS_SMART_LOAD && NetworkUtil.isWifiConnected(
context
))
}
@Composable @Composable
fun NetworkImage( fun NetworkImage(
@ -26,26 +43,34 @@ fun NetworkImage(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
photoViewData: ImmutableHolder<PhotoViewData>? = null, photoViewData: ImmutableHolder<PhotoViewData>? = null,
contentScale: ContentScale = ContentScale.Fit, contentScale: ContentScale = ContentScale.Fit,
skipNetworkCheck: Boolean = false,
) { ) {
val context = LocalContext.current val context = LocalContext.current
val clickableModifier = if (photoViewData != null) { var shouldLoad by remember { mutableStateOf(shouldLoadImage(context, skipNetworkCheck)) }
val clickableModifier = if (photoViewData != null || !shouldLoad) {
Modifier.clickable( Modifier.clickable(
indication = null, indication = null,
interactionSource = remember { interactionSource = remember { MutableInteractionSource() }
MutableInteractionSource()
}
) { ) {
if (!shouldLoad) {
shouldLoad = true
} else if (photoViewData != null) {
context.goToActivity<PhotoViewActivity> { context.goToActivity<PhotoViewActivity> {
putExtra(EXTRA_PHOTO_VIEW_DATA, photoViewData.get() as Parcelable) putExtra(EXTRA_PHOTO_VIEW_DATA, photoViewData.get() as Parcelable)
} }
} }
}
} else Modifier } else Modifier
val request = remember(imageUri) { val request = remember(imageUri, shouldLoad) {
DisplayRequest(context, imageUri) { DisplayRequest(context, imageUri) {
placeholder(ImageUtil.getPlaceHolder(context, 0)) placeholder(ImageUtil.getPlaceHolder(context, 0))
crossfade() crossfade()
if (!shouldLoad) {
depth(Depth.LOCAL)
} }
} }
}
AsyncImage( AsyncImage(
request = request, request = request,
contentDescription = contentDescription, contentDescription = contentDescription,
@ -61,6 +86,7 @@ fun NetworkImage(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
photoViewDataProvider: (() -> ImmutableHolder<PhotoViewData>)? = null, photoViewDataProvider: (() -> ImmutableHolder<PhotoViewData>)? = null,
contentScale: ContentScale = ContentScale.Fit, contentScale: ContentScale = ContentScale.Fit,
skipNetworkCheck: Boolean = false,
) { ) {
val imageUri by rememberUpdatedState(newValue = imageUriProvider()) val imageUri by rememberUpdatedState(newValue = imageUriProvider())
val photoViewData by rememberUpdatedState(newValue = photoViewDataProvider?.invoke()) val photoViewData by rememberUpdatedState(newValue = photoViewDataProvider?.invoke())
@ -71,5 +97,6 @@ fun NetworkImage(
modifier = modifier, modifier = modifier,
photoViewData = photoViewData, photoViewData = photoViewData,
contentScale = contentScale, contentScale = contentScale,
skipNetworkCheck = skipNetworkCheck,
) )
} }

View File

@ -28,6 +28,7 @@
<attr name="colorDivider" format="color" /> <attr name="colorDivider" format="color" />
<attr name="shadow_color" format="color" /> <attr name="shadow_color" format="color" />
<attr name="colorIndicator" format="color" /> <attr name="colorIndicator" format="color" />
<attr name="colorPlaceholder" format="color" />
</declare-styleable> </declare-styleable>
<declare-styleable name="ShadowLayout"> <declare-styleable name="ShadowLayout">

View File

@ -243,6 +243,13 @@
<color name="theme_color_shadow_day">@color/color_shadow</color> <color name="theme_color_shadow_day">@color/color_shadow</color>
<color name="theme_color_shadow_night">#FE191919</color> <color name="theme_color_shadow_night">#FE191919</color>
<color name="theme_color_placeholder_light">#FFF0F0F0</color>
<color name="theme_color_placeholder_blue_dark">#99000000</color>
<color name="theme_color_placeholder_grey_dark">#99000000</color>
<color name="theme_color_placeholder_amoled_dark">#99000000</color>
<color name="theme_color_placeholder_translucent_light">#15FFFFFF</color>
<color name="theme_color_placeholder_translucent_dark">#30000000</color>
<color name="red_accent">#FFC51100</color> <color name="red_accent">#FFC51100</color>
<color name="color_divider">#FFF7FBFE</color> <color name="color_divider">#FFF7FBFE</color>

View File

@ -707,4 +707,5 @@
<string name="below_is_latest_post">以下为最新回复</string> <string name="below_is_latest_post">以下为最新回复</string>
<string name="btn_open_origin_thread">查看原贴</string> <string name="btn_open_origin_thread">查看原贴</string>
<string name="tip_blocked_thread">由于你的屏蔽设置,该贴已被屏蔽</string> <string name="tip_blocked_thread">由于你的屏蔽设置,该贴已被屏蔽</string>
<string name="desc_picture">图片</string>
</resources> </resources>