android - 如何通过单击按钮使新捕获的图像立即获得水印
问题描述
所以当我点击Floating Action Button时,我有一个关于相机应用程序的小项目,它不仅可以保存图像,而且在拍摄新图像后无法直接添加水印。我没有关于如何制作的想法,我想知道的是如何从Textviews制作水印,应用于我制作的新照片并自动保存它。我为此使用了Canvas、Paint和Bitmap,但它们似乎都没有尝试让它工作。它仅保存在设备中捕获的原始图像。
显现
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"tools:ignore="ScopedStorage"/>
Gradle 实现
implementation 'androidx.camera:camera-camera2:1.1.0-alpha01'
implementation 'androidx.camera:camera-lifecycle:1.1.0-alpha01'
implementation 'androidx.camera:camera-view:1.0.0-alpha21'
main_activity.xml
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<androidx.camera.view.PreviewView
android:id="@+id/previewView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:layout_width="150dp"
android:layout_height="100dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.022"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.025">
<TextView
android:id="@+id/testText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/test_view"
android:textColor="#E91E63" />
</LinearLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/camera_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="15dp"
android:importantForAccessibility="no"
app:backgroundTint="#B8AEAE"
app:borderWidth="5dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.kt
package com.example.bitmapwatermark
import android.Manifest
import android.annotation.SuppressLint
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Color.RED
import android.graphics.Paint
import android.graphics.Path
import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.google.android.material.floatingactionbutton.FloatingActionButton
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
class MainActivity : AppCompatActivity() {
/**watermark private variables**/
private var paint = Paint()
private val config = Bitmap.Config.ARGB_8888
private val width = 1
private val height = 1
private var bitmap = Bitmap.createBitmap(width, height, config)
/**Camera variables**/
private var imageCapture: ImageCapture? = null
private lateinit var cameraExecutor: ExecutorService
private lateinit var outputDirectory: File
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
/**Checking for all permissions to be granted through method,
* if so it will start the camera, if not checks for a request with permissions and request code**/
if (allPermissionsGranted()) {
startCamera()
} else {
ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
}
// Making a variable, connecting to the button and setting up a listener to initiate taking a photo through a method
val imageCaptureBtn = findViewById<FloatingActionButton>(R.id.camera_button)
imageCaptureBtn.setOnClickListener {
takePhoto().apply {
drawBitmap(bitmap, left = 5f, top = 5f, paint)
}
}
// Connecting the late init variable with a new method to get output directory
outputDirectory = getOutputDirectory()
cameraExecutor = Executors.newSingleThreadExecutor()
}
private fun getOutputDirectory(): File {
val mediaDir = externalMediaDirs.firstOrNull().let {
File(it, "BitmapWatermark").apply { mkdirs() } }
return if (mediaDir.exists())
mediaDir else filesDir
}
private fun takePhoto() {
val photoFile = File(outputDirectory, SimpleDateFormat(FILENAME_FORMAT, Locale.ENGLISH).format(System.currentTimeMillis()) + ".jpg")
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
imageCapture?.takePicture(
outputOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
val saveUri = Uri.fromFile(photoFile).apply { drawBitmap(bitmap, left = 5f, top = 5f, paint) }
val msg = "Photo capture succeeded: $saveUri"
Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
Log.d(TAG, msg)
}
override fun onError(exception: ImageCaptureException) {
Toast.makeText(baseContext, "Photo capture failed: ${exception.message}", Toast.LENGTH_SHORT).show()
photoFile.delete()
}
}
)
}
private fun startCamera() {
val viewFinder = findViewById<PreviewView>(R.id.previewView)
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
// Used to bind the lifecycle of cameras to the lifecycle owner
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
// Preview
val preview = Preview.Builder()
.build()
.also {
it.setSurfaceProvider(viewFinder.surfaceProvider)
}
imageCapture = ImageCapture.Builder()
.build()
// Select back camera as a default
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
// Unbind use cases before rebinding
cameraProvider.unbindAll()
// Bind use cases to camera
cameraProvider.bindToLifecycle(
this, cameraSelector, preview, imageCapture)
} catch (exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
}, ContextCompat.getMainExecutor(this))
}
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(
baseContext, it) == PackageManager.PERMISSION_GRANTED
}
@SuppressLint("SetTextI18n")
fun drawBitmap(bitmap: Bitmap, left: Float, top: Float, paint: Paint?) {
val textText = findViewById<TextView>(R.id.testText)
val paintText = Paint()
paintText.textAlign
paintText.textSize = 14F
paintText.color = RED
paintText.getTextPath(textText.toString(), 1, 10, 12f, 12f, path)
}
companion object {
private const val TAG = "CadIS for Android"
private const val FILENAME_FORMAT = "dd.MM.yyyy-HH:mm:ss"
private const val REQUEST_CODE_PERMISSIONS = 10
private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
private var path: Path = Path() // this gets us the new image
}
}
如果有人知道为什么它没有将TextView中的水印放在与主 xml 文件相同的位置的问题,请告诉我在哪里放置和做什么。先感谢您。
解决方案
推荐阅读
- google-apps-script - 如何获取 findText() 的下一个或上一个字符
- php - 不明白为什么我的 php 余弦相似度代码不起作用
- asp.net-core - 是否可以通过命名空间动态创建招摇规范?
- php - PHP 函数中的参数(或参数)如何工作?
- flutter - Flutter Android 应用程序在启动时崩溃,没有错误
- google-apps-script - GoogleSheets 触发器脚本仅在定义的时间之间运行
- node.js - 节点 - 如果一个查询失败,Mysql 事务不会回滚
- python - 将新值从表单添加到 django 中的现有值
- r - 如何删除 R 数据框中的“奇怪空间”?
- perl - 实现调度表