添加拍照提示音和相册双指缩放功能
This commit is contained in:
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
Reference in New Issue
Block a user