#android #kotlin #android-jetpack-compose
Вопрос:
Я хочу реализовать средство выбора изображений с помощью Jetpack compose Я искал решения и нашел несколько учебных пособий, подобных этому https://ngengesenior.medium.com/pick-image-from-gallery-in-jetpack-compose-5fa0d0a8ddaf Я использовал код, который они объяснили, и он работал нормально, но у меня проблема!
Мое приложение включает в себя одно действие «Основная активность», которое запускает рендеринг компонентов композиции, один из моих экранов представляет собой форму с полем для выбора изображения и другими полями, когда я использовал приведенный ниже код, открывается галерея, и я выбираю изображение, и при нажатии кнопки » ОК » оно переходит в основную активность, но вместо этого мне нужно оставаться на том же экране формы, чтобы пользователь мог продолжить заполнение формы, я перечислю код и надеюсь, что кто-нибудь сможет мне помочь с этим
val launcher = rememberLauncherForActivityResult(contract =
ActivityResultContracts.GetContent()) { uri: Uri? ->
imageUri = uri
}
Column() {
Button(onClick = {
launcher.launch("image/*")
}) {
Text(text = "Pick image")
}
Spacer(modifier = Modifier.height(12.dp))
imageUri?.let {
if (Build.VERSION.SDK_INT < 28) {
bitmap.value = MediaStore.Images
.Media.getBitmap(context.contentResolver,it)
} else {
val source = ImageDecoder
.createSource(context.contentResolver,it)
bitmap.value = ImageDecoder.decodeBitmap(source)
}
bitmap.value?.let { btm ->
Image(bitmap = btm.asImageBitmap(),
contentDescription =null,
modifier = Modifier.size(400.dp))
}
}
}
- мое главное преимущество, у меня много вложенных экранов, но, допустим, у меня есть кнопка на главном экране, которая переходит на экран формы, как показано ниже
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Button(
onClick = {
navController.navigate(AppScreens.FormScreen.route)
},
) {
Text(text = "Go to form screen" )
}
}
}
}
- и вот экран моей формы содержит множество полей, таких как поля текста, числа, даты и изображения
@Composable
fun FormScreen() {
var imageUri by remember { mutableStateOf<Uri?>(null) }
val context = LocalContext.current
var bitmap by remember { mutableStateOf<Bitmap?>(null) }
val launcher = rememberLauncherForActivityResult(contract =
ActivityResultContracts.GetContent()) { uri: Uri? ->
imageUri = uri
}
Column {
// some text field in the form
// another number field in the form
// select image filed in the form
CustomInputFieldContainer(
label = "select image"
) {
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
imageUri?.let {
if (Build.VERSION.SDK_INT < 28) {
bitmap = MediaStore.Images
.Media.getBitmap(context.contentResolver,it)
} else {
val source = ImageDecoder
.createSource(context.contentResolver,it)
bitmap = ImageDecoder.decodeBitmap(source)
}
bitmap.let { btm ->
Image(bitmap = btm.asImageBitmap(),
contentDescription =null,
modifier = Modifier.size(400.dp))
}
}
Button(
onClick = { launcher.launch("image/*") },
contentPadding = PaddingValues(),
modifier = Modifier.background(Color.Yellow)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.BottomCenter)
.padding(vertical = 10.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(imageVector = Icons.Filled.AddAPhoto, contentDescription = null)
Spacer(modifier = Modifier.width(8.dp))
Text(text = "Add Photo")
}
}
}
}
}
}
}
теперь я хочу выбрать изображение из галереи при нажатии на кнопку «Выбрать изображение» и вернуться в свою форму, чтобы заполнить остальные поля. Когда я попробовал приведенный выше код, я мог выбрать изображение, но оно переходит к основному действию, из-за которого я потерял данные
есть ли помощь в решении этой проблемы?
Ответ №1:
Когда вы говорите «это сработало нормально», вы имеете в виду, что вы проверили, что «val imageUri» имеет правильный Uri выбранного файла ?
Вы получаете какие-либо ошибки от logcat после выбора изображения из средства выбора изображений ?
Попробуйте с этим и дайте нам знать, если это что-нибудь даст
var imageUri = remember { mutableStateOf<Uri?>(null) } // UPDATE
val context = LocalContext.current
var bitmap by remember { mutableStateOf<Bitmap?>(null) }
val launcher = rememberLauncherForActivityResult(contract =
ActivityResultContracts.GetContent()) { uri: Uri? ->
imageUri.value = uri // UPDATE
}
Комментарии:
1. проблема в том, что после выбора изображения оно переходит к основному действию, а не остается на экране формы! Я только что протестировал его и дал мне правильный Uri, и даже он просто отобразил изображение, но перешел к основному действию, поэтому мне нужно открыть экран формы с самого начала, мне нужно что-то, что удерживает его на том же экране после выбора изображения, вы понимаете мою проблему?
2. Я вас понял, и мы можем посмотреть, как настроен ваш навигационный контроллер ?
3. это просто основное, как описано в документах, и вы можете увидеть некоторый код из основного действия и основной навигации по адресу: gist.github.com/mstva/a391b2e1c57a2e02d8cbeecd3c2d3e9b дайте мне знать, если вы хотите узнать что-то еще
Ответ №2:
Действительно, кажется, что это не имеет отношения к тому, как вы используете лаунчер. Я понимаю, что проблема в том, что экран возвращается к основному действию после того, как вы выберете файл изображения из средства выбора изображений, на этом этапе я бы проверил, как управляется backStackEntry. Попробуйте прокомментировать тело «составить» из MainActivity и понаблюдайте, не приведет ли это к чему-нибудь. —> прокомментируйте этот блок.
{backStackEntry -> backStackEntry.arguments?.getString("form)?.let { _form ->
val form = Gson().fromJson(_form, FormModel::class.java)
FormDetailScreen(
navController = navController,
form = form,
formViewModel = formViewModel
)
}
}
Ответ №3:
Я был в той же ситуации, что и вы, если бы хорошо понимал вашу проблему. Вот как я это сделал. Это немного банально, но это работает.
У меня есть это
//The clickable composable
ProfilePictureView(
avatarUrl, firstname, lastname, isUpdatingProfileImage,
Modifier
.padding(top = 50.dp)
.size(110.dp)
.align(Alignment.CenterHorizontally),
onClick = {
scope.launch {
openImagePicker(permissionManager, LocalContext.current as MainActivity)
}
})
fun openImagePicker(permissionManager: PermissionManager, activity: AppCompatActivity) {
permissionManager.requestStoragePermission {
if (it) {
val intent = Intent(Intent.ACTION_PICK)
intent.type = "image/*"
startActivityForResult(activity, intent, MainActivity.IMAGE_PICK_CODE, null)
}
}
}
Класс для обработки разрешений в приложении
class PermissionManager(private val activity: AppCompatActivity) {
private val errorHandler = CoroutineExceptionHandler { _, throwable ->
Timber.e("$throwable")
}
private val scope = CoroutineScope(Job() Dispatchers.IO errorHandler)
fun requestStoragePermission(onResult: (Boolean) -> Unit) {
requestPermission(
Manifest.permission.READ_EXTERNAL_STORAGE,
Permissions.STORAGE.code,
onResult
)
}
fun requestPermission(permission: String, permissionCode: Int, onResult: (Boolean) -> Unit) {
if (
ContextCompat.checkSelfPermission(activity, permission) == PackageManager.PERMISSION_DENIED
) {
val permissions = arrayOf(permission)
scope.launch {
activity.requestPermissions(permissions, permissionCode)
LiveEventBus.listen<EventPermissionResult>().collect {
onResult.invoke(it.isGranted)
}
}
} else {
onResult.invoke(true)
}
}
enum class Permissions(val code: Int) {
STORAGE(1001)
}
}
Затем в основной активности, когда мы получаем изображение, мы запускаем вызов с помощью шины событий
class MainActivity : AppCompatActivity() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_OK) {
lifecycleScope.launch {
when (requestCode) {
IMAGE_PICK_CODE -> data?.data?.let {
LiveEventBus.send(
EventImageSelected(it)
)
}
}
}
}
}
}
Возможно, есть лучшее решение
Возможно, стоит проверить библиотеку Jetpack Compose для аккомпаниатора, на данный момент она все еще экспериментальная https://github.com/google/accompanist/tree/main/permissions и вот этот док : https://google.github.io/accompanist/permissions/