首页 > 解决方案 > 如何通过单击按钮使新捕获的图像立即获得水印

问题描述

所以当我点击Floating Action Button时,我有一个关于相机应用程序的小项目,它不仅可以保存图像,而且在拍摄新图像后无法直接添加水印。我没有关于如何制作的想法,我想知道的是如何从Textviews制作水印,应用于我制作的新照片并自动保存它。我为此使用了CanvasPaintBitmap,但它们似乎都没有尝试让它工作。它仅保存在设备中捕获的原始图像。

显现

<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 文件相同的位置的问题,请告诉我在哪里放置和做什么。先感谢您。

标签: androidandroid-studiokotlinwatermark

解决方案


推荐阅读