我正在用 Kotlin 语言制作一个绘画应用程序。绘图、着色、撤消、清除、保存等一切正常。但是当我在画画时改变形式时,我画的画就消失了。我想画一点,切换到另一种形式再回来,但我的画不应该被删除。
package com.coding.curvedbottomnavigationapp
import android.content.Context
import android.graphics.*
import android.os.Environment
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.widget.Toast // Toast kütüphanesi eklendi
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
class PaintView(context: Context, attrs: AttributeSet) : View(context, attrs) {
private var currentPaint = Paint().apply {
isAntiAlias = true
style = Paint.Style.STROKE
strokeJoin = Paint.Join.ROUND
strokeCap = Paint.Cap.ROUND
strokeWidth = 10f
color = Color.BLACK // Varsayılan renk siyah
}
private var strokeWidth = 10f
private var paintColor = Color.BLACK
// Her bir çizim yolunu ve ona karşılık gelen Paint nesnesini saklayacağımız yapı
private val paths = mutableListOf<Pair<Path, Paint>>()
private var currentPath: Path? = null
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Tüm yolları ve onların kendi boyama stillerini çiz
for ((path, paint) in paths) {
canvas.drawPath(path, paint)
}
// Halen çizilmekte olan yolu çiz
currentPath?.let { canvas.drawPath(it, currentPaint) }
}
override fun onTouchEvent(event: MotionEvent): Boolean {
val point = PointF(event.x, event.y)
when (event.action) {
MotionEvent.ACTION_DOWN -> {
// Yeni bir yol oluştur ve onu mevcut boyama ayarlarıyla başlat
currentPath = Path().apply {
moveTo(point.x, point.y)
}
return true
}
MotionEvent.ACTION_MOVE -> {
// Yolu güncelle
currentPath?.lineTo(point.x, point.y)
}
MotionEvent.ACTION_UP -> {
// Çizim bitince yolu ve boyama ayarlarını sakla
currentPath?.let {
paths.add(Pair(it, Paint(currentPaint))) // Paint nesnesini kopyalayarak sakla
currentPath = null
}
}
}
invalidate() // Ekranı güncelle
return true
}
// Renk değiştirme fonksiyonu
fun setPaintColor(color: Int) {
paintColor = color
currentPaint.color = color // Mevcut boyama stiline yeni rengi uygula
invalidate()
}
// Kalem genişliği değiştirme fonksiyonu
fun setStrokeWidth(width: Float) {
strokeWidth = width
currentPaint.strokeWidth = width // Mevcut boyama stiline yeni kalem genişliğini uygula
invalidate()
}
// Geri alma fonksiyonu
fun undo() {
if (paths.isNotEmpty()) {
paths.removeAt(paths.size - 1)
invalidate()
}
}
// Tüm çizimleri temizleme fonksiyonu
fun clear() {
paths.clear()
invalidate()
}
// Çizimi kaydetme fonksiyonu
fun saveDrawing() {
// Çizimi bitmap olarak oluştur
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
draw(canvas) // Çizimi bitmape çiz
// Kaydetme işlemi
val file = File(context.getExternalFilesDir(null), "drawing_${System.currentTimeMillis()}.png")
try {
FileOutputStream(file).use { output ->
bitmap.compress(Bitmap.CompressFormat.PNG, 100, output)
Toast.makeText(context, "Çizim kaydedildi: ${file.absolutePath}", Toast.LENGTH_LONG).show()
}
} catch (e: IOException) {
e.printStackTrace()
Toast.makeText(context, "Kaydetme işlemi başarısız.", Toast.LENGTH_SHORT).show()
}
}
}
package com.coding.curvedbottomnavigationapp.fragments
import android.Manifest
import android.content.pm.PackageManager
import android.graphics.Color
import android.os.Bundle
import android.speech.tts.TextToSpeech
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.SeekBar
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import com.coding.curvedbottomnavigationapp.MainActivity
import com.coding.curvedbottomnavigationapp.R
import com.coding.curvedbottomnavigationapp.PaintView
import java.util.*
class HomeFragment : Fragment(), TextToSpeech.OnInitListener {
private lateinit var paintView: PaintView
private lateinit var buttonUndo: Button
private lateinit var buttonClear: Button
private lateinit var buttonSave: Button
private lateinit var seekBarStrokeWidth: SeekBar
private lateinit var tts: TextToSpeech // TTS tanımlandı
companion object {
private const val STORAGE_PERMISSION_CODE = 1001
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_home, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
paintView = view.findViewById(R.id.paintView)
buttonUndo = view.findViewById(R.id.buttonUndo)
buttonClear = view.findViewById(R.id.buttonClear)
buttonSave = view.findViewById(R.id.buttonSave)
seekBarStrokeWidth = view.findViewById(R.id.seekBarStrokeWidth)
// Text-to-Speech başlatma
tts = TextToSpeech(context, this)
// İzinleri kontrol et
requestStoragePermissions()
// Geri alma işlemi
buttonUndo.setOnClickListener {
paintView.undo()
playColorExplanation("Çizim geri alındı")
}
// Temizleme işlemi
buttonClear.setOnClickListener {
paintView.clear()
playColorExplanation("Ekran temizlendi")
}
// Kalem kalınlığını ayarlama
seekBarStrokeWidth.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
paintView.setStrokeWidth(progress.toFloat())
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {}
override fun onStopTrackingTouch(seekBar: SeekBar?) {}
})
// Kaydetme işlemi
buttonSave.setOnClickListener {
paintView.saveDrawing() // Çizimi kaydet
playColorExplanation("Muhteşem bir resim kaydettin")
}
// Renk seçimi butonları için tıklama olaylarını ekleme
view.findViewById<Button>(R.id.buttonRed).setOnClickListener {
paintView.setPaintColor(Color.RED)
playColorExplanation("Kırmızı renk seçildi") // Sesli uyarı
}
view.findViewById<Button>(R.id.buttonGreen).setOnClickListener {
paintView.setPaintColor(Color.GREEN)
playColorExplanation("Yeşil renk seçildi") // Sesli uyarı
}
view.findViewById<Button>(R.id.buttonBlue).setOnClickListener {
paintView.setPaintColor(Color.BLUE)
playColorExplanation("Mavi renk seçildi") // Sesli uyarı
}
view.findViewById<Button>(R.id.buttonYellow).setOnClickListener {
paintView.setPaintColor(Color.YELLOW)
playColorExplanation("Sarı renk seçildi") // Sesli uyarı
}
view.findViewById<Button>(R.id.buttonBlack).setOnClickListener {
paintView.setPaintColor(Color.BLACK)
playColorExplanation("Siyah renk seçildi") // Sesli uyarı
}
}
// TTS başlatıldığında bu fonksiyon çalışır
override fun onInit(status: Int) {
if (status == TextToSpeech.SUCCESS) {
// Dil olarak Türkçe seçiyoruz
val result = tts.setLanguage(Locale("tr", "TR"))
if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
speakOut("Bu dil desteklenmiyor")
}
} else {
speakOut("Text to Speech başlatılamadı")
}
}
// Sesli mesaj oynatma fonksiyonu
private fun speakOut(message: String) {
tts.speak(message, TextToSpeech.QUEUE_FLUSH, null, "")
}
// Text-to-Speech ile renk seçimi açıklaması yaparken müziği durdurma ve yeniden başlatma
private fun playColorExplanation(message: String) {
(activity as? MainActivity)?.let { mainActivity ->
val wasPlaying = mainActivity.isMusicPlaying() // mediaPlayer yerine getter kullanılıyor
mainActivity.pauseMusic()
speakOut(message)
tts.setOnUtteranceCompletedListener {
// Eğer müzik başlangıçta çalıyorduysa tekrar başlat
if (wasPlaying) {
mainActivity.playMusic()
}
}
}
}
override fun onDestroy() {
// TTS kaynağını serbest bırak
if (tts != null) {
tts.stop()
tts.shutdown()
}
super.onDestroy()
}
private fun requestStoragePermissions() {
if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(requireActivity(), arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), STORAGE_PERMISSION_CODE)
}
}
}
大家好,我正在用Kotlin语言制作一个绘画绘图应用程序。绘图、着色、撤消、清除、保存等一切正常。但是当我在画画时改变形式时,我画的画就消失了。我想画一点,切换到另一种形式再回来,但我的画不应该被删除。
package com.coding.curvedbottomnavigationapp
import android.content.Context
import android.graphics.*
import android.os.Environment
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.widget.Toast // Toast kütüphanesi eklendi
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
class PaintView(context: Context, attrs: AttributeSet) : View(context, attrs) {
private var currentPaint = Paint().apply {
isAntiAlias = true
style = Paint.Style.STROKE
strokeJoin = Paint.Join.ROUND
strokeCap = Paint.Cap.ROUND
strokeWidth = 10f
color = Color.BLACK // Varsayılan renk siyah
}
private var strokeWidth = 10f
private var paintColor = Color.BLACK
// Her bir çizim yolunu ve ona karşılık gelen Paint nesnesini saklayacağımız yapı
private val paths = mutableListOf<Pair<Path, Paint>>()
private var currentPath: Path? = null
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Tüm yolları ve onların kendi boyama stillerini çiz
for ((path, paint) in paths) {
canvas.drawPath(path, paint)
}
// Halen çizilmekte olan yolu çiz
currentPath?.let { canvas.drawPath(it, currentPaint) }
}
override fun onTouchEvent(event: MotionEvent): Boolean {
val point = PointF(event.x, event.y)
when (event.action) {
MotionEvent.ACTION_DOWN -> {
// Yeni bir yol oluştur ve onu mevcut boyama ayarlarıyla başlat
currentPath = Path().apply {
moveTo(point.x, point.y)
}
return true
}
MotionEvent.ACTION_MOVE -> {
// Yolu güncelle
currentPath?.lineTo(point.x, point.y)
}
MotionEvent.ACTION_UP -> {
// Çizim bitince yolu ve boyama ayarlarını sakla
currentPath?.let {
paths.add(Pair(it, Paint(currentPaint))) // Paint nesnesini kopyalayarak sakla
currentPath = null
}
}
}
invalidate() // Ekranı güncelle
return true
}
// Renk değiştirme fonksiyonu
fun setPaintColor(color: Int) {
paintColor = color
currentPaint.color = color // Mevcut boyama stiline yeni rengi uygula
invalidate()
}
// Kalem genişliği değiştirme fonksiyonu
fun setStrokeWidth(width: Float) {
strokeWidth = width
currentPaint.strokeWidth = width // Mevcut boyama stiline yeni kalem genişliğini uygula
invalidate()
}
// Geri alma fonksiyonu
fun undo() {
if (paths.isNotEmpty()) {
paths.removeAt(paths.size - 1)
invalidate()
}
}
// Tüm çizimleri temizleme fonksiyonu
fun clear() {
paths.clear()
invalidate()
}
// Çizimi kaydetme fonksiyonu
fun saveDrawing() {
// Çizimi bitmap olarak oluştur
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
draw(canvas) // Çizimi bitmape çiz
// Kaydetme işlemi
val file = File(context.getExternalFilesDir(null), "drawing_${System.currentTimeMillis()}.png")
try {
FileOutputStream(file).use { output ->
bitmap.compress(Bitmap.CompressFormat.PNG, 100, output)
Toast.makeText(context, "Çizim kaydedildi: ${file.absolutePath}", Toast.LENGTH_LONG).show()
}
} catch (e: IOException) {
e.printStackTrace()
Toast.makeText(context, "Kaydetme işlemi başarısız.", Toast.LENGTH_SHORT).show()
}
}
}
package com.coding.curvedbottomnavigationapp.fragments
import android.Manifest
import android.content.pm.PackageManager
import android.graphics.Color
import android.os.Bundle
import android.speech.tts.TextToSpeech
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.SeekBar
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import com.coding.curvedbottomnavigationapp.MainActivity
import com.coding.curvedbottomnavigationapp.R
import com.coding.curvedbottomnavigationapp.PaintView
import java.util.*
class HomeFragment : Fragment(), TextToSpeech.OnInitListener {
private lateinit var paintView: PaintView
private lateinit var buttonUndo: Button
private lateinit var buttonClear: Button
private lateinit var buttonSave: Button
private lateinit var seekBarStrokeWidth: SeekBar
private lateinit var tts: TextToSpeech // TTS tanımlandı
companion object {
private const val STORAGE_PERMISSION_CODE = 1001
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_home, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
paintView = view.findViewById(R.id.paintView)
buttonUndo = view.findViewById(R.id.buttonUndo)
buttonClear = view.findViewById(R.id.buttonClear)
buttonSave = view.findViewById(R.id.buttonSave)
seekBarStrokeWidth = view.findViewById(R.id.seekBarStrokeWidth)
// Text-to-Speech başlatma
tts = TextToSpeech(context, this)
// İzinleri kontrol et
requestStoragePermissions()
// Geri alma işlemi
buttonUndo.setOnClickListener {
paintView.undo()
playColorExplanation("Çizim geri alındı")
}
// Temizleme işlemi
buttonClear.setOnClickListener {
paintView.clear()
playColorExplanation("Ekran temizlendi")
}
// Kalem kalınlığını ayarlama
seekBarStrokeWidth.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
paintView.setStrokeWidth(progress.toFloat())
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {}
override fun onStopTrackingTouch(seekBar: SeekBar?) {}
})
// Kaydetme işlemi
buttonSave.setOnClickListener {
paintView.saveDrawing() // Çizimi kaydet
playColorExplanation("Muhteşem bir resim kaydettin")
}
// Renk seçimi butonları için tıklama olaylarını ekleme
view.findViewById<Button>(R.id.buttonRed).setOnClickListener {
paintView.setPaintColor(Color.RED)
playColorExplanation("Kırmızı renk seçildi") // Sesli uyarı
}
view.findViewById<Button>(R.id.buttonGreen).setOnClickListener {
paintView.setPaintColor(Color.GREEN)
playColorExplanation("Yeşil renk seçildi") // Sesli uyarı
}
view.findViewById<Button>(R.id.buttonBlue).setOnClickListener {
paintView.setPaintColor(Color.BLUE)
playColorExplanation("Mavi renk seçildi") // Sesli uyarı
}
view.findViewById<Button>(R.id.buttonYellow).setOnClickListener {
paintView.setPaintColor(Color.YELLOW)
playColorExplanation("Sarı renk seçildi") // Sesli uyarı
}
view.findViewById<Button>(R.id.buttonBlack).setOnClickListener {
paintView.setPaintColor(Color.BLACK)
playColorExplanation("Siyah renk seçildi") // Sesli uyarı
}
}
// TTS başlatıldığında bu fonksiyon çalışır
override fun onInit(status: Int) {
if (status == TextToSpeech.SUCCESS) {
// Dil olarak Türkçe seçiyoruz
val result = tts.setLanguage(Locale("tr", "TR"))
if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
speakOut("Bu dil desteklenmiyor")
}
} else {
speakOut("Text to Speech başlatılamadı")
}
}
// Sesli mesaj oynatma fonksiyonu
private fun speakOut(message: String) {
tts.speak(message, TextToSpeech.QUEUE_FLUSH, null, "")
}
// Text-to-Speech ile renk seçimi açıklaması yaparken müziği durdurma ve yeniden başlatma
private fun playColorExplanation(message: String) {
(activity as? MainActivity)?.let { mainActivity ->
val wasPlaying = mainActivity.isMusicPlaying() // mediaPlayer yerine getter kullanılıyor
mainActivity.pauseMusic()
speakOut(message)
tts.setOnUtteranceCompletedListener {
// Eğer müzik başlangıçta çalıyorduysa tekrar başlat
if (wasPlaying) {
mainActivity.playMusic()
}
}
}
}
override fun onDestroy() {
// TTS kaynağını serbest bırak
if (tts != null) {
tts.stop()
tts.shutdown()
}
super.onDestroy()
}
private fun requestStoragePermissions() {
if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(requireActivity(), arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), STORAGE_PERMISSION_CODE)
}
}
}
大家好,我正在用Kotlin语言制作一个绘画绘图应用程序。绘图、着色、撤消、清除、保存等一切正常。但是当我在画画时改变形式时,我画的画就消失了。我想画一点,切换到另一种形式再回来,但我的画不应该被删除。
提前感谢您的回答。
从这里开始:
https://developer.android.com/guide/fragments/ saving-state
我在这里建议一种方法。
Fragment
类有一个名为 onSaveInstanceState
的方法,您可以重写该方法来跟踪 UI 状态。 实现 Parcelable
(Android 世界的 Serializable
)的类可以以在重建时可以恢复的方式保存。
当您实现将
onSaveInstanceState
状态置于 Parcelable
中的 Bundle
覆盖时,您可以在 onCreateView
和 onViewCreated
等方法中再次使用该数据作为 savedInstanceState
参数。 在其中一种方法中,您检查 savedInstanceState
是否存在数据,如果存在,您可以使用该数据重新创建视图的状态。
编码中最困难的部分是获取绘图数据的重要值——您的
List<Pair<Path, Paint>>
属性——并创建 Parcelable
实现来保存这些值,例如路径形状、路径坐标、油漆颜色、描边等
但是一旦你有了这些类,你的实现可能是这样的:
PaintView
一个访问当前路径-绘制对列表(getter)的方法PaintView
上编写一个方法来获取路径绘制对数据的列表,设置当前列表并渲染所有这些路径(setter)onSaveInstanceState
以调用视图的 getter 并创建列表的 Parcelable
表示并将其保存在包中savedInstanceState
片段创建。 如果有已保存的数据,请反转操作并从 Parcelable
数据创建绘画路径对列表,并调用 PaintView
上的设置器以重新渲染绘图。这种
savedInstanceState
方法仅适用于简单的绘图;对于复杂的绘图,持久数据对于状态包来说可能太大。
“使用片段保存状态”页面还讨论了使用
ViewModel
,它可以保留较大绘图的状态。