繁体   English   中英

如何在 Android 上管理 startActivityForResult

[英]How to manage startActivityForResult on Android

在我的活动中,我通过startActivityForResult从主活动中调用第二个活动。 在我的第二个活动中,有一些方法可以完成此活动(可能没有结果),但是,只有其中一个返回结果。

例如,从主要活动中,我调用了第二个活动。 在本次活动中,我将检查手机的某些功能,例如它是否有摄像头。 如果没有,那么我将关闭此活动。 此外,在准备MediaRecorderMediaPlayer期间,如果发生问题,我将关闭此活动。

如果它的设备有摄像头并且录制完成,那么在录制视频后,如果用户单击完成按钮,我会将结果(录制视频的地址)发送回主活动。

如何检查主要活动的结果?

从您的FirstActivity ,使用startActivityForResult()方法调用SecondActivity

例如:

int LAUNCH_SECOND_ACTIVITY = 1
Intent i = new Intent(this, SecondActivity.class);
startActivityForResult(i, LAUNCH_SECOND_ACTIVITY);

在您的SecondActivity ,设置要返回给FirstActivity 如果您不想返回,请不要设置任何内容。

例如:在SecondActivity如果你想发回数据:

Intent returnIntent = new Intent();
returnIntent.putExtra("result",result);
setResult(Activity.RESULT_OK,returnIntent);
finish();

如果您不想返回数据:

Intent returnIntent = new Intent();
setResult(Activity.RESULT_CANCELED, returnIntent);
finish();

现在在您的FirstActivity类中,为onActivityResult()方法编写以下代码。

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

    if (requestCode == LAUNCH_SECOND_ACTIVITY) {
        if(resultCode == Activity.RESULT_OK){
            String result=data.getStringExtra("result");
        }
        if (resultCode == Activity.RESULT_CANCELED) {
            // Write your code if there's no result
        }
    }
} //onActivityResult

要在 Kotlin 中以更好的方式在两个活动之间传递数据,请阅读在活动之间传递数据的更好方法

如何检查主要活动的结果?

您需要覆盖Activity.onActivityResult()然后检查其参数:

  • requestCode标识哪个应用程序返回了这些结果。 这是由您在调用startActivityForResult()时定义的。
  • resultCode通知您此应用程序是成功、失败还是不同的
  • data保存此应用程序返回的任何信息。 这可能是null

例子

要在上下文中查看整个过程,这里是一个补充答案。 有关更多解释,请参阅我更完整的答案

在此处输入图片说明

主活动.java

public class MainActivity extends AppCompatActivity {

    // Add a different request code for every activity you are starting from here
    private static final int SECOND_ACTIVITY_REQUEST_CODE = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    // "Go to Second Activity" button click
    public void onButtonClick(View view) {

        // Start the SecondActivity
        Intent intent = new Intent(this, SecondActivity.class);
        startActivityForResult(intent, SECOND_ACTIVITY_REQUEST_CODE);
    }

    // This method is called when the second activity finishes
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        // check that it is the SecondActivity with an OK result
        if (requestCode == SECOND_ACTIVITY_REQUEST_CODE) {
            if (resultCode == RESULT_OK) { // Activity.RESULT_OK

                // get String data from Intent
                String returnString = data.getStringExtra("keyName");

                // set text view with string
                TextView textView = (TextView) findViewById(R.id.textView);
                textView.setText(returnString);
            }
        }
    }
}

第二个Activity.java

public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
    }

    // "Send text back" button click
    public void onButtonClick(View view) {

        // get the text from the EditText
        EditText editText = (EditText) findViewById(R.id.editText);
        String stringToPassBack = editText.getText().toString();

        // put the String to pass back into an Intent and close this activity
        Intent intent = new Intent();
        intent.putExtra("keyName", stringToPassBack);
        setResult(RESULT_OK, intent);
        finish();
    }
}

补充Nishant 的回答,返回活动结果的最佳方式是:

Intent returnIntent = getIntent();
returnIntent.putExtra("result",result);
setResult(RESULT_OK,returnIntent);
finish();

我遇到了问题

new Intent();

然后我发现正确的方法是使用

getIntent();

获取当前意图。

对于那些在 onActivityResult 中错误 requestCode问题的人

如果您从Fragment调用startActivityForResult() ,则 requestCode 将被拥有 Fragment 的 Activity 更改。

如果你想在你的活动中获得正确的 resultCode 试试这个:

改变:

startActivityForResult(intent, 1); 到:

getActivity().startActivityForResult(intent, 1);

ActivityResultRegistry 是推荐的方法

ComponentActivity现在提供了一个ActivityResultRegistry ,让您可以处理startActivityForResult() + onActivityResult()以及requestPermissions() + onRequestPermissionsResult()流,而无需覆盖ActivityFragment ,通过ActivityResultContract提高类型安全性,并提供用于测试这些的钩子流动。

强烈建议使用 Android 10 Activity 1.2.0-alpha02 和 Fragment 1.3.0-alpha02 中引入的 Activity Result API。

将此添加到您的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"

如何使用预建合约

这个新 API 具有以下预建功能

  1. 拍视频
  2. PickContact
  3. 获取内容
  4. 获取内容
  5. 开放文档
  6. 开放文档
  7. 打开文档树
  8. 创建文档
  9. 拨号
  10. 拍照片
  11. 请求权限
  12. 请求权限

使用 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() }
}

那么这里发生了什么? 让我们稍微分解一下。 takePicture只是一个回调,它返回一个可为 null 的 Bitmap - 它是否为 null 取决于onActivityResult过程是否成功。 然后, prepareCall将此调用注册到ComponentActivity名为ActivityResultRegistry的新功能中 - 我们稍后会回到这一点。 ActivityResultContracts.TakePicture()是 Google 为我们创建的内置助手之一,最终调用takePicture实际上会以与之前使用Activity.startActivityForResult(intent, REQUEST_CODE)相同的方式触发 Intent。

如何编写自定义合同

一个简单的契约,它接受一个 Int 作为输入并返回一个字符串,请求的 Activity 在结果 Intent 中返回该字符串。

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)
        }
    }
}

查看此官方文档以获取更多信息。

startActivityForResult:已在 Android X 中弃用

对于新的方式,我们有registerForActivityResult

在 Java 中:

 // You need to create a launcher variable inside onAttach or onCreate or global, i.e, before the activity is displayed
 ActivityResultLauncher<Intent> launchSomeActivity = registerForActivityResult(
     new ActivityResultContracts.StartActivityForResult(),
     new ActivityResultCallback<ActivityResult>() {
              @Override
              public void onActivityResult(ActivityResult result) {
                   if (result.getResultCode() == Activity.RESULT_OK) {
                         Intent data = result.getData();
                         // your operation....
                    }
               }
      });

      public void openYourActivity() {
            Intent intent = new Intent(this, SomeActivity.class);
            launchSomeActivity.launch(intent);
      }

在科特林:

var resultLauncher = registerForActivityResult(StartActivityForResult()) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        val data: Intent? = result.data
        // your operation...
    }
}

fun openYourActivity() {
    val intent = Intent(this, SomeActivity::class.java)
    resultLauncher.launch(intent)
}

优势:

  1. 新方法是降低我们在从片段或另一个活动中调用活动时所面临的复杂性
  2. 轻松请求任何许可并获得回调

如果你想用活动结果更新用户界面,你不能使用this.runOnUiThread(new Runnable() {} 。这样做,UI 不会用新值刷新。相反,你可以这样做:

@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;
    }
}

这看起来很傻,但效果很好。

我将在简短的回答中发布 Android X的新“方式”(因为在某些情况下您不需要自定义注册表或合同)。 如果您需要更多信息,请参阅: 从活动中获取结果

重要提示:Android X 的向后兼容性实际上存在一个错误,因此您必须在 Gradle 文件中添加fragment_version 否则,您将收到异常“新结果 API 错误:请求代码只能使用低 16 位”

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"
}

现在你只需要添加你的活动的这个成员变量。 这使用预定义的注册表和通用合同。

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;
                    }
                }
            });

在新 API 之前,您拥有:

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);
            }
        });

您可能会注意到请求代码现在由 Google 框架生成(并保留)。 您的代码变为:

 btn.setOnClickListener(new View.OnClickListener() {

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

首先,您在第一个Activity使用带有参数的startActivityForResult() ,如果您想将数据从第二个Activity发送到第一个Activity然后使用IntentsetResult()方法传递值并在onActivityResult()方法中获取该数据第一个Activity

这是Android上非常常见的问题

它可以分解成三块

  1. 开始活动 B(发生在活动 A)
  2. 设置请求的数据(发生在活动 B)
  3. 接收请求的数据(发生在活动 A)
  1. 开始活动 B
Intent i = new Intent(A.this, B.class);
startActivity(i);
  1. 设置请求的数据

在这部分中,您决定在发生特定事件时是否要发回数据。

例如:在活动 B 中有一个 EditText 和两个按钮 b1、b2。 单击按钮 b1 将数据发送回活动 A。单击按钮 b2 不会发送任何数据。

发送数据

b1......clickListener
{
    Intent resultIntent = new Intent();
    resultIntent.putExtra("Your_key", "Your_value");
    setResult(RES_CODE_A, resultIntent);
    finish();
}

不发送数据

b2......clickListener
{
   setResult(RES_CODE_B, new Intent());
   finish();
}

用户点击后退按钮

默认情况下,结果设置为 Activity.RESULT_CANCEL 响应代码

  1. 检索结果

对于覆盖 onActivityResult 方法

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

    if (resultCode == RES_CODE_A) {

       // b1 was clicked
       String x = data.getStringExtra("RES_CODE_A");

    }
    else if(resultCode == RES_CODE_B){

       // b2 was clicked
    }
    else{
       // The back button was clicked
    }
}

您需要覆盖 Activity.onActivityResult():

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

    if (resultCode == RESULT_CODE_ONE) {

       String a = data.getStringExtra("RESULT_CODE_ONE");

    }
    else if(resultCode == RESULT_CODE_TWO){

       // b was clicked
    }
    else{

    }
}

在您的主要活动中

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    findViewById(R.id.takeCam).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent intent=new Intent(getApplicationContext(),TakePhotoActivity.class);
            intent.putExtra("Mode","Take");
            startActivity(intent);
        }
    });
    findViewById(R.id.selectGal).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent intent=new Intent(getApplicationContext(),TakePhotoActivity.class);
            intent.putExtra("Mode","Gallery");
            startActivity(intent);
        }
    });
}

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

}

在要显示的第二个活动中

private static final int CAMERA_REQUEST = 1888;
private ImageView imageView;
private static final int MY_CAMERA_PERMISSION_CODE = 100;
private static final int PICK_PHOTO_FOR_AVATAR = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_take_photo);

    imageView=findViewById(R.id.imageView);

    if(getIntent().getStringExtra("Mode").equals("Gallery"))
    {
        pickImage();
    }
    else {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                requestPermissions(new String[]{Manifest.permission.CAMERA}, MY_CAMERA_PERMISSION_CODE);
            } else {
                Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                startActivityForResult(cameraIntent, CAMERA_REQUEST);
            }
        }
    }
}
public void pickImage() {
    Intent intent = new Intent(Intent.ACTION_PICK);
    intent.setType("image/*");
    startActivityForResult(intent, PICK_PHOTO_FOR_AVATAR);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
{
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == MY_CAMERA_PERMISSION_CODE)
    {
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED)
        {
            Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
            startActivityForResult(cameraIntent, CAMERA_REQUEST);
        }
        else
        {
            Toast.makeText(this, "Camera Permission Denied..", Toast.LENGTH_LONG).show();
        }
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == CAMERA_REQUEST && resultCode == Activity.RESULT_OK) {
        Bitmap photo = (Bitmap) data.getExtras().get("data");
        imageView.setImageBitmap(photo);
    }
        if (requestCode == PICK_PHOTO_FOR_AVATAR && resultCode == Activity.RESULT_OK) {
            if (data == null) {
                Log.d("ABC","No Such Image Selected");
                return;
            }
            try {
                Uri selectedData=data.getData();
                Log.d("ABC","Image Pick-Up");
                imageView.setImageURI(selectedData);
                InputStream inputStream = getApplicationContext().getContentResolver().openInputStream(selectedData);
                Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                Bitmap bmp=MediaStore.Images.Media.getBitmap(getContentResolver(),selectedData);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch(IOException e){

            }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM