В прошлой теме было рассмотрено как вызывать новую 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: