Недавно я создавал тестовое приложение для ознакомления с 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
в манифесте, если это поможет.
Спасибо.