1063 votes

Comment gérer startActivityForResult sur Android ?

Dans mon activité, j'appelle une deuxième activité à partir de l'activité principale par startActivityForResult . Dans ma deuxième activité, il y a quelques méthodes qui terminent cette activité (peut-être sans résultat), cependant, une seule d'entre elles renvoie un résultat.

Par exemple, à partir de l'activité principale, j'en appelle une deuxième. Dans cette activité, je vérifie certaines caractéristiques d'un combiné, par exemple s'il possède une caméra. S'il n'en a pas, je ferme cette activité. De même, pendant la préparation de MediaRecorder o MediaPlayer si un problème survient, je fermerai cette activité.

Si l'appareil est équipé d'une caméra et que l'enregistrement se fait complètement, après l'enregistrement d'une vidéo, si l'utilisateur clique sur le bouton "terminé", je renverrai le résultat (adresse de la vidéo enregistrée) à l'activité principale.

Comment vérifier le résultat de l'activité principale ?

14voto

Tomasz Mularczyk Points 12030

Pour ceux qui ont des problèmes avec requestCode incorrect dans onActivityResult

Si vous appelez startActivityForResult() de votre Fragment le requestCode est modifié par l'activité qui possède le fragment.

Si vous voulez obtenir le code de résultat correct dans votre activité, essayez ceci :

Changez :

startActivityForResult(intent, 1); A :

getActivity().startActivityForResult(intent, 1);

14voto

Darish Points 2956

L'approche recommandée est le ActivityResultRegistry.

ComponentActivity fournit désormais un ActivityResultRegistry qui vous permet de gérer le startActivityForResult() + onActivityResult() ainsi que requestPermissions() + onRequestPermissionsResult() sans remplacer les méthodes dans votre Activity o Fragment apporte une sécurité accrue par le biais de ActivityResultContract et fournit des crochets pour tester ces flux.

Il est fortement recommandé d'utiliser les API de résultat d'activité introduites dans Android 10 Activity 1.2.0-alpha02 et Fragment 1.3.0-alpha02.

Ajoutez ceci à votre build.gradle

def activity_version = "1.2.0-beta01"

// Java language implementation
implementation "androidx.activity:activity:$activity_version"
// Kotlin
implementation "androidx.activity:activity-ktx:$activity_version"

Comment utiliser le contrat préétabli

Cette nouvelle API dispose des fonctionnalités préétablies suivantes

  1. TakeVideo
  2. PickContact
  3. GetContent
  4. GetContents
  5. OpenDocument
  6. OpenDocuments
  7. OpenDocumentTree
  8. CreateDocument
  9. Composez le
  10. Prendre une photo
  11. Demande d'autorisation
  12. Demande d'autorisations

Un exemple qui utilise le contrat takePicture :

private val takePicture = prepareCall(ActivityResultContracts.TakePicture()) { bitmap: Bitmap? ->
    // Do something with the Bitmap, if present
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    button.setOnClickListener { takePicture() }
}

Alors qu'est-ce qui se passe ici ? Décomposons un peu. takePicture est juste une callback qui renvoie un Bitmap nullable - le fait qu'il soit nul ou non dépend du fait que la fonction onActivityResult a réussi. prepareCall puis enregistre cet appel dans une nouvelle fonctionnalité sur ComponentActivity appelé le ActivityResultRegistry - nous y reviendrons plus tard. ActivityResultContracts.TakePicture() est l'une des aides intégrées que Google a créées pour nous, et enfin l'invocation de takePicture déclenche en fait l'intention de la même manière que vous le feriez précédemment avec Activity.startActivityForResult(intent, REQUEST_CODE) .

Comment rédiger un contrat personnalisé

Un contrat simple qui prend un Int comme entrée et renvoie une chaîne de caractères que l'Activité demandée renvoie dans l'Intent du résultat.

class MyContract : ActivityResultContract<Int, String>() {

    companion object {
        const val ACTION = "com.myapp.action.MY_ACTION"
        const val INPUT_INT = "input_int"
        const val OUTPUT_STRING = "output_string"
    }

    override fun createIntent(input: Int): Intent {
        return Intent(ACTION)
            .apply { putExtra(INPUT_INT, input) }
    }

    override fun parseResult(resultCode: Int, intent: Intent?): String? {
        return when (resultCode) {
            Activity.RESULT_OK -> intent?.getStringExtra(OUTPUT_STRING)
            else -> null
        }
    }
}

class MyActivity : AppCompatActivity() {

    private val myActionCall = prepareCall(MyContract()) { result ->
        Log.i("MyActivity", "Obtained result: $result")
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...
        button.setOnClickListener {
            myActionCall(500)
        }
    }
}

Vérifiez cette documentation officielle pour plus d'informations.

10voto

DaviF Points 44

Si vous souhaitez mettre à jour l'interface utilisateur avec le résultat de l'activité, vous ne pouvez pas utiliser la fonction this.runOnUiThread(new Runnable() {} . En faisant cela, l'interface utilisateur ne se rafraîchira pas avec la nouvelle valeur. Au lieu de cela, vous pouvez faire ceci :

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (resultCode == RESULT_CANCELED) {
        return;
    }

    global_lat = data.getDoubleExtra("LATITUDE", 0);
    global_lng = data.getDoubleExtra("LONGITUDE", 0);
    new_latlng = true;
}

@Override
protected void onResume() {
    super.onResume();

    if(new_latlng)
    {
        PhysicalTagProperties.this.setLocation(global_lat, global_lng);
        new_latlng=false;
    }
}

Cela semble idiot, mais cela fonctionne plutôt bien.

3voto

Zhar Points 412

Je vais afficher la nouvelle "façon" avec Android X dans une réponse courte (parce que dans certains cas, vous n'avez pas besoin de registre ou de contrat personnalisé). Si vous voulez plus d'informations, consultez : Obtenir un résultat à partir d'une activité

Important : il y a en fait un bug avec la rétrocompatibilité d'Android X, vous devez donc ajouter fragment_version dans votre fichier Gradle. Sinon, vous obtiendrez une exception "New result API error : Can only use lower 16 bits for requestCode". .

dependencies {

    def activity_version = "1.2.0-beta01"
    // Java language implementation
    implementation "androidx.activity:activity:$activity_version"
    // Kotlin
    implementation "androidx.activity:activity-ktx:$activity_version"

    def fragment_version = "1.3.0-beta02"
    // Java language implementation
    implementation "androidx.fragment:fragment:$fragment_version"
    // Kotlin
    implementation "androidx.fragment:fragment-ktx:$fragment_version"
    // Testing Fragments in Isolation
    debugImplementation "androidx.fragment:fragment-testing:$fragment_version"
}

Maintenant, il vous suffit d'ajouter cette variable membre de votre activité. Ceci utilise un registre prédéfini et un contrat générique.

public class MyActivity extends AppCompatActivity{

   ...

    /**
     * Activity callback API.
     */
    // https://developer.android.com/training/basics/intents/result
    private ActivityResultLauncher<Intent> mStartForResult = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),

            new ActivityResultCallback<ActivityResult>() {

                @Override
                public void onActivityResult(ActivityResult result) {
                    switch (result.getResultCode()) {
                        case Activity.RESULT_OK:
                            Intent intent = result.getData();
                            // Handle the Intent
                            Toast.makeText(MyActivity.this, "Activity returned ok", Toast.LENGTH_SHORT).show();
                            break;
                        case Activity.RESULT_CANCELED:
                            Toast.makeText(MyActivity.this, "Activity canceled", Toast.LENGTH_SHORT).show();
                            break;
                    }
                }
            });

Avant la nouvelle API, vous aviez :

btn.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MyActivity .this, EditActivity.class);
                startActivityForResult(intent, Constants.INTENT_EDIT_REQUEST_CODE);
            }
        });

Vous pouvez remarquer que le code de la requête est maintenant généré (et maintenu) par le framework Google. Votre code devient :

 btn.setOnClickListener(new View.OnClickListener() {

                @Override
                public void onClick(View v) {
                    Intent intent = new Intent(MyActivity .this, EditActivity.class);
                    mStartForResult.launch(intent);
                }
            });

2voto

D'abord, vous utilisez startActivityForResult() avec des paramètres dans le premier Activity et si vous voulez envoyer des données du deuxième Activity au premier Activity puis passer la valeur en utilisant Intent avec le setResult() et récupérer ces données dans la méthode onActivityResult() dans la première Activity .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X