Как сделать так, чтобы новое захваченное изображение мгновенно получало водяной знак нажатием кнопки

#android #android-studio #kotlin #watermark

#Android #android-studio #kotlin #водяной знак

Вопрос:

итак, у меня есть небольшой проект о приложении для камеры, когда я нажимаю на плавающую кнопку действия, оно не только может сохранить изображение, но я не могу добавить водяной знак сразу после создания нового изображения. У меня закончились идеи о том, как это сделать, и я хочу знать, как сделать водяные знаки из 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-файл, пожалуйста, покажите мне, куда поместить и что делать. Заранее благодарю вас.

Комментарии:

1. medium.com/@guygriv / … проверьте этот пост, это может вам помочь

2. @KrishnaSony Я попробовал это, но затем, когда я разместил метод и добавил все необходимые мне детали в качестве теста, сделав снимок, водяной знак по-прежнему не отображается после того, как я делаю снимок.