Файл не сохраняется при изменении ориентации - PullRequest
0 голосов
/ 05 июля 2018

Недавно я создавал тестовое приложение для ознакомления с RecyclerView и библиотекой Android Palette, когда натолкнулся на эту семантическую ошибку в моем фрагменте, который имеет дело с Palette. Когда я делаю снимок во фрагменте, он сохраняет фотографию в File для текущей ориентации, для альбомной ориентации, но когда я поворачиваю свой телефон обратно в портретную ориентацию, File сбрасывается обратно в null. Я обнаружил это, основываясь на моих тестах Log и чтении следов стека.

В настоящее время я обернул абсолютный путь null в проверку null, чтобы предотвратить дальнейшие ошибки, но я не уверен, что делать дальше. Ниже мой файл Kotlin.

class PicFragment : Fragment() {
 private var imgFile: File? = null
 private lateinit var cameraPic: ImageView
 private lateinit var cycleLayout: View
 private var swatchIndex: Int = 0


override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    val view: View? = inflater?.inflate(R.layout.camera_fragment, container, false)
    // init
    val cameraButton: ImageButton = view!!.findViewById(R.id.click_pic)
    val colorCycler: ImageButton = view.findViewById(R.id.color_clicker)
    cameraPic = view.findViewById(R.id.camera_pic)
    cycleLayout = view.findViewById(R.id.color_selector)
    val swatchDisplay: ImageView = view.findViewById(R.id.main_color)
    val swatchName: TextView = view.findViewById(R.id.main_color_name)

    // restoring the picture taken if it exists
    if(savedInstanceState != null){
        val path: String? = savedInstanceState.getString("imageFile")
        swatchIndex = savedInstanceState.getInt("swatchIndex")

        if(path != null) {
            val bm: Bitmap = BitmapFactory.decodeFile(path)
            cameraPic.setImageBitmap(bm)
            animateColorSlides(cycleLayout, duration = 500)
        }
    }

    // taking the picture (full size)
    cameraButton.setOnClickListener { _ ->
        val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
        if (intent.resolveActivity(context.packageManager) != null){
            imgFile = createFileName()
            val photoURI = FileProvider.getUriForFile(context, "com.github.astronoodles.provider", imgFile)
            grantUriPermissions(intent, photoURI)
            intent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
            startActivityForResult(intent, 3)
        }
    }
    // Palette Button (click to go through color values)
    colorCycler.setOnClickListener { _ ->
        if(cameraPic.drawable is BitmapDrawable){
            val img: Bitmap = (cameraPic.drawable as BitmapDrawable).bitmap
            Palette.from(img).generate { palette ->
                val swatches = palette.swatches
                Log.d(MainActivity.TAG, "Swatch Size: ${swatches.size}")
                Log.d(MainActivity.TAG, "Counter: $swatchIndex")

                val hexCode = "#${Integer.toHexString(swatches[swatchIndex++ % swatches.size].rgb)}"
                swatchName.text = hexCode
                animateColorDrawableFade(context, swatchDisplay, hexCode)
            }
        } else Log.e(MainActivity.TAG, "No bitmap found! Cannot cycle images...")
    }
    return view
}

override fun onSaveInstanceState(outState: Bundle?) {
    super.onSaveInstanceState(outState)
    outState?.putString("imageFile", imgFile?.absolutePath)
    outState?.putInt("swatchIndex", swatchIndex)
}

/**
 * Animates the color of an ImageView using its image drawable
 * @author Michael + StackOverflow
 * @since 6/24/18
 * @param ctx Context needed to load the animations
 * @param target Target ImageView for switching colors
 * @param hexCode The hex code of the colors switching in
 */
private fun animateColorDrawableFade(ctx: Context, target: ImageView, hexCode: String){
    val fadeOut = AnimationUtils.loadAnimation(ctx, android.R.anim.fade_out)
    val fadeIn = AnimationUtils.loadAnimation(ctx, android.R.anim.fade_in)
    fadeOut.setAnimationListener(object: Animation.AnimationListener {
        override fun onAnimationStart(animation: Animation?) {}
        override fun onAnimationRepeat(animation: Animation?) {}
        override fun onAnimationEnd(animation: Animation?) {
            target.setImageDrawable(ColorDrawable(Color.parseColor(hexCode)))
            target.startAnimation(fadeIn)
        }
    })

    target.startAnimation(fadeOut)
}

/**
 * Helper method for animating a layout's visibility from invisible and visible
 * @author Michael
 * @param layout The layout to animate
 * @param duration The length of the alpha animation.
 */
private fun animateColorSlides(layout: View, duration: Long){
    layout.alpha = 0f
    layout.visibility = View.VISIBLE

    layout.animate().alpha(1f).setListener(null).duration = duration
}

/**
 * Creates an unique name for the file as suggested here using a SimpleDateFormat
 * @author Michael
 * @returns A (temporary?) file linking to where the photo will be saved.
 */
private fun createFileName(): File {
    val timeStamp: String = SimpleDateFormat("yyyyMd_km", Locale.US).format(Date())
    val jpegTitle = "JPEG_${timeStamp}_"
    val directory: File = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
    try {
        return File.createTempFile(jpegTitle, ".png", directory)
    } catch (e: IOException) {
        e.printStackTrace()
    }
    return File(directory, "$jpegTitle.jpg")
}

/**
 * Grants URI permissions for the file provider to successfully save the full size file. <br>
 * Code borrowed from https://stackoverflow.com/questions/18249007/how-to-use-support-fileprovider-for-sharing-content-to-other-apps
 * @param intent The intent to send the photo
 * @param uri The URI retrieved from the FileProvider
 * @author Michael and Leszek
 */
private fun grantUriPermissions(intent: Intent, uri: Uri){
    val intentHandleList: List<ResolveInfo> = context.packageManager.queryIntentActivities(intent,
            PackageManager.MATCH_DEFAULT_ONLY)
    intentHandleList.forEach {
        val packageName: String = it.activityInfo.packageName
        context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION or
                Intent.FLAG_GRANT_READ_URI_PERMISSION)
    }
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if(requestCode == 3 && resultCode == Activity.RESULT_OK){
        val bitmap: Bitmap = BitmapFactory.decodeFile(imgFile!!.absolutePath)
        cameraPic.setImageBitmap(bitmap)
        animateColorSlides(cycleLayout, duration = 2000)
    }
}
}

У меня также есть разрешение WRITE_EXTERNAL_STORAGE в манифесте, если это поможет.

Спасибо.

1 Ответ

0 голосов
/ 05 июля 2018

Из документации по жизненному циклу активности Android , это соответствующая часть:

Если вы переопределяете onSaveInstanceState (), вы должны вызвать реализацию суперкласса, если хотите, чтобы реализация по умолчанию сохранила состояние иерархии представлений

Что даст вам что-то вроде этого:

override fun onSaveInstanceState(outState: Bundle?) {
    outState?.putString("imageFile", imgFile?.absolutePath)
    outState?.putInt("swatchIndex", swatchIndex)

    // Always call the superclass so it can save the view hierarchy state
    super.onSaveInstanceState(outState)
}
...