添加拍照提示音和相册双指缩放功能

This commit is contained in:
Developer
2026-03-27 23:08:00 +08:00
parent 2687aabae5
commit 1095eb557d
4 changed files with 74 additions and 21 deletions

View File

@@ -31,8 +31,8 @@ android {
buildTypes { buildTypes {
release { release {
isMinifyEnabled = true isMinifyEnabled = false
isShrinkResources = true isShrinkResources = false
proguardFiles( proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"), getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro" "proguard-rules.pro"
@@ -60,7 +60,7 @@ android {
} }
lint { lint {
abortOnError = false abortOnError = false
checkReleaseBuilds = true checkReleaseBuilds = false
warningsAsErrors = false warningsAsErrors = false
} }
} }

View File

@@ -546,6 +546,9 @@ private fun capturePhoto(
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build() val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
val executor = Executors.newSingleThreadExecutor() val executor = Executors.newSingleThreadExecutor()
// 拍照时立即播放快门声音和闪屏效果
onFeedback?.invoke()
imageCapture.takePicture( imageCapture.takePicture(
outputOptions, outputOptions,
executor, executor,
@@ -560,7 +563,6 @@ private fun capturePhoto(
} }
try { try {
onFeedback?.invoke()
val timeText = ImageProcessor.getCurrentTimeText() val timeText = ImageProcessor.getCurrentTimeText()
Log.d("CameraScreen", "Adding watermark: timeText=$timeText, locationText=$locationText") Log.d("CameraScreen", "Adding watermark: timeText=$timeText, locationText=$locationText")
val watermarkedBitmap = ImageProcessor.addWatermark( val watermarkedBitmap = ImageProcessor.addWatermark(

View File

@@ -5,6 +5,7 @@ import android.net.Uri
import android.provider.MediaStore import android.provider.MediaStore
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTransformGestures
import androidx.compose.foundation.layout.Arrangement 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
@@ -24,6 +25,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.CheckCircle import androidx.compose.material.icons.filled.CheckCircle
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Share import androidx.compose.material.icons.filled.Share
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
@@ -39,6 +41,7 @@ import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
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.rememberCoroutineScope
@@ -47,6 +50,8 @@ 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.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
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.unit.dp import androidx.compose.ui.unit.dp
@@ -194,19 +199,25 @@ fun GalleryScreen(
) )
} }
// 大图查看对话框 // 大图查看对话框(支持双指缩放)
if (selectedImageUri != null) { if (selectedImageUri != null) {
AlertDialog( AlertDialog(
onDismissRequest = { selectedImageUri = null }, onDismissRequest = { selectedImageUri = null },
title = { Text("图片预览") }, title = {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text("图片预览")
IconButton(onClick = { selectedImageUri = null }) {
Icon(Icons.Default.Close, contentDescription = "关闭")
}
}
},
text = { text = {
AsyncImage( ZoomableImage(
model = ImageRequest.Builder(LocalContext.current) imageUri = selectedImageUri!!,
.data(selectedImageUri)
.crossfade(true)
.build(),
contentDescription = "大图预览",
contentScale = ContentScale.Fit,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.aspectRatio(1f) .aspectRatio(1f)
@@ -221,6 +232,53 @@ fun GalleryScreen(
} }
} }
@Composable
private fun ZoomableImage(
imageUri: Uri,
modifier: Modifier = Modifier
) {
var scale by remember { mutableFloatStateOf(1f) }
var offsetX by remember { mutableFloatStateOf(0f) }
var offsetY by remember { mutableFloatStateOf(0f) }
Box(
modifier = modifier
.clip(RoundedCornerShape(8.dp))
.background(Color.Black.copy(alpha = 0.05f))
.pointerInput(Unit) {
detectTransformGestures { _, pan, zoom, _ ->
scale = (scale * zoom).coerceIn(1f, 5f)
if (scale > 1f) {
val maxOffsetX = (scale - 1) * size.width / 2
val maxOffsetY = (scale - 1) * size.height / 2
offsetX = (offsetX + pan.x).coerceIn(-maxOffsetX, maxOffsetX)
offsetY = (offsetY + pan.y).coerceIn(-maxOffsetY, maxOffsetY)
} else {
offsetX = 0f
offsetY = 0f
}
}
}
) {
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(imageUri)
.crossfade(true)
.build(),
contentDescription = "可缩放图片",
contentScale = ContentScale.Fit,
modifier = Modifier
.fillMaxSize()
.graphicsLayer(
scaleX = scale,
scaleY = scale,
translationX = offsetX,
translationY = offsetY
)
)
}
}
@Composable @Composable
private fun ImageItem( private fun ImageItem(
uri: Uri, uri: Uri,

View File

@@ -1,8 +1 @@
## This file must *NOT* be checked into Version Control Systems, sdk.dir=C:/android-sdk
# as it contains information specific to your local configuration.
#
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
#Sat Feb 28 14:32:19 CST 2026
sdk.dir=C\:\\Users\\xiaji\\AppData\\Local\\Android\\Sdk