onActivityResult не вызывается во фрагменте
Активность хостинга этого фрагмента имеет свою onActivityResult
вызывается, когда активность камеры возвращается.
Мой фрагмент запускает действие для результата с намерением, посланным камерой, чтобы сделать снимок. Приложение с изображениями загружается нормально, делает снимок и возвращается. onActivityResult
Однако никогда не ударил. Я установил точки останова, но ничего не сработало. Может ли фрагмент иметь onActivityResult
? Я бы так подумал, поскольку это предоставленная функция. Почему это не срабатывает?
ImageView myImage = (ImageView)inflatedView.findViewById(R.id.image);
myImage.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, 1888);
}
});
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if( requestCode == 1888 ) {
Bitmap photo = (Bitmap) data.getExtras().get("data");
((ImageView)inflatedView.findViewById(R.id.image)).setImageBitmap(photo);
}
}
41 ответ
Хостинг переопределяет onActivityResult()
, но он не позвонил super.onActivityResult()
для необработанных кодов результатов. Видимо, хотя фрагмент является тем, что делает startActivityForResult()
вызов, деятельность получает первый выстрел в обработке результата. Это имеет смысл, если учесть модульность фрагментов. Однажды я реализовал super.onActivityResult()
для всех необработанных результатов фрагмент получил шанс обработать результат.
А также из @siqing ответа:
Чтобы получить результат в вашем фрагменте, обязательно позвоните startActivityForResult(intent,111);
вместо getActivity().startActivityForResult(intent,111);
внутри вашего фрагмента.
Я думаю ты звонил getActivity().startActivityForResult(intent,111);
, Вам следует позвонить startActivityForResult(intent,111);
,
Опция 1:
Если ты звонишь startActivityForResult()
из фрагмента, то вы должны позвонить startActivityForResult()
не getActivity().startActivityForResult()
, так как это приведет к фрагменту onActivityResult().
Если вы не уверены, куда звоните startActivityForResult()
и как вы будете вызывать методы.
Вариант 2:
Так как активность получает результат onActivityResult()
, вам нужно будет переопределить действия onActivityResult()
и позвонить super.onActivityResult()
распространяться на соответствующий фрагмент для необработанных кодов результатов или для всех.
Если вышеупомянутые два варианта не работают, то обратитесь к варианту 3, поскольку он определенно будет работать.
Вариант 3:
Явный вызов фрагмента функции onActivityResult выглядит следующим образом.
В родительском классе Activity переопределите метод onActivityResult () и даже переопределите его в классе Fragment и вызовите, как показано в следующем коде.
В родительском классе:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.dualPane);
fragment.onActivityResult(requestCode, resultCode, data);
}
В детском классе:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// In fragment class callback
}
Если вы не знаете фрагменты своей деятельности, просто перечислите их все и отправьте аргументы результата деятельности:
// In your activity
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
for (Fragment fragment : getSupportFragmentManager().getFragments()) {
fragment.onActivityResult(requestCode, resultCode, data);
}
}
У меня такая же проблема с ChildFragmentManager
, Менеджер не будет передавать результат во вложенный фрагмент, вы должны сделать это вручную в базовом фрагменте.
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
Fragment fragment = (Fragment) getChildFragmentManager().findFragmentByTag(childTag);
if (fragment != null) {
fragment.onActivityResult(requestCode, resultCode, intent);
}
}
FragmentActivity
заменяет requestCode
измененным. После этого, когда onActivityResult()
будет вызван, FragmentActivity
анализирует старшие 16 бит и восстанавливает индекс исходного фрагмента. Посмотрите на эту схему:
Если у вас есть несколько фрагментов на корневом уровне, проблем нет. Но если у вас есть вложенные фрагменты, например Fragment
с несколькими вкладками внутри ViewPager
, вы гарантированно столкнетесь с проблемой (или уже сталкивались с ней).
Потому что внутри хранится только один индекс requestCode
, Это индекс Fragment
внутри его FragmentManager
, Когда мы используем вложенные фрагменты, есть дочерние FragmentManager
ы, которые имеют свой список фрагментов. Итак, необходимо сохранить всю цепочку индексов, начиная с root FragmentManager
,
Как мы решаем эту проблему? В этом посте есть общее решение проблемы.
Это одна из самых популярных проблем. Мы можем найти много веток по этой проблеме. Но ни один из них не полезен для меня.
Поэтому я решил эту проблему, используя это решение.
Давайте сначала поймем, почему это происходит.
Мы можем позвонить startActivityForResult
непосредственно из фрагмента, но на самом деле механик позади - все обрабатывается Activity.
Как только вы позвоните startActivityForResult
из фрагмента requestCode будет изменен, чтобы прикрепить идентификатор фрагмента к коду. Это позволит Activity быть в состоянии отследить того, кто отправил этот запрос после получения результата.
После того, как Activity будет возвращен обратно, результат будет отправлен в onActivityResult Activity с измененным requestCode, который будет декодирован в исходный идентификатор requestCode + Fragment. После этого Activity отправит результат Activity этому фрагменту через onActivityResult. И все это сделано.
Проблема в:
Activity может отправить результат только фрагменту, который был прикреплен непосредственно к Activity, но не вложенному. Вот почему onActivityResult вложенного фрагмента никогда не будет вызываться, несмотря ни на что.
Решение:
1) Начните Намерение в вашем Фрагменте с помощью кода ниже:
/** Pass your fragment reference **/
frag.startActivityForResult(intent, REQUEST_CODE); // REQUEST_CODE = 12345
2) Теперь в вашей родительской активности переопределить ** onActivityResult()
: **
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
}
Вы должны вызвать это в родительском действии, чтобы заставить это работать.
3) В вашем фрагменте позвоните:
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
}
}
Вот и все. С помощью этого решения его можно применять для любого отдельного фрагмента, независимо от того, является он вложенным или нет. И да, это также охватывает все дела! Кроме того, коды также хороши и чисты.
Для тех, кто использует компонент навигации Android, следует использовать в ActivityonActivityResult(...)
в primaryNavigationFragment
чтобы получить ссылку на фрагмент и вызвать фрагмент fragment.onActivityResult(...)
.
Вот активность onActivityResult(...)
@Override
public void onActivityResult(int requestCode, int resultCode, Intent imageData)
{
super.onActivityResult(requestCode, resultCode, imageData);
for (Fragment fragment : getSupportFragmentManager().getPrimaryNavigationFragment().getChildFragmentManager().getFragments())
{
fragment.onActivityResult(requestCode, resultCode, imageData);
}
}
ДЛЯ МНОГИХ ВСТРОЕННЫХ ФРАГМЕНТОВ (например, при использовании ViewPager во фрагменте)
В вашей основной деятельности:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
}
В вашем фрагменте:
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
for (Fragment fragment : getChildFragmentManager().getFragments()) {
fragment.onActivityResult(requestCode, resultCode, data);
}
}
В вашем вложенном фрагменте
Вызов деятельности
getParentFragment().startActivityForResult(intent, uniqueInstanceInt);
uniqueInstanceInt - замените его на int, которое является уникальным среди вложенных фрагментов, чтобы другой фрагмент не обработал ответ.
Получить ответ
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == uniqueInstanceInt ) {
// TODO your code
}
}
Внимание
Число от 0 до 65536 необходимо использовать в uniqueInstanceInt во избежание ошибок: "Можно использовать только младшие 16 битов для requestCode".
Версия Kotlin для тех, кто использует компонент навигации Android, вдохновленный ответом Мохита Мехты
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
supportFragmentManager.primaryNavigationFragment?.childFragmentManager?.fragments?.forEach { fragment ->
fragment.onActivityResult(requestCode, resultCode, data)
}
}
Я также столкнулся с той же проблемой, когда переместил этот блок кода за пределы фрагмента в класс утилит с parentActivity
передан в качестве аргумента,
Intent intent = new Intent(parentActivity, CameraCaptureActivity.class);
parentActivity.startActivityForResult(intent,requestCode);
Тогда я не получил никакого значения в onActivityResult
Метод этого фрагмента. Впоследствии я изменил аргумент на Fragment, поэтому пересмотренное определение метода выглядело так:
Intent intent = new Intent(fragment.getContext(), CameraCaptureActivity.class);
fragment.startActivityForResult(intent,requestCode);
После этого я смог получить значение в onActivityResult
на фрагменте
Я могу добавить два совета, если кто-то все еще не может сделать это. В файле Manifest.xml убедитесь, что хостинг-операция не завершилась при обратном вызове, и для запускаемой активности стандартным является режим запуска. Подробности см. Ниже:
Для активности хостинга установите для свойства no history значение false, если
android:noHistory="false"
Для запуска действия установите стандартный режим запуска, если есть
android:launchMode="standard"
ДЛЯ ВСТРОЕННЫХ ФРАГМЕНТОВ (например, при использовании ViewPager)
В вашей основной деятельности:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
}
В вашем основном фрагменте верхнего уровня (фрагмент ViewPager):
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
YourFragment frag = (YourFragment) getChildFragmentManager().getFragments().get(viewPager.getCurrentItem());
frag.yourMethod(data); // Method for callback in YourFragment
super.onActivityResult(requestCode, resultCode, data);
}
В YourFragment (вложенный фрагмент):
public void yourMethod(Intent data){
// Do whatever you want with your data
}
Я также встретил эту проблему во фрагменте. И я позвонил startActivityForResult
в DialogFragment
,
Но теперь эта проблема была решена: FragmentClassname.this.startActivityForResult
,
In my case it was an Android bug ( http://technet.weblineindia.com/mobile/onactivityresult-not-getting-called-in-nested-fragments-android/), if you use supported FragmentActivity
ты должен использовать getSupportFragmentManager
вместо getChildFragmentManager
:
List<Fragment> fragments = getSupportFragmentManager().getFragments();
if (fragments != null) {
for (Fragment fragment : fragments) {
if(fragment instanceof UserProfileFragment) {
fragment.onActivityResult(requestCode, resultCode, data);
}
}
}
Внутри вашего фрагмента звоните
this.startActivityForResult(intent, REQUEST_CODE);
где this
имеет в виду фрагмент. В противном случае сделайте, как сказал @Clevester:
Fragment fragment = this;
....
fragment.startActivityForResult(intent, REQUEST_CODE);
Я также должен был позвонить
super.onActivityResult(requestCode, resultCode, data);
в родительской деятельности onActivityResult
чтобы это работало.
(Я адаптировал этот ответ из ответа @ Clevester.)
Короче,
Во фрагменте объявляем Fragment fragment = this
;
после этого использования fragment.startActivityForResult
,
Результат вернется в ActivityResult.
Решение 1:
Вызов startActivityForResult(intent, REQUEST_CODE);
вместо getActivity().startActivityForResult(intent, REQUEST_CODE);
,
Решение 2:
когда startActivityForResult(intent, REQUEST_CODE);
называется деятельность onActivityResult(requestCode,resultcode,intent)
вызывается, а затем вы можете позвонить fragments onActivityResult()
отсюда, проходя requestCode, resultCode and intent
,
С компонентом навигации Android эта проблема, когда у вас есть вложенные фрагменты, может показаться неразрешимой загадкой.
Основываясь на знаниях и вдохновении из следующих ответов в этом посте, мне удалось придумать простое решение, которое работает:
В вашей деятельности onActivityResult()
, вы можете просмотреть список активных фрагментов, который вы получите, используя FragmentManager
с getFragments()
метод.
Обратите внимание, что для этого вам необходимо использоватьgetSupportFragmentManager()
или таргетинг на API 26 и выше.
Идея здесь состоит в том, чтобы просмотреть список, проверяя тип экземпляра каждого фрагмента в списке, используя instanceof
.
Просматривая этот список типов Fragment
идеально, к сожалению, когда вы используете компонент навигации Android, в списке будет только один элемент, т.е. NavHostFragment
.
Что теперь? Нам нужно, чтобы фрагменты были известныNavHostFragment
. NavHostFragment
сам по себе является Фрагментом. Итак, используяgetChildFragmentManager().getFragments()
, мы снова получаем List<Fragment>
фрагментов, известных нашим NavHostFragment
. Мы просматриваем этот список, проверяяinstanceof
каждый фрагмент.
Как только мы находим наш интересующий фрагмент в списке, мы называем его onActivityResult()
, передав ему все параметры, которые Activity onActivityResult()
заявляет.
// Your activity's onActivityResult()
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
List<Fragment> lsActiveFragments = getSupportFragmentManager().getFragments();
for (Fragment fragmentActive : lsActiveFragments) {
if (fragmentActive instanceof NavHostFragment) {
List<Fragment> lsActiveSubFragments = fragmentActive.getChildFragmentManager().getFragments();
for (Fragment fragmentActiveSub : lsActiveSubFragments) {
if (fragmentActiveSub instanceof FragWeAreInterestedIn) {
fragmentActiveSub.onActivityResult(requestCode, resultCode, data);
}
}
}
}
}
Другой вариант использования, еще не описанный в других ответах:
onActivityResult()
объявленный во фрагменте не вызывается при использовании exception.startResolutionForResult()
:
if (exception is ResolvableApiException) {
exception.startResolutionForResult(activity!!, MY_REQUEST_CODE)
}
В этом случае замените exception.startResolutionForResult()
с фрагментом startIntentSenderForResult()
:
if (exception is ResolvableApiException) {
startIntentSenderForResult(exception.resolution.intentSender, MY_REQUEST_CODE, null, 0, 0, 0, null)
}
Большинство из этих ответов постоянно говорят, что вам нужно позвонить super.onActivityResult(...)
в вашем хосте Activity
для тебя Fragment
, Но это, похоже, не работает для меня.
Итак, в вашем хосте Activity
ты должен позвонить Fragments
onActivityResult(...)
вместо. Вот пример.
public class HostActivity extends Activity {
private MyFragment myFragment;
protected void onActivityResult(...) {
super.onActivityResult(...);
this.myFragment.onActivityResult(...);
}
}
В какой-то момент в вашем HostActivity
вам нужно будет назначить this.myFragment
Fragment
ты используешь. Или используйте FragmentManager
чтобы получить Fragment
вместо того, чтобы хранить ссылку на это в вашем HostActivity
, Кроме того, проверьте null
прежде чем пытаться позвонить this.myFragment.onActivityResult(...);
,
Я решил проблему, написав базовый класс, который расширяет Fragment
, И в onactivityresult
активности, которую я идентифицировал текущий фрагмент с использованием fragmenttag
, Затем я вызываю пользовательский метод в классе фрагмента. Это вызовет событие в текущем фрагменте.
public class takeimage extends Fragment {
private Uri mImageCaptureUri;
private static final int PICK_FROM_CAMERA = 1;
private static final int PICK_FROM_FILE = 2;
private String mPath;
private ImageView mImageView;
Bitmap bitmap = null;
View view;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.activity_send_image, container, false);
final String[] items = new String[] { "From Camera", "From SD Card" };
mImageView = (ImageView)view.findViewById(R.id.iv_pic);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(), android.R.layout.select_dialog_item, items);
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("Select Image");
builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
if (item == 0) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File file = new File(Environment.getExternalStorageDirectory(), "tmp_avatar_"
+ String.valueOf(System.currentTimeMillis())
+ ".jpg");
mImageCaptureUri = Uri.fromFile(file);
try {
intent.putExtra(
android.provider.MediaStore.EXTRA_OUTPUT,
mImageCaptureUri);
intent.putExtra("return-data", true);
getActivity().startActivityForResult(intent,
PICK_FROM_CAMERA);
} catch (Exception e) {
e.printStackTrace();
}
dialog.cancel();
} else {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
getActivity().startActivityForResult(
Intent.createChooser(intent,
"Complete action using"), PICK_FROM_FILE);
}
}
});
final AlertDialog dialog = builder.create();
Button show = (Button) view.findViewById(R.id.btn_choose);
show.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// Switch the tab content to display the list view.
dialog.show();
}
});
return view;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK)
return;
if (requestCode == PICK_FROM_FILE) {
mImageCaptureUri = data.getData();
// mPath = getRealPathFromURI(mImageCaptureUri); //from Gallery
if (mPath == null)
mPath = mImageCaptureUri.getPath(); // from File Manager
if (mPath != null)
bitmap = BitmapFactory.decodeFile(mPath);
} else {
mPath = mImageCaptureUri.getPath();
bitmap = BitmapFactory.decodeFile(mPath);
}
mImageView.setImageBitmap(bitmap);
}
public String getRealPathFromURI(Uri contentUri) {
String [] proj = {MediaStore.Images.Media.DATA};
Cursor cursor = managedQuery(contentUri, proj, null, null,null);
if (cursor == null) return null;
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
}
}
Версия Kotlin (в вашей деятельности onActivityResult())
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
//add following lines in your activity
if(supportFragmentManager?.fragments!=null && supportFragmentManager?.fragments!!.size>0)
for (i in 0..supportFragmentManager?.fragments!!.size-1) {
val fragment= supportFragmentManager?.fragments!!.get(i)
fragment.onActivityResult(requestCode, resultCode, data)
}
}
Вы можете просто переопределить BaseActivity
onActivityResult
на фрагментеbaseActivity.startActivityForResult
,На BaseActivity добавьте интерфейс и переопределите onActivityResult.
private OnBaseActivityResult baseActivityResult; public static final int BASE_RESULT_RCODE = 111; public interface OnBaseActivityResult{ void onBaseActivityResult(int requestCode, int resultCode, Intent data); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if(getBaseActivityResult() !=null && requestCode == BASE_RESULT_RCODE){ getBaseActivityResult().onBaseActivityResult(requestCode, resultCode, data); setBaseActivityResult(null); }
На Фрагменте реализует
OnBaseActivityResult
@Override public void onBaseActivityResult(int requestCode, int resultCode, Intent data) { Log.d("RQ","OnBaseActivityResult"); if (data != null) { Log.d("RQ","OnBaseActivityResult + Data"); Bundle arguments = data.getExtras(); } }
Этот обходной путь сделает свое дело.
С помощью @Mohit Mehta, если вы используетеNavigation Component
это может помочь и вам. Это сработало для меня ..
В методеMainActivity
вставьте ниже код:
for (Fragment fragment : navHostFragment.getChildFragmentManager().getFragments()){
if (fragment != null){
Log.d(TAG, "onResult Has Been Sent!");
fragment.onActivityResult(requestCode, resultCode, data);
}
}
И вonActivityResult
методYOUR FRAGMENT
вы можете получить результат. Надеюсь, это поможет вам.
Если вышеупомянутая проблема встречается при входе в Facebook, то вы можете использовать приведенный ниже код в родительской активности вашего фрагмента, например:
Fragment fragment = getFragmentManager().findFragmentById(android.R.id.tabcontent);
fragment.onActivityResult(requestCode, resultCode, data);
Или же:
Fragment fragment = getFragmentManager().findFragmentById("fragment id here");
fragment.onActivityResult(requestCode, resultCode, data);
И добавьте приведенный ниже вызов в свой фрагмент...
callbackManager.onActivityResult(requestCode, resultCode, data);
Прежде всего вам нужно переопределить этот код в Activity;
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
}
а после, в вашем фрагменте,
startActivityForResult(intent,GALLERY_REQUEST_CODE);
и снова, чем в вашем фрагменте,
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// Result code is RESULT_OK only if the user selects an Image
if (resultCode == Activity.RESULT_OK) {
}
}
Если вы используете вложенные фрагменты, это также работает:
getParentFragment().startActivityForResult(intent, RequestCode);
В дополнение к этому, вы должны позвонить super.onActivityResult
от родительской деятельности и заполните onActivityResult
метод фрагмента.
Ваш код имеет вложенный фрагмент. Вызов super.onActivityForResult не работает
Вы не хотите изменять каждое действие, из которого может быть вызван ваш фрагмент, или выполнять обход всех фрагментов в цепочке фрагментов.
Вот одно из многих работающих решений. создайте фрагмент на лету и подключите его непосредственно к операции с помощью менеджера фрагментов поддержки. Затем вызовите startActivityForResult из вновь созданного фрагмента.
private void get_UserEmail() {
if (view == null) {
return;
}
((TextView) view.findViewById(R.id.tvApplicationUserName))
.setText("Searching device for user accounts...");
final FragmentManager fragManager = getActivity().getSupportFragmentManager();
Fragment f = new Fragment() {
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
startActivityForResult(AccountPicker.newChooseAccountIntent(null, null,
new String[]{"com.google"}, false, null, null, null, null), REQUEST_CODE_PICK_ACCOUNT);
}
@Override
public void onActivityResult(int requestCode, int resultCode,
Intent data) {
if (requestCode == REQUEST_CODE_PICK_ACCOUNT) {
String mEmail = "";
if (resultCode == Activity.RESULT_OK) {
if (data.hasExtra(AccountManager.KEY_ACCOUNT_NAME)) {
mEmail = data
.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
}
}
if (mActivity != null) {
GoPreferences.putString(mActivity, SettingApplication.USER_EMAIL, mEmail);
}
doUser();
}
super.onActivityResult(requestCode, resultCode, data);
fragManager.beginTransaction().remove(this).commit();
}
};
FragmentTransaction fragmentTransaction = fragManager
.beginTransaction();
fragmentTransaction.add(f, "xx" + REQUEST_CODE_PICK_ACCOUNT);
fragmentTransaction.commit();
}