En Java 8 se puede escribir así:
ActivityResultLauncher<Intent> startActivityForResult = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), result -> { if (result.getResultCode() == AppCompatActivity.RESULT_OK) { Intent data = result.getData(); // ... } } ); Intent intent = new Intent( ... ); startActivityForResult.launch(intent);Aquí está mi solución:
En nuestro proyecto, tuvimos más de 20 ocurrencias de startActivityForResult (y onActivityResult).
Queríamos cambiar el código lo menos posible (y seguir usando códigos de solicitud), mientras introdujimos una solución elegante para uso futuro.
Dado que muchos de nosotros, los desarrolladores, usamos el concepto BaseActivity , ¿por qué no aprovecharlo?
Aquí está BaseActivity:
abstract class BaseActivity : AppCompatActivity() { private var requestCode: Int = -1 private var resultHandler: ActivityResultLauncher<Intent>? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) registerForActivityResult() } private fun registerForActivityResult() { if (shouldRegisterForActivityResult()) { resultHandler = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> onActivityResult(result.data, requestCode, result.resultCode) this.requestCode = -1 } } } fun startActivityForResult(requestCode: Int, intent: Intent) { this.requestCode = requestCode resultHandler?.launch(intent) } protected open fun onActivityResult(data: Intent?, requestCode: Int, resultCode: Int) { // For sub activities } protected open fun shouldRegisterForActivityResult(): Boolean { // Sub activities that need the onActivityResult "mechanism", should override this and return true return false } }Aquí está la SubActividad:
class SubActivity : BaseActivity() { companion object { private const val SOME_REQUEST_CODE = 300 } private fun testActivityResult() { val intent = Intent(this, OtherActivity::class.java) startActivityForResult(SOME_REQUEST_CODE, intent) } override fun shouldRegisterForActivityResult(): Boolean { return true } override fun onActivityResult(data: Intent?, requestCode: Int, resultCode: Int) { if (requestCode == SOME_REQUEST_CODE) { // Yes! } } }Espero que ayude a alguien
Versión Kotlin de la solución @Muntashir Akon
class BetterActivityResult<Input, Result> private constructor( caller : ActivityResultCaller, contract : ActivityResultContract<Input, Result>, var onActivityResult : ((Result) -> Unit)?, ) { private val launcher : ActivityResultLauncher<Input> = caller.registerForActivityResult(contract) { onActivityResult?.invoke(it) } /** * Launch activity, same as [ActivityResultLauncher.launch] except that it * allows a callback * executed after receiving a result from the target activity. */ /** * Same as [.launch] with last parameter set to `null`. */ @JvmOverloads fun launch( input : Input, onActivityResult : ((Result) -> Unit)? = this.onActivityResult, ) { this.onActivityResult = onActivityResult launcher.launch(input) } companion object { /** * Register activity result using a [ActivityResultContract] and an in-place * activity result callback like * the default approach. You can still customise callback using [.launch]. */ fun <Input, Result> registerForActivityResult( caller : ActivityResultCaller, contract : ActivityResultContract<Input, Result>, onActivityResult : ((Result) -> Unit)?, ) : BetterActivityResult<Input, Result> { return BetterActivityResult(caller, contract, onActivityResult) } /** * Same as [.registerForActivityResult] except * the last argument is set to `null`. */ fun <Input, Result> registerForActivityResult( caller : ActivityResultCaller, contract : ActivityResultContract<Input, Result>, ) : BetterActivityResult<Input, Result> { return registerForActivityResult(caller, contract, null) } /** * Specialised method for launching new activities. */ fun registerActivityForResult( caller : ActivityResultCaller, ) : BetterActivityResult<Intent, ActivityResult> { return registerForActivityResult(caller, StartActivityForResult()) } } }startActivityForResult y onActivityResult están obsoletos en Android 10 API 30 ahora tenemos una nueva forma de obtener el resultado usando registerForActivityResult
resultContract = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> if (result.resultCode == Activity.RESULT_OK) { // There are no request codes val country = result.data?.getParcelableExtra<Country>("Country") showLiveDemoDialogue(country) } }y para iniciar la actividad
val intent = Intent(this, CountriesListActivity::class.java) resultContract.launch(intent)pero debes registrarte antes de llamar a lanzar y lanzar donde quieras. de lo contrario, obtendrá esta excepción
attempting to register while current state is RESUMED. LifecycleOwners must call register before they are STARTED.Parece que onActivityResult está en desuso en la superclase, pero no mencionó el nombre de la superclase y compileSdkVersion aquí en su pregunta.
En Java y Kotlin, cada clase o método podría marcarse como obsoleto simplemente agregando @Deprecated , así que verifique su superclase, puede extender una clase incorrecta.
Cuando una clase está en desuso, todos sus métodos también están en desuso.
Para ver una solución rápida, haga clic en el método obsoleto y presione Ctrl+Q en el estudio de Android para ver la documentación del método, debería haber una solución.
En mi proyecto que usa androidx y API 29 como compileSdkVersion , este método NO está obsoleto en actividades y fragmentos
onActivityResult , startActivityForResult , requestPermissions y onRequestPermissionsResult están obsoletos en androidx.fragment desde 1.3.0-alpha04 , no en android.app.Activity .
En su lugar, puede usar las Activity Result APIs con registerForActivityResult .
A partir de ahora, startActivityForResult() ha quedado obsoleto, así que use un nuevo método en lugar de eso.
Ejemplo de Kotlin
fun openActivityForResult() { startForResult.launch(Intent(this, AnotherActivity::class.java)) } val startForResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult -> if (result.resultCode == Activity.RESULT_OK) { val intent = result.data // Handle the Intent //do stuff here } }En KOTLIN cambié mi código
startActivityForResult(intent, Constants.MY_CODE_REQUEST)y
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) { super.onActivityResult(requestCode, resultCode, data) if (resultCode == Activity.RESULT_OK) { when (requestCode) { Constants.MY_CODE_REQUEST -> { ... }para
registerForActivityResult(StartActivityForResult()) { result -> onActivityResult(Constants.MY_CODE_REQUEST, result) }.launch(intent)y
private fun onActivityResult(requestCode: Int, result: ActivityResult) { if(result.resultCode == Activity.RESULT_OK) { val intent = result.data when (requestCode) { Constants.MY_CODE_REQUEST -> { ...Espero que te funcione. :D
ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() { @Override public void onActivityResult(ActivityResult result) { if (result.getResultCode() == Activity.RESULT_OK) { } } });Puede utilizar funciones de extensión para Koltin. Por ejemplo:
//random utils file fun Fragment.buildGetContentRequest(function: (Uri) -> Unit): ActivityResultLauncher<String> { return this.registerForActivityResult(ActivityResultContracts.GetContent()) { function(it) } } fun Fragment.buildTakePhotoRequest(function: (Boolean) -> Unit): ActivityResultLauncher<Uri> { return this.registerForActivityResult(ActivityResultContracts.TakePicture()) { function(it) } } fun Fragment.buildSelectMultipleContentRequest(function: (MutableList<Uri>?) -> Unit): ActivityResultLauncher<String> { return this.registerForActivityResult(ActivityResultContracts.GetMultipleContents()) { function(it) } }Y luego en tu fragmento algo como esto
//your actual fragment logic class YourFragment : Fragment() { //we can assign our request in init process private val mRequestSelectFiles = buildSelectMultipleContentRequest { onFilesSelected(it) } fun onSelectFiles() { val mime = "*/*" mRequestSelectFiles.launch(mime) } fun onFilesSelected(list: MutableList<Uri>?) { //your logic } }Estoy usando la extensión kotlin para que sea muy simple. Agregue la siguiente función de extensión en su archivo Extenstions.kt :
fun AppCompatActivity.startForResult(intent: Intent, onResult: (resultCode: Int, data: Intent?) -> Unit ) { this.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {result -> onResult(result.resultCode, result.data) }.launch(intent) }Ahora, dentro de cualquier actividad que herede AppCompatActivity, puede usar el siguiente código simple:
val i = Intent(this, TargetActivity::class.java) startForResult(i) { resultCode, data -> //put your code here like: if (resultCode == RESULT_OK) { //your code here... } } }La actualización de la implementación anterior puede causar la siguiente excepción: java.lang.IllegalStateException: LifecycleOwner xxxx está intentando registrarse mientras el estado actual es REANUDAR. Los LifecycleOwners deben llamar a registrarse antes de COMENZAR.
Por lo tanto, se debe llamar a registerForActivityResult con anticipación, por ejemplo, antes de onCreate. Aquí está la solución alternativa.
Agregue la siguiente función de extensión en su archivo Extenstions.kt :
fun AppCompatActivity.registerForResult(onResult: (resultCode: Int, data: Intent?) -> Unit): ActivityResultLauncher<Intent> { return this.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> onResult(result.resultCode, result.data) } }Ahora, dentro de cualquier actividad que herede AppCompatActivity, puede usar el siguiente código simple:
private val myActionResult = registerForResult { resultCode, data -> //put your code here like: if (resultCode == RESULT_OK) { //your code here... } } } val i = Intent(this, TargetActivity::class.java) myActionResult.launch(i)Si implementa su actividad base de esta manera, puede continuar usando startActivityForResult a la antigua. La única limitación es que tendrá que usar setResult(resultado, intención) para establecer el resultado dentro de su actividad. La clave es dejar que el resultado lleve el código de solicitud al consumidor de resultados.
public class MyBaseActivity extends AppCompatActivity { private ActivityResultLauncher<Intent> activityLauncher; protected static String ACTIVITY_REQUEST_CODE = "my.activity.request.code"; protected _originalIntent; public void launchActivityForResult(Intent intent, int requestCode){ intent.putExtra(UGM_ACTIVITY_REQUEST_CODE, requestCode); activityLauncher.launch(intent); } // //In order to be signature compatible for the rest of derived activities, //we will override the deprecated method with our own implementation! // @SuppressWarnings( "deprecation" ) public void startActivityForResult(Intent intent, int requestCode){ launchActivityForResult(intent, requestCode); } @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); _originalIntent = getIntent(); //set the default result setResult(Activity.RESULT_OK, _originalIntent); activityLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() { @Override public void onActivityResult(ActivityResult result) { Intent intent = result.getData(); int requestCode = intent.getIntExtra(ACTIVITY_REQUEST_CODE, -1); MyBaseActivity.this.onActivityResult(requestCode, result.getResultCode(), intent); } }); } }Una forma alternativa de hacer esto es en 3 pasos. (Teniendo en cuenta que tiene un startActivityForResult(0 y onActivityResult())
var resultLauncher:ActivityResultLauncher<Intent> resultLauncher=registerForActivityResult(ActivityResultContracts.StartActivityForResult()){result -> // copy paste the code from the onActivityResult replacing resultcode to result.resultCode if(result.resultcode==Activity.Result_OK){ val data=result.data // this data variable is of type intent and you can use it }else{ //code if you do not get the data } }startActivityForResult() y reemplácela con la línea resultLauncher.launch(intent) registerForActivityResult( ActivityResultContracts.StartActivityForResult() ) { activityResult -> if (activityResult.resultCode == Activity.RESULT_OK) { //... } }¿Solicitando permisos de Actividad?
registerForActivityResult( ActivityResultContracts.RequestMultiplePermissions() ) { //it: Map<String, Boolean> } Use los mismos métodos, pero asegúrese de poner estas implementaciones en initialization, onAttach(), or onCreate()
Referencia: Kotlin - Elegir imagen de la galería
La alternativa más simple que he encontrado hasta ahora
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.id.activity_main) var ivPhoto = findViewById<ImageView>(R.id.ivPhoto) var btnChoosePhoto = findViewById<Button>(R.id.btnChoosePhoto) val getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? -> ivPhoto.setImageURI(uri) // Handle the returned Uri } btnChoose.setOnClickListener { getContent.launch("image/*") } }Hay 4 pasos simples a seguir al reemplazar el método en desuso startActivityForResult(...) .
En lugar del método anulado onActivityResult(..) -
ActivityResultLauncher<Intent> activityResultLaunch = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() { @Override public void onActivityResult(ActivityResult result) { if (result.getResultCode() == 123) { // ToDo : Do your stuff... } else if(result.getResultCode() == 321) { // ToDo : Do your stuff... } } });Para múltiples solicitudes personalizadas, agregue la condición como
if (result.getResultCode() == 123) { .. } else if(result.getResultCode() == 131){ .. } // so on..Importaciones:
import androidx.activity.result.ActivityResult; import androidx.activity.result.ActivityResultCallback; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts;En lugar de startActivityForResult(intent, 123), use
Intent intent = new Intent(this, SampleActivity.class); activityResultLaunch.launch(intent);En la clase SampleActivity.java, al regresar a la actividad de origen, el código seguirá siendo el mismo, como:
Intent intent = new Intent(); setResult(123, intent); finish();¡Feliz codificación! :)
En caso de que esté utilizando la API de consentimiento de SMS, utilice el siguiente código (Kotlin):
resultLauncher.launch( consentIntent ) var resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> if (result.resultCode == Activity.RESULT_OK) { // There are no request codes // val data: Intent? = result.data val message = result.data?.getStringExtra(SmsRetriever.EXTRA_SMS_MESSAGE) getOtpFromMessage(message) } }Así fue como reemplacé varios códigos de solicitud (ponga este código en su actividad):
ActivityResultLauncher<Intent> launchCameraActivity = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() { @Override public void onActivityResult(ActivityResult result) { if (result.getResultCode() == Activity.RESULT_OK) { Intent data = result.getData(); Bitmap photoBitmap; if(data != null && data.getExtras() != null){ photoBitmap = (Bitmap) data.getExtras().get("data"); if (photoBitmap != null) { dataModel.setPhoto(ImageUtil.convert(photoBitmap)); imageTaken.setVisibility(View.VISIBLE); imageTaken.setImageBitmap(photoBitmap); } } } } }); ActivityResultLauncher<Intent> launchCameraAndGalleryActivity = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() { @Override public void onActivityResult(ActivityResult result) { if (result.getResultCode() == Activity.RESULT_OK) { Intent data = result.getData(); Uri imageUri; if (data != null) { imageUri = data.getData(); InputStream imageStream; try { imageStream = getContentResolver().openInputStream(imageUri); Bitmap photoBitmap = BitmapFactory.decodeStream(imageStream); dataModel.setOtherImage(ImageUtil.convert(photoBitmap)); documentImageTaken.setVisibility(View.VISIBLE); documentImageTaken.setImageBitmap(photoBitmap); }catch (FileNotFoundException e) { e.printStackTrace(); } } } } });Lanzo las actividades así:
Intent photoIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); launchCameraAndGalleryActivity.launch(photoIntent ); Intent galleryIntent= new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI); launchCameraActivity.launch(galleryIntent);Además de las respuestas de muntashir akon y abhijeet , puede modificar el nuevo formato para que funcione como el formato anterior pasando valores en la intención, por ejemplo:
// calling class .... val i = Intent(this@GEBShopActivity, BarcodeScannerActivity::class.java) when(loadedFragment){ is ShopHomeFragment -> { i.putExtra("myapp.result.code", CODE_ACTIVITY_SCAN_LIST_MAINT) } is ShopListFragment -> { i.putExtra("myapp.result.code", CODE_ACTIVITY_SCAN_LIST_MAINT) } is ShopItemMaintFragment -> { i.putExtra("myapp.result.code", CODE_ACTIVITY_SCAN_ITEM_MAINT) } is ShopPriceFragment -> { i.putExtra("myapp.result.code", CODE_ACTIVITY_PRICE_CAPTURE) } is ShopCompareFragment -> { i.putExtra("myapp.result.code", CODE_ACTIVITY_PRICE_CAPTURE) } } shopFragmentLauncher.launch(i) .... // called class .... val resultIntent = Intent() val bundle = Bundle() bundle.putStringArrayList("scanned_barcodes", scanned_barcodes) bundle.putInt("scan_count", scan_count) resultIntent.putExtras(bundle) resultIntent.putExtra("myapp.result.code", intent.getIntExtra("myapp.result.code", 0)) setResult(Activity.RESULT_OK, resultIntent) ....Esto le permitirá mantener la clase con el mismo nombre con solo una línea adicional para agregar su código de resultado llamado original. También le permite crear una instancia de lanzador reutilizable.
Después de estar atrapado durante unas horas. Me di cuenta de mi problema. Entonces, cuando estaba tratando de usar la intención, me movía directamente a la siguiente actividad sin usar Google Sign In.
Lo que funcionó para mí:
Dentro de OnCreate configure onClickListener para el botón de inicio de sesión:
btnSignIn.setOnClickListener { signIn() } private fun signIn() { val intent = client.signInIntent mainActivityResultLauncher.launch(intent) } En el código anterior, estaba escribiendo la intención de pasar a la siguiente actividad, pero tuve que escribir client.signInIntent
var mainActivityResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){ result -> if(result.resultCode == Activity.RESULT_OK){ val data = result.data val task = GoogleSignIn.getSignedInAccountFromIntent(data) try { // Google Sign In was successful, authenticate with Firebase val account = task.getResult(ApiException::class.java)!! Log.d(TAG, "firebaseAuthWithGoogle:" + account.id) firebaseAuthWithGoogle(account.idToken!!) } catch (e: ApiException) { // Google Sign In failed, update UI appropriately Log.w(TAG, "Google sign in failed", e) } } }La respuesta dor506 funcionó para mí ya que uso BaseActivity en la mayoría de mis proyectos, por lo que es más fácil para mí cambiar el código en un solo archivo en lugar de todas mis actividades. He escrito la versión Java de este código.
Código de actividad base:
private int requestCode = -1; private ActivityResultLauncher<Intent> resultHandler = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mContext = this; registerForActivityResult(); } private final void registerForActivityResult() { if (shouldRegisterForActivityResult()) { this.resultHandler = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback() { public void onActivityResult(Object var1) { this.onActivityResult((ActivityResult)var1); } public final void onActivityResult(ActivityResult result) { Intrinsics.checkNotNullExpressionValue(result, "result"); AppActivityClass.onActivityResult(result.getData(), AppActivityClass.this.requestCode, result.getResultCode()); AppActivityClass.this.requestCode = -1; } }); } } public final void startActivityForResult(int requestCode, Intent intent) { this.requestCode = requestCode; if (resultHandler != null) { resultHandler.launch(intent); } } protected static void onActivityResult(Intent intent, int requestCode, int resultCode) { } protected Boolean shouldRegisterForActivityResult() { return false; }Ahora en cualquier actividad usa este código así:
@Override protected Boolean shouldRegisterForActivityResult() { return true; // this will override the baseactivity method and we can use onactivityresult } private void someMethod(){ Intent i = new Intent(mContext,SomeOtherClassActivity.class); startActivityForResult(101,i); } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == 101) { if (resultCode == RESULT_OK) { //revert from called class } } }Compartiendo la solución que he encontrado
Primero, registre esta actividad para el resultado usando registerForActivityResult . Esto devolverá un objeto de tipo ActivityResultLauncher<Intent!> Así,
private val getResult = registerForActivityResult( ActivityResultContracts.StartActivityForResult() ) { if (it.resultCode == Activity.RESULT_OK) { val value = it.data?.getStringExtra("input") } } Ahora, donde quiera que queramos iniciar la actividad para obtener resultados, podemos usar getResult.launch(intent)
Descubrí cómo hacerlo correctamente desde un Fragmento en Kotlin, para capturar una imagen y manejar el mapa de bits devuelto. Es más o menos lo mismo en otros casos también.
Primero, debe registrar el fragmento para escuchar los resultados de la actividad. Esto debe hacerse antes de iniciar el fragmento, lo que significa crear una variable miembro en lugar de iniciar dentro de la función onCreate.
class DummyFragment : Fragment() { //registering fragment for camera listener private val takePhoto = registerForActivityResult( ActivityResultContracts.StartActivityForResult() ) { if (it.resultCode == Activity.RESULT_OK) { val imageBitmap = it.data?.extras?.get("data") as Bitmap // do your thing with the obtained bitmap } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) } }Luego, llama a la intención de la cámara como lo harías normalmente. Y use esta variable creada anteriormente para iniciar la intención.
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) someRandomButton.setOnClickListener { val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE) takePhoto.launch(takePictureIntent) } }