В прошлой теме было рассмотрено как вызывать новую Activity и передавать ей некоторые данные. Но мы можем не только передавать данные запускаемой activity, но и ожидать от нее некоторого результата работы.
К примеру, пусть у нас в проекте будут две activity: MainActivity и SecondActivity. А для каждой activity есть свой файл интерфейса: activity_main.xml и activity_second.xml соответственно.
В прошлой теме мы вызывали новую activity с помощью метода startActivity(). Для получения же результата работы запускаемой activity необходимо
использовать Activity Result API.
Activity Result API предоставляет компоненты для регистрации, запуска и обработки результата другой Activity. Одним из преимуществ применения Activity Result API является то, что он отвязывает результат Activity от самой Activity. Это позволяет получить и обработать результат, даже если Activity, которая возвращает результат, в силу ограничений памяти или в силу других причин завершила свою работу. Вкратце рассмотрим основные моменты применения Activity Result API.
Для регистрации функции, которая будет обрабатывать результат, Activity Result API предоставляет метод registerForActivityResult(). Этот метод в качестве параметров принимает объекты ActivityResultContract и ActivityResultCallback и возвращает объект ActivityResultLauncher, который применяется для запуска другой activity.
ActivityResultLauncher<I> registerForActivityResult (
ActivityResultContract<I, O> contract,
ActivityResultCallback<O> callback)
ActivityResultContract определяет контракт: данные какого типа будут подаваться на вход и какой тип будет представлять результат.
ActivityResultCallback представляет интерфейс с единственным методом onActivityResult(), который определяет обработку полученного результата. Когда вторая activity закончит работу и возвратит результат, то будет как раз вызываться этот метод. Результат передается в метод в качестве параметра. При этом тип параметра должен соответствовать типу результата, определенного в ActivityResultContract. Например:
ActivityResultLauncher<Intent> mStartForResult = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult result) {
// обработка result
}
});
Класс ActivityResultContracts предоставляет ряд встроенных типов контрактов. Например, в листинге кода выше применяется встроенный тип ActivityResultContracts.StartActivityForResult, который в качестве входного объекта устанавливает объект Intent, а в качестве типа результата - тип ActivityResult.
Метод registerForActivityResult() регистрирует функцию-колбек и возвращает объект ActivityResultLauncher.
С помощью этого мы можем запустить activity. Для этого у объекта ActivityResultLauncher вызывается метод launch():
mStartForResult.launch(intent);
В метод lauch() передается объект того типа, который определен объектом ActivityResultContracts в качестве входного.
Итак, определим в файле activity_main.xml следующий интерфейс:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Укажите возраст"
android:textSize="22sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<EditText
android:id="@+id/age"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView"/>
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="Отправить"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/age"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Для ввода данных здесь определен элемент EditText, а для отправки - кнопка.
Определим в классе MainActivity запуск второй activity:
package com.example.viewapp;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
static final String AGE_KEY = "AGE";
static final String ACCESS_MESSAGE="ACCESS_MESSAGE";
ActivityResultLauncher<Intent> mStartForResult = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult result) {
TextView textView = findViewById(R.id.textView);
if(result.getResultCode() == Activity.RESULT_OK){
Intent intent = result.getData();
String accessMessage = intent.getStringExtra(ACCESS_MESSAGE);
textView.setText(accessMessage);
}
else{
textView.setText("Ошибка доступа");
}
}
});
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void onClick(View view) {
// получаем введенный возраст
EditText ageBox = findViewById(R.id.age);
String age = ageBox.getText().toString();
Intent intent = new Intent(this, SecondActivity.class);
intent.putExtra(AGE_KEY, age);
mStartForResult.launch(intent);
}
}
Вкратце рассмотрим главные моменты этого кода. Прежде всего, мы определяем объект ActivityResultLauncher, с помощью которого будем запускать вторую activity и передавать ей данные:
ActivityResultLauncher<Intent> mStartForResult = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult result) {
TextView textView = findViewById(R.id.textView);
if(result.getResultCode() == Activity.RESULT_OK){
Intent intent = result.getData();
String accessMessage = intent.getStringExtra(ACCESS_MESSAGE);
textView.setText(accessMessage);
}
else{
textView.setText("Ошибка доступа");
}
}
});
Объект ActivityResultLauncher типизируется типом Intent, так как объект этого типа будет передаваться в метод launch()
при запуске второй activity.
Тип контракта определяется типом ActivityResultContracts.StartActivityForResult, который и определяет тип Intent в качестве
входного типа и тип ActivityResult в качестве типа результата.
Второй аргумент метода registerForActivityResult() - объект ActivityResultCallback типизируется типом результата - типом
ActivityResult и определяет функцию-колбек onActivityResult(), которая получает результат и обрабатывает его. В данном случае
обработка состоит в том, что мы выводим в текстовое поле ответ от второй activity.
При обработке мы проверяем полученный код результата:
if(result.getResultCode() == Activity.RESULT_OK)
В качестве результата, как правило, применяются встроенные константы Activity.RESULT_OK и Activity.RESULT_CANCELED.
На уровне условностей Activity.RESULT_OK означает, что activity успешно обработала запрос, а Activity.RESULT_CANCELED - что
activity отклонила обработку запроса.
С помощью метода getData() результата получаем переданные из второй activity данные в виде объекта Intent:
Intent intent = result.getData();
Далее извлекаем из Intent строку, которая имеют ключ ACCESS_MESSAGE, и выводим ее в текстовое поле.
Таким образом, мы определили объект ActivityResultLauncher. Далее в обработчике нажатия onClick с помощью этого объекта запускаем вторую activity - SecondActivity:
public void onClick(View view) {
// получаем введенный возраст
EditText ageBox = findViewById(R.id.age);
String age = ageBox.getText().toString();
Intent intent = new Intent(this, SecondActivity.class);
intent.putExtra(AGE_KEY, age);
mStartForResult.launch(intent);
}
В обработчике нажатия кнопки onClick() получаем введенный в текстовое поле возраст, добавляем его в объект Intent с ключем AGE_KEY и запускаем
SecondActivity с помощью метода launch()
Теперь перейдем к SecondActivity и определим в файле activity_second.xml набор кнопок:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/ageView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textSize="26sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<Button
android:id="@+id/button1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Открыть доступ"
android:onClick="onButton1Click"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/ageView"/>
<Button
android:id="@+id/button2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Отклонить доступ"
android:onClick="onButton2Click"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button1"/>
<Button
android:id="@+id/button3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Возраст недействителен"
android:onClick="onButton3Click"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button2" />
<Button
android:id="@+id/cancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Отмена"
android:onClick="onCancelClick"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button3" />
</androidx.constraintlayout.widget.ConstraintLayout>
А в классе SecondActivity определим обработчики для этих кнопок:
package com.example.viewapp;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Bundle extras = getIntent().getExtras();
if (extras != null) {
TextView ageView = findViewById(R.id.ageView);
String age = extras.getString(MainActivity.AGE_KEY);
ageView.setText("Возраст: " + age);
}
}
public void onCancelClick(View v) {
setResult(RESULT_CANCELED);
finish();
}
public void onButton1Click(View v) {
sendMessage("Доступ разрешен");
}
public void onButton2Click(View v) {
sendMessage("Доступ запрещен");
}
public void onButton3Click(View v) {
sendMessage("Недопустимый возраст");
}
private void sendMessage(String message){
Intent data = new Intent();
data.putExtra(MainActivity.ACCESS_MESSAGE, message);
setResult(RESULT_OK, data);
finish();
}
}
Три кнопки вызывают метод sendMessage(), в который передают отправляемый ответ. Это и будет то сообщение, которое получить MainActivity в методе onActivityResult.
Для возврата результата необходимо вызвать метод setResult(), в который передается два параметра:
числовой код результата
отправляемые данные
После вызова метода setResult() нужно вызвать метод finish, который уничтожит текущую activity.
Одна кнопка вызывает обработчик onCancelClick(), в котором передается в setResult только код результата - RESULT_CANCELED.
То есть условно говоря, мы получаем в SecondActivity введенный в MainActivity возраст и с помощью нажатия определенной кнопки возвращаем некоторый результат в виде сообщения.
В зависимости от нажатой кнопки на SecondActivity мы будем получать разные результаты в MainActivity: