onActivityResult не вызывается во фрагменте

активность хостинга этого фрагмента имеет свой onActivityResult вызывается при возвращении активности камеры.

мой фрагмент запускает действие для результата с намерением, отправленным для камеры, чтобы сделать снимок. Приложение picture загружается нормально, делает снимок и возвращается. The 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);
    }
}

30 ответов


действие хостинга переопределяет 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
}

у меня такая же проблема с 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);
    }
}

Если вы не знаете фрагментов в своей деятельности, просто перечислите их все и отправьте аргументы результата действия:

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

Оригинальный пост.

FragmentActivity заменяет requestCode измененным. После этого, когда onActivityResult() будет вызываться, FragmentActivity анализирует более высокие 16 бит и восстанавливает индекс исходного фрагмента. Посмотрите на эту схему:

Enter image description here

если у вас есть несколько фрагментов на корневом уровне, нет никаких проблем. Но если у вас есть вложенные фрагменты, например Fragment С несколькими вкладками внутри ViewPager, вы гарантированно столкнется с проблемой (или уже столкнулся с этим).

, потому что только один индекс хранится внутри requestCode. Индекс Fragment внутри FragmentManager. Когда мы используем вложенные фрагменты, есть ребенок FragmentManagers, которые имеют свой собственный список фрагментов. Таким образом, необходимо сохранить всю цепочку индексов, начиная с root FragmentManager.

Enter image description here

как решить эту проблему? Есть общее решение решение в этой статье.

GitHub:https://github.com/shamanland/nested-fragment-issue


это один из самых популярных вопрос. Мы можем найти много потоков по этому вопросу. Но ни одна из них мне не нужна.

поэтому я решил эту проблему, используя это решение.

давайте сначала поймем, почему это происходит.

можно назвать startActivityForResult непосредственно из фрагмента, но на самом деле механик позади все обрабатываются деятельностью.

как только вы позвоните 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) {

       }
}

вот и все. С помощью этого решения он может применяться для любого отдельного фрагмента, независимо от того, вложен он или нет. И да, это также охватывает все дело! Кроме того, коды также хороши и чисты.


для многих вложенных фрагментов (например, при использовании 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".


Я могу добавить два совета, если кто-то еще не получится. В Манифесте.xml-файл, убедитесь, что действие хостинга не завершено при обратном вызове, и действие, которое будет запущено, имеет стандартный режим запуска. Подробнее см. ниже:

для действия хостинга установите свойство no history как false, если have

android:noHistory="false"

для запуска активности установите стандартный режим запуска, если есть

android:launchMode="standard"

Я также встретил эту проблему во фрагменте. И я крикнул startActivityForResult на DialogFragment.

но теперь эта проблема решена:
FragmentClassname.this.startActivityForResult.


Я также столкнулся с той же проблемой, как только я переместил этот блок кода за пределы фрагмент до Служебный Класс С parentActivity передается как аргумент,

Intent intent = new Intent(parentActivity, CameraCaptureActivity.class);
parentActivity.startActivityForResult(intent,requestCode);

тогда я не получал никакого значения в onActivityResult метод фрагмент, После этого я изменил аргумент to фрагмент, поэтому пересмотренное определение метода выглядело так:

Intent intent = new Intent(fragment.getContext(), CameraCaptureActivity.class);
fragment.startActivityForResult(intent,requestCode);

после этого, я смог чтобы получить значение onActivityResult на фрагмент


Решение 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.


внутри вашего фрагмента, вызов

this.startActivityForResult(intent, REQUEST_CODE);

здесь this относится к фрагменту. В противном случае сделайте, как сказал @Clevester:

Fragment fragment = this;
....
fragment.startActivityForResult(intent, REQUEST_CODE);

мне тоже пришлось вызвать

super.onActivityResult(requestCode, resultCode, data);

в родительской деятельности onActivityResult чтобы заставить его работать.

(я адаптировал этот ответ из ответа @Clevester.)


в моем случае это была ошибка Android(http://technet.weblineindia.com/mobile/onactivityresult-not-getting-called-in-nested-fragments-android/), Если вы используете 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);
        }
    }
}

короче,

во фрагменте объявить Fragment fragment = this;

после этого использовать fragment.startActivityForResult.

результат вернется в activityResult.


для вложенных фрагментов (например, при использовании 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
}

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

  1. вы можете просто переопределить BaseActivity onActivityResult на фрагмент baseActivity.startActivityForResult .

  2. на 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);
    }
    
  3. на фрагмент реализует 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();
      }
    }
    

этот метод будет делать трюк.


если вышеуказанная проблема стоит на логин 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);

большинство из этих ответов продолжают говорить, что вы должны позвонить super.onActivityResult(...) в узле Activity для Fragment. Но это, похоже, не сработало.

Итак, в вашем хосте Activity вы должны позвонить своему Fragments . Вот пример.

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(...);.


Как упоминал Олли C, существует активная ошибка для библиотеки поддержки, использующей возвращенные значения onActivityResult при использовании вложенных фрагментов. Я просто ударил его :-(.

посмотреть фрагмент.onActivityResult не вызывается при запросе кода != 0.


У меня есть сильное подозрение, что все ответы здесь-не более, чем хаки. Я пробовал их все и многие другие, но без какого-либо надежного вывода, поскольку всегда есть какая-то глупая проблема. Я, например, не могу полагаться на непоследовательные результаты. Если вы посмотрите на официальную документацию Android API для фрагментов, вы увидите, что Google четко заявляет следующее:

вызов startActivityForResult (Intent, int) из фрагмента, содержащего Деятельность.

посмотреть: Android фрагмент API

Итак, казалось бы, самым правильным и надежным подходом было бы на самом деле startActivityForResult() от хостинга активности, а также обрабатывать результирующий onActivityResult () оттуда.


ваш код имеет вложенный фрагмент. Звоню супер.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();
}

моя проблема заключалась в активности Хоста, которую я нашел с набором android:launchMode="standard" Я удалил его временную работу !


один из самых простых способов-запустить действие из вашего фрагмента.

startActivity(ActivityName);

затем добавьте вызов startActivityForResult(intent,"1"); от вашей деятельности и добавить onActivityResult в работе

startActivityForResult(intent,"1");

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.dualPane);
    fragment.onActivityResult(requestCode, resultCode, data);
// perform other activity result like fetching from Uris or handling cursors

finish(); // finish the activity will  get to back to your fragment.
}

Я просто делаю обходной метод:

public static Fragment _tempFragment = null;
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if(_tempFragment != null)
        _tempFragment.onActivityResult(requestCode, resultCode, data);
}

в вашем фрагменте, перед startActivityResult, установите

MainActivity._tempFragment = this;

после onActivityResult

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

     // Do your job
     {

     }
     MainActivity._tempFragment = null;
}

просто используйте приведенный ниже код для фрагмента.

@Override
public void onOtherButtonClick(ActionSheet actionSheet, int index) {

    if (index == 1)
    {
        Intent intent = new Intent(Intent.ACTION_PICK,
                                   android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
        intent.setType("image/*");
        startActivityForResult(Intent.createChooser(intent,
                                                    "Select Picture"), 1);
     }
}

public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == 1) {
        if (requestCode == 1) {
            Uri selectedImageUri = data.getData();
            //selectedImagePath = getPath(selectedImageUri);
        }
    }
}

onActivityResult будет вызывать без вызова своего родителя.


одна точка нет mention убедитесь, что ваша активность Хоста режим запуска не должны singleInstance или singleTask.

onActivityResult не будет работать, если ваш режим запуска установлен в SingleInstance или SingleTask. или вы называете свою деятельность с помощью этих IntentFilters

standard или singleTop режим запуска будет работать нормально.


если есть проблемы с методом onActivityResult это внутри класса fragment, и вы хотите обновить что-то, что также находится внутри класса fragment, используйте:

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

    if(resultCode == Activity.RESULT_OK)
    {
        // If the user had agreed to enabling Bluetooth,
        // populate the ListView with all the paired devices.
        this.arrayDevice = new ArrayAdapter<String>(this.getContext(), R.layout.device_item);
        for(BluetoothDevice bd : this.btService.btAdapater.getBondedDevices())
        {
            this.arrayDevice.add(bd.getAddress());
            this.btDeviceList.setAdapter(this.arrayDevice);
        }
    }
    super.onActivityResult(requestCode, resultCode, data);
}

просто добавить this.variable как показано в коде выше. В противном случае метод будет вызываться в Родительском действии, и переменная не будет обновляться текущего экземпляра.

Я также протестировал его, поместив этот блок кода в MainActivity, вместо this С HomeFragment класс и наличие переменных статических. Я получил результаты, как и ожидал.

Итак, если вы хотите, чтобы класс fragment имел свою собственную реализацию onActivityResult, пример кода выше-это ответ.


Если вы используете вложенные фрагменты, это тоже работает:

getParentFragment().startActivityForResult(intent, RequestCode);

В дополнение к этому, вы должны позвонить super.onActivityResult из родительской активности и заполните onActivityResult метод фрагмента.