How to manage startActivityForResult on Android?
В своей деятельности я называю второе действие из основного действия startActivityForResult
, В моем втором задании есть несколько методов, которые завершают это задание (возможно, без результата), однако только один из них возвращает результат.
Например, из основного занятия я называю второе. В этом упражнении я проверяю некоторые функции телефона, такие как наличие камеры. Если этого не произойдет, я закрою это занятие. Кроме того, во время подготовки MediaRecorder
или же MediaPlayer
если возникнет проблема, я закрою это действие.
Если на его устройстве есть камера и запись завершена полностью, то после записи видео, если пользователь нажмет кнопку "Готово", я отправлю результат (адрес записанного видео) обратно в основное действие.
Как я могу проверить результат основной деятельности?
15 ответов
От твоего FirstActivity
позвонить SecondActivity
с помощью startActivityForResult()
метод
Например:
Intent i = new Intent(this, SecondActivity.class);
startActivityForResult(i, 1);
В вашем 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) {
if (requestCode == 1) {
if(resultCode == Activity.RESULT_OK){
String result=data.getStringExtra("result");
}
if (resultCode == Activity.RESULT_CANCELED) {
//Write your code if there's no result
}
}
}//onActivityResult
Как проверить результат от основной деятельности?
Вы должны переопределить Activity.onActivityResult()
затем проверьте его параметры:
requestCode
определяет, какое приложение вернуло эти результаты. Это определяется вами, когда вы звонитеstartActivityForResult()
,resultCode
информирует вас о том, успешно ли выполнено это приложение или нетdata
содержит любую информацию, возвращенную этим приложением. Это может бытьnull
,
пример
Чтобы увидеть весь процесс в контексте, вот дополнительный ответ. Смотрите мой более полный ответ для более подробного объяснения.
MainActivity.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);
}
}
}
}
SecondActivity.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();
чтобы получить текущее намерение
startActivityForResult: устарело в androidx
Для нового способа у нас есть `registerForActivityResult`в Java:
// You need to create lanucher 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)
}
Новый способ - уменьшить сложность, с которой мы столкнулись, когда вызываем активность из фрагмента или из другой активности.
ActivityResultRegistry - рекомендуемый подход
ComponentActivity
теперь предоставляет ActivityResultRegistry
что позволяет вам справиться с startActivityForResult()
+onActivityResult()
так же как requestPermissions()
+onRequestPermissionsResult()
потоков без переопределения методов в вашем Activity
или Fragment
, обеспечивает повышенную безопасность типов за счет ActivityResultContract
, и предоставляет хуки для тестирования этих потоков.
Настоятельно рекомендуется использовать API результатов деятельности, представленные в AndroidX Activity 1.2.0-alpha02 и Fragment 1.3.0-alpha02.
Добавьте это в свой build.gradle
def activity_version = "1.2.0-alpha03"
// Java language implementation
implementation "androidx.activity:activity:$activity_version"
// Kotlin
implementation "androidx.activity:activity-ktx:$activity_version"
Как использовать готовый контракт?
Этот новый API имеет следующие предварительно встроенные функции
- TakeVideo
- PickContact
- GetContent
- GetContents
- OpenDocument
- OpenDocuments
- OpenDocumentTree
- CreateDocument
- Набирать номер
- Сделать фотографию
- Просить разрешения
- RequestPermissions
Пример использования контракта 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 - значение NULL зависит от того, является ли onActivityResult
процесс прошел успешно. prepareCall
затем регистрирует этот вызов в новой функции на ComponentActivity
называется ActivityResultRegistry
- мы вернемся к этому позже. ActivityResultContracts.TakePicture()
является одним из встроенных помощников, которые Google создал для нас, и, наконец, вызывает takePicture
фактически запускает намерение так же, как раньше с Activity.startActivityForResult(intent, REQUEST_CODE)
.
Как написать индивидуальный договор?
Простой контракт, который принимает 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)
}
}
}
Проверьте эту официальную документацию для получения дополнительной информации.
Для тех, у кого проблема с неправильным requestCode в onActivityResult
Если вы звоните startActivityForResult()
от твоего Fragment
код запроса изменяется действием, которому принадлежит фрагмент.
Если вы хотите получить правильный код результата в вашей деятельности, попробуйте это:
Изменить:
startActivityForResult(intent, 1);
Для того, чтобы:
getActivity().startActivityForResult(intent, 1);
Если вы хотите обновить пользовательский интерфейс с результатом активности, вы не можете использовать this.runOnUiThread(new Runnable() {}
При этом пользовательский интерфейс не будет обновляться с новым значением. Вместо этого вы можете сделать это:
@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;
}
}
Это кажется глупым, но работает довольно хорошо.
В Котлине
Предположим, что A и B являются действиями, а навигация происходит от A -> B. Нам нужен результат от A <- B.
в А
// calling the Activity B
resultLauncher.launch(Intent(requireContext(), B::class.java))
// we get data in here from B
private var resultLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
when (result.resultCode) {
Activity.RESULT_OK -> {
result.data?.getStringExtra("VALUE")?.let {
// data received here
}
}
Activity.RESULT_CANCELED -> {
// cancel or failure
}
}
}
В Б
// Sending result value back to A
if (success) {
setResult(RESULT_OK, Intent().putExtra("VALUE", value))
} else {
setResult(RESULT_CANCELED)
}
Я опубликую новый "способ" с
androidx
вкратце (потому что в некоторых случаях заказной реестр или договор не требуется). Если вам нужна дополнительная информация, см. Https://developer.android.com/training/basics/intents/result
Важно : на самом деле есть ошибка с обратной совместимостью androidx, поэтому вам нужно добавить
fragment_version
в вашем файле gradle. В противном случае вы получите исключение «Ошибка API нового результата: для requestCode можно использовать только младшие 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);
}
});
Надеюсь, мой ответ поможет некоторым людям!
Очень распространенная проблема в Android
Это может быть разбито на 3 части
1) начать упражнение B (происходит в упражнении A)
2) Установите запрошенные данные (происходит в действии B)
3) Получить запрошенные данные (происходит в действии А)
1) начало деятельности B
Intent i = new Intent(A.this, B.class);
startActivity(i);
2) Установите запрашиваемые данные
В этой части вы решаете, хотите ли вы отправлять данные обратно или нет, когда происходит определенное событие.
Например: в действии 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
3) Получить результат
Для этого переопределить метод 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{
// back button clicked
}
}
Сначала вы используете startActivityForResult()
с параметрами в первую очередь Activity
и если вы хотите отправить данные со второго Activity
к первому Activity
затем передать значение с помощью Intent
с setResult()
метод и получить эти данные внутри onActivityResult()
метод в первую очередь Activity
,
You need to override 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){
}
}
}
Ответ @Nishant правильный. В моем случае onActivityResult никогда не вызывался из-за настроек в AndroidManifest. Убедитесь, что ваше второе действие (или отправитель) не имеет этой строки в манифесте Android.
<activity android:name=".SecondViewActivity"
android:parentActivityName=".FirstActivity"/>
Если это так, пожалуйста, удалите android:parentActivityName=".FirstActivity"