pref: 看图优化

This commit is contained in:
HuanCheng65 2023-09-21 16:10:27 +08:00
parent c112cf79eb
commit cbf1fe64c4
No known key found for this signature in database
GPG Key ID: 5EC9DD60A32C7360
4 changed files with 81 additions and 89 deletions

View File

@ -157,7 +157,6 @@ dependencies {
implementation "io.github.panpf.sketch3:sketch-compose:$sketch_version" implementation "io.github.panpf.sketch3:sketch-compose:$sketch_version"
implementation "io.github.panpf.sketch3:sketch-extensions:$sketch_version" implementation "io.github.panpf.sketch3:sketch-extensions:$sketch_version"
implementation "io.github.panpf.sketch3:sketch-gif-movie:$sketch_version" implementation "io.github.panpf.sketch3:sketch-gif-movie:$sketch_version"
implementation "io.github.panpf.sketch3:sketch-zoom:$sketch_version"
implementation "io.github.panpf.sketch3:sketch-okhttp:$sketch_version" implementation "io.github.panpf.sketch3:sketch-okhttp:$sketch_version"
def zoomimage_version = "1.0.0-alpha03" def zoomimage_version = "1.0.0-alpha03"

View File

@ -29,22 +29,22 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue 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.geometry.Offset
import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
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.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView import com.github.panpf.sketch.request.DisplayRequest
import com.github.panpf.sketch.displayImage import com.github.panpf.zoomimage.SketchZoomAsyncImage
import com.github.panpf.sketch.zoom.Edge import com.google.accompanist.systemuicontroller.SystemUiController
import com.github.panpf.sketch.zoom.ReadModeDecider
import com.github.panpf.sketch.zoom.SketchZoomImageView
import com.huanchengfly.tieba.post.R import com.huanchengfly.tieba.post.R
import com.huanchengfly.tieba.post.arch.BaseComposeActivityWithParcelable import com.huanchengfly.tieba.post.arch.BaseComposeActivityWithParcelable
import com.huanchengfly.tieba.post.arch.collectPartialAsState import com.huanchengfly.tieba.post.arch.collectPartialAsState
@ -56,65 +56,40 @@ import com.huanchengfly.tieba.post.ui.widgets.compose.LazyLoad
import com.huanchengfly.tieba.post.ui.widgets.compose.ProvideContentColor import com.huanchengfly.tieba.post.ui.widgets.compose.ProvideContentColor
import com.huanchengfly.tieba.post.utils.ImageUtil import com.huanchengfly.tieba.post.utils.ImageUtil
import com.huanchengfly.tieba.post.utils.download import com.huanchengfly.tieba.post.utils.download
import kotlinx.coroutines.launch import kotlinx.collections.immutable.persistentListOf
import kotlin.math.abs
import kotlin.math.roundToInt import kotlin.math.roundToInt
object MyReadModeDecider : ReadModeDecider {
override fun should(
imageWidth: Int,
imageHeight: Int,
viewWidth: Int,
viewHeight: Int
): Boolean {
val imageAspectRatio = imageHeight.toFloat() / imageWidth
val viewAspectRatio = viewHeight.toFloat() / viewWidth
return if (viewAspectRatio > 1f) {
imageAspectRatio >= viewAspectRatio * 1.25f
} else {
imageAspectRatio >= (1f / viewAspectRatio) * 3f
}
}
}
@Composable @Composable
private fun ViewPhoto( private fun ViewPhoto(
imageUri: String, imageUri: String,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onDrag: ((dx: Float, dy: Float, isAtEdge: Boolean) -> Unit)? = null onTap: (offset: Offset) -> Unit = {},
) { ) {
Box( Box(
modifier = modifier, modifier = modifier,
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
var progress by remember { mutableStateOf(0f) } val context = LocalContext.current
var progress by remember { mutableFloatStateOf(0f) }
var showProgress by remember { mutableStateOf(true) } var showProgress by remember { mutableStateOf(true) }
AndroidView( val request = remember(imageUri) {
factory = { DisplayRequest.Builder(context, imageUri)
SketchZoomImageView(it).apply { .listener(
readModeEnabled = true
readModeDecider = MyReadModeDecider
allowParentInterceptOnEdge = true
addOnViewDragListener { dx, dy ->
val isAtEdge = horScrollEdge != Edge.NONE
onDrag?.invoke(dx, dy, isAtEdge)
}
}
},
modifier = Modifier.fillMaxSize(),
update = {
it.displayImage(imageUri) {
listener(
onStart = { showProgress = true }, onStart = { showProgress = true },
onSuccess = { _, _ -> showProgress = false }, onSuccess = { _, _ -> showProgress = false },
onError = { _, _ -> showProgress = false }, onError = { _, _ -> showProgress = false },
onCancel = { showProgress = false } onCancel = { showProgress = false }
) )
progressListener { _, totalLength: Long, completedLength: Long -> .progressListener { _, totalLength, completedLength ->
progress = (completedLength.toDouble() / totalLength).toFloat() progress = completedLength.toFloat() / totalLength
}
} }
.build()
} }
SketchZoomAsyncImage(
request = request,
contentDescription = null,
modifier = Modifier.fillMaxSize(),
onTap = onTap,
) )
if (showProgress) { if (showProgress) {
Box( Box(
@ -150,7 +125,7 @@ class PhotoViewActivity : BaseComposeActivityWithParcelable<PhotoViewData>() {
} }
val items by viewModel.uiState.collectPartialAsState( val items by viewModel.uiState.collectPartialAsState(
prop1 = PhotoViewUiState::data, prop1 = PhotoViewUiState::data,
initial = emptyList() initial = persistentListOf()
) )
val initialIndex by viewModel.uiState.collectPartialAsState( val initialIndex by viewModel.uiState.collectPartialAsState(
prop1 = PhotoViewUiState::initialIndex, prop1 = PhotoViewUiState::initialIndex,
@ -172,14 +147,17 @@ class PhotoViewActivity : BaseComposeActivityWithParcelable<PhotoViewData>() {
prop1 = PhotoViewUiState::loadPicPageData, prop1 = PhotoViewUiState::loadPicPageData,
initial = LoadPicPageData() initial = LoadPicPageData()
) )
val pageCount by remember { derivedStateOf { items.size } } val pageCount by remember { derivedStateOf { items.size } }
Surface(color = Color.Black) {
if (items.isNotEmpty()) {
val coroutineScope = rememberCoroutineScope()
val pagerState = rememberPagerState(initialPage = initialIndex) { pageCount } val pagerState = rememberPagerState(initialPage = initialIndex) { pageCount }
LaunchedEffect(initialIndex) { LaunchedEffect(initialIndex) {
if (pagerState.currentPage != initialIndex) pagerState.scrollToPage(initialIndex) if (pagerState.currentPage != initialIndex) pagerState.scrollToPage(initialIndex)
} }
Surface(color = Color.Black) {
if (items.isNotEmpty()) {
LaunchedEffect(pagerState.currentPage, pageCount, loadPicPageData) { LaunchedEffect(pagerState.currentPage, pageCount, loadPicPageData) {
loadPicPageData?.let { loadPicPageData?.let {
val item = items[pagerState.currentPage] val item = items[pagerState.currentPage]
@ -205,30 +183,15 @@ class PhotoViewActivity : BaseComposeActivityWithParcelable<PhotoViewData>() {
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
HorizontalPager( HorizontalPager(
state = pagerState, state = pagerState,
key = { key = { items[it].picId }
"${items[it].overallIndex}"
}
) { ) {
val item = items[it] val item = items[it]
ViewPhoto( ViewPhoto(
imageUri = item.originUrl, imageUri = item.originUrl,
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
onDrag = { dx, dy, isAtEdge -> onTap = {
val currentPage = pagerState.currentPage finish()
if (abs(dy) < 15 && abs(dx) > 30 && isAtEdge) { },
val prevPage = currentPage - 1
val nextPage = currentPage + 1
if (dx > 0 && prevPage >= 0) {
coroutineScope.launch {
pagerState.animateScrollToPage(prevPage)
}
} else if (dx < 0 && nextPage < items.size) {
coroutineScope.launch {
pagerState.animateScrollToPage(nextPage)
}
}
}
}
) )
} }
Box( Box(
@ -313,6 +276,10 @@ class PhotoViewActivity : BaseComposeActivityWithParcelable<PhotoViewData>() {
} }
} }
override fun onCreateContent(systemUiController: SystemUiController) {
systemUiController.isSystemBarsVisible = false
}
override fun dispatchTouchEvent(ev: MotionEvent): Boolean { override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
return try { return try {
super.dispatchTouchEvent(ev) super.dispatchTouchEvent(ev)

View File

@ -10,6 +10,9 @@ import com.huanchengfly.tieba.post.arch.UiIntent
import com.huanchengfly.tieba.post.arch.UiState import com.huanchengfly.tieba.post.arch.UiState
import com.huanchengfly.tieba.post.models.protos.LoadPicPageData import com.huanchengfly.tieba.post.models.protos.LoadPicPageData
import com.huanchengfly.tieba.post.models.protos.PhotoViewData import com.huanchengfly.tieba.post.models.protos.PhotoViewData
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.catch
@ -62,6 +65,7 @@ class PhotoViewViewModel :
val hasPrev = items.first().overallIndex > 1 val hasPrev = items.first().overallIndex > 1
PhotoViewPartialChange.LoadPrev.Success( PhotoViewPartialChange.LoadPrev.Success(
hasPrev = hasPrev, hasPrev = hasPrev,
currentPicId = picId,
items = items items = items
) )
} }
@ -159,12 +163,15 @@ class PhotoViewViewModel :
val items = localItems + fetchedItems val items = localItems + fetchedItems
val hasNext = items.last().overallIndex < picAmount val hasNext = items.last().overallIndex < picAmount
val hasPrev = items.first().overallIndex > 1 val hasPrev = items.first().overallIndex > 1
val initialIndex =
items.indexOfFirst { it.picId == data.data_.picId }.takeIf { it != -1 }
?: (data.data_.picIndex - 1)
PhotoViewPartialChange.Init.Success( PhotoViewPartialChange.Init.Success(
hasPrev = hasPrev, hasPrev = hasPrev,
hasNext = hasNext, hasNext = hasNext,
totalAmount = picAmount, totalAmount = picAmount,
items = items, items = items,
initialIndex = data.data_.picIndex - 1, initialIndex = initialIndex,
loadPicPageData = data.data_ loadPicPageData = data.data_
) )
} }
@ -198,7 +205,7 @@ sealed interface PhotoViewPartialChange : PartialChange<PhotoViewUiState> {
override fun reduce(oldState: PhotoViewUiState): PhotoViewUiState = override fun reduce(oldState: PhotoViewUiState): PhotoViewUiState =
when (this) { when (this) {
is Success -> oldState.copy( is Success -> oldState.copy(
data = items, data = items.toImmutableList(),
hasNext = hasNext, hasNext = hasNext,
hasPrev = hasPrev, hasPrev = hasPrev,
totalAmount = totalAmount, totalAmount = totalAmount,
@ -216,7 +223,7 @@ sealed interface PhotoViewPartialChange : PartialChange<PhotoViewUiState> {
url = if (item.showOriginBtn) item.url else null, url = if (item.showOriginBtn) item.url else null,
overallIndex = index + 1 overallIndex = index + 1
) )
}, }.toImmutableList(),
hasNext = false, hasNext = false,
hasPrev = false, hasPrev = false,
totalAmount = data.picItems.size, totalAmount = data.picItems.size,
@ -246,19 +253,26 @@ sealed interface PhotoViewPartialChange : PartialChange<PhotoViewUiState> {
when (this) { when (this) {
Start -> oldState.copy(isLoading = true) Start -> oldState.copy(isLoading = true)
is Success -> oldState.copy( is Success -> {
data = items.filterNot { item -> oldState.data.any { item.picId == it.picId } } + oldState.data, val items =
(items.filterNot { item -> oldState.data.any { item.picId == it.picId } } + oldState.data).toImmutableList()
val newIndex = items.indexOfFirst { it.picId == currentPicId }
oldState.copy(
data = items,
hasPrev = hasPrev, hasPrev = hasPrev,
isLoading = false isLoading = false,
initialIndex = newIndex
) )
}
is Failure -> oldState.copy(isLoading = false) is Failure -> oldState.copy(isLoading = false)
} }
object Start : LoadPrev() data object Start : LoadPrev()
data class Success( data class Success(
val items: List<PhotoViewItem>, val items: List<PhotoViewItem>,
val currentPicId: String,
val hasPrev: Boolean, val hasPrev: Boolean,
) : LoadPrev() ) : LoadPrev()
@ -273,7 +287,7 @@ sealed interface PhotoViewPartialChange : PartialChange<PhotoViewUiState> {
Start -> oldState.copy(isLoading = true) Start -> oldState.copy(isLoading = true)
is Success -> oldState.copy( is Success -> oldState.copy(
data = oldState.data + items.filterNot { item -> oldState.data.any { item.picId == it.picId } }, data = (oldState.data + items.filterNot { item -> oldState.data.any { item.picId == it.picId } }).toImmutableList(),
hasNext = hasNext, hasNext = hasNext,
isLoading = false isLoading = false
) )
@ -281,7 +295,7 @@ sealed interface PhotoViewPartialChange : PartialChange<PhotoViewUiState> {
is Failure -> oldState.copy(isLoading = false) is Failure -> oldState.copy(isLoading = false)
} }
object Start : LoadMore() data object Start : LoadMore()
data class Success( data class Success(
val items: List<PhotoViewItem>, val items: List<PhotoViewItem>,
@ -296,7 +310,7 @@ sealed interface PhotoViewPartialChange : PartialChange<PhotoViewUiState> {
data class PhotoViewUiState( data class PhotoViewUiState(
val isLoading: Boolean = false, val isLoading: Boolean = false,
val data: List<PhotoViewItem> = emptyList(), val data: ImmutableList<PhotoViewItem> = persistentListOf(),
val totalAmount: Int = 0, val totalAmount: Int = 0,
val hasNext: Boolean = false, val hasNext: Boolean = false,
val hasPrev: Boolean = false, val hasPrev: Boolean = false,

View File

@ -24,4 +24,16 @@
<item name="android:navigationBarColor">?colorNavBar</item> <item name="android:navigationBarColor">?colorNavBar</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item> <item name="android:windowDrawsSystemBarBackgrounds">true</item>
</style> </style>
<style name="AppTheme.PhotoView" parent="Theme.MaterialComponents.Light.NoActionBar">
<item name="android:windowAnimationStyle">@style/Animation.Fade</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
<item name="android:windowBackground">@color/transparent</item>
<item name="android:statusBarColor">@color/transparent</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item><!--控制刘海处显示信息-->
</style>
</resources> </resources>