APP头像更换开发需求:图库选图及拍照存储至指定目录
Hey there! Since you've already got the necessary permissions sorted out, let's walk through implementing the full flow for selecting/taking an avatar and saving it to your APP_NAME/images directory.
1. Create the Custom Dialog for Image Options
First, let's build the dialog with two ImageView options for gallery selection and camera capture. Start by creating a layout file (e.g., dialog_image_picker.xml) for the dialog content:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="16dp"> <ImageView android:id="@+id/iv_gallery" android:layout_width="0dp" android:layout_height="100dp" android:layout_weight="1" android:src="@drawable/ic_gallery" android:scaleType="centerCrop" android:contentDescription="Select from gallery"/> <View android:layout_width="16dp" android:layout_height="match_parent"/> <ImageView android:id="@+id/iv_camera" android:layout_width="0dp" android:layout_height="100dp" android:layout_weight="1" android:src="@drawable/ic_camera" android:scaleType="centerCrop" android:contentDescription="Take photo"/> </LinearLayout>
Then, inflate this layout in your activity/fragment to show the dialog when the "changePhoto" TextView is clicked:
// In your Activity/Fragment fun showImagePickerDialog() { val dialog = AlertDialog.Builder(this) .setView(R.layout.dialog_image_picker) .create() dialog.findViewById<ImageView>(R.id.iv_gallery)?.setOnClickListener { dialog.dismiss() openGallery() } dialog.findViewById<ImageView>(R.id.iv_camera)?.setOnClickListener { dialog.dismiss() openCamera() } dialog.show() } // Set click listener on your "changePhoto" TextView findViewById<TextView>(R.id.changePhoto).setOnClickListener { showImagePickerDialog() }
2. Handle Gallery Selection & Copy Image to Target Directory
We'll use ActivityResultContracts.GetContent() to pick an image from the gallery, then copy the selected image to your custom directory.
First, register the activity result launcher:
private val pickGalleryImageLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? -> uri?.let { val success = copyImageToTargetDirectory(it) if (success) { val fileName = getFileNameFromUri(it) updateAvatar("${getTargetDirectoryPath()}/$fileName") } else { // Handle copy failure (show toast, log error) } } } private fun openGallery() { pickGalleryImageLauncher.launch("image/*") }
Next, implement helper methods to manage the target directory and copy the image:
private fun getTargetDirectoryPath(): String { val appDir = File(getExternalFilesDir(null), "APP_NAME/images") if (!appDir.exists()) { appDir.mkdirs() // Create directory if it doesn't exist } return appDir.absolutePath } private fun copyImageToTargetDirectory(sourceUri: Uri): Boolean { return try { val inputStream = contentResolver.openInputStream(sourceUri) ?: return false val fileName = "avatar_${System.currentTimeMillis()}.jpg" val targetFile = File(getTargetDirectoryPath(), fileName) val outputStream = FileOutputStream(targetFile) inputStream.copyTo(outputStream) inputStream.close() outputStream.close() true } catch (e: IOException) { e.printStackTrace() false } } private fun getFileNameFromUri(uri: Uri): String { var fileName = "avatar_${System.currentTimeMillis()}.jpg" val cursor = contentResolver.query(uri, null, null, null, null) cursor?.let { if (it.moveToFirst()) { val nameIndex = it.getColumnIndex(OpenableColumns.DISPLAY_NAME) if (nameIndex != -1) { fileName = it.getString(nameIndex) } } it.close() } return fileName }
3. Handle Camera Capture & Save Directly to Target Directory
For camera capture, we'll create a file in the target directory first, then use ActivityResultContracts.TakePicture() to save the photo directly there.
Register the camera launcher:
private lateinit var cameraImageFile: File private val takePictureLauncher = registerForActivityResult(ActivityResultContracts.TakePicture()) { success: Boolean -> if (success) { updateAvatar(cameraImageFile.absolutePath) } else { // Handle capture failure } } private fun openCamera() { cameraImageFile = File(getTargetDirectoryPath(), "avatar_${System.currentTimeMillis()}.jpg") val imageUri = FileProvider.getUriForFile(this, "${packageName}.fileprovider", cameraImageFile) takePictureLauncher.launch(imageUri) }
Critical Setup: Add a FileProvider to your AndroidManifest.xml to safely share file URIs (required for Android 7.0+):
<application ...> <provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider> </application>
Then create res/xml/file_paths.xml:
<paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-files-path name="my_images" path="APP_NAME/images" /> </paths>
4. Update the Avatar Display
Finally, implement a method to refresh your UI with the new avatar (use an image loading library like Glide for better performance):
private fun updateAvatar(imagePath: String) { val avatarImageView = findViewById<ImageView>(R.id.your_avatar_imageview) Glide.with(this) .load(File(imagePath)) .circleCrop() // Optional: for circular avatar styling .into(avatarImageView) }
内容的提问来源于stack exchange,提问作者Ahmed Afifi

