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