onActivityResult() не вызывается в новом вложенном фрагменте API


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

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

Я не знаю, если я чего-то не хватает о вложенных фрагментах, но я не ожидал описанного поведения. Ниже приведен код, который воспроизводит эту проблему. Я бы очень признателен, если кто-то может мне точку в правильном направлении и объяснить мне что я делаю не так:

package com.example.nestedfragmentactivityresult;

import android.media.RingtoneManager;
import android.os.Bundle;
import android.content.Intent;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends FragmentActivity
{
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        this.getSupportFragmentManager()
            .beginTransaction()
            .add(android.R.id.content, new ContainerFragment())
            .commit();
    }

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

        // This is called
        Toast.makeText(getApplication(),
            "Consumed by activity",
            Toast.LENGTH_SHORT).show();
    }

    public static class ContainerFragment extends Fragment
    {
        public final View onCreateView(LayoutInflater inflater,
                                       ViewGroup container,
                                       Bundle savedInstanceState)
        {
            View result = inflater.inflate(R.layout.test_nested_fragment_container,
                container,
                false);

            return result;
        }

        public void onActivityCreated(Bundle savedInstanceState)
        {
            super.onActivityCreated(savedInstanceState);
            getChildFragmentManager().beginTransaction()
                .add(R.id.content, new NestedFragment())
                .commit();
        }

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

            // This is called
            Toast.makeText(getActivity(),
                "Consumed by parent fragment",
                Toast.LENGTH_SHORT).show();
        }
    }

    public static class NestedFragment extends Fragment
    {
        public final View onCreateView(LayoutInflater inflater,
                                       ViewGroup container,
                                       Bundle savedInstanceState)
        {
            Button button = new Button(getActivity());
            button.setText("Click me!");
            button.setOnClickListener(new View.OnClickListener()
            {
                public void onClick(View v)
                {
                    Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
                    startActivityForResult(intent, 0);
                }
            });

            return button;
        }

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

            // This is NOT called
            Toast.makeText(getActivity(),
                "Consumed by nested fragment",
                Toast.LENGTH_SHORT).show();
        }
    }
}

test_nested_fragment_container.xml-это:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/content"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

</FrameLayout>
7 74

7 ответов:

да onActivityResult() во вложенном фрагменте не будет вызываться таким образом.

вызывающая последовательность onActivityResult (в библиотеке поддержки Android) является

  1. Activity.dispatchActivityResult().
  2. FragmentActivity.onActivityResult().
  3. Fragment.onActivityResult().

на 3-м шаге фрагмент находится в FragmentMananger родитель Activity. Так что в вашем примере, это фрагмент контейнера, который находится в диспетчерской onActivityResult(), вложенный фрагмент никогда не сможет получить событие.

Я думаю, что вы должны реализовать свою собственную отправку в ContainerFragment.onActivityResult(), найти вложенный фрагмент и вызвать передать результат и данные к нему.

Я решил эту проблему с помощью следующего кода (используется библиотека поддержки):

в контейнере фрагмент переопределить onActivityResult таким образом:

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

        List<Fragment> fragments = getChildFragmentManager().getFragments();
        if (fragments != null) {
            for (Fragment fragment : fragments) {
                fragment.onActivityResult(requestCode, resultCode, data);
            }
        }
    }

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

также, как отметитьЭрик Brynsvold в аналогичном вопросе вложенный фрагмент должен начать действие, используя его Родительский фрагмент, а не простой вызов startActivityForResult (). Итак, во вложенном фрагменте начните деятельность с:

getParentFragment().startActivityForResult(intent, requestCode);

вот как я это решил.

В Работе:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    List<Fragment> frags = getSupportFragmentManager().getFragments();
    if (frags != null) {
        for (Fragment f : frags) {
            if (f != null)
                handleResult(f, requestCode, resultCode, data);
        }
    }
}

private void handleResult(Fragment frag, int requestCode, int resultCode, Intent data) {
    if (frag instanceof IHandleActivityResult) { // custom interface with no signitures
        frag.onActivityResult(requestCode, resultCode, data);
    }
    List<Fragment> frags = frag.getChildFragmentManager().getFragments();
    if (frags != null) {
        for (Fragment f : frags) {
            if (f != null)
                handleResult(f, requestCode, resultCode, data);
        }
    }
}

эта проблема была исправлена в Библиотека Поддержки Android 23.2 далее. Нам больше не нужно делать ничего лишнего с Fragment в библиотеке поддержки Android 23.2+. onActivityResult() теперь вызывается во вложенном Fragment как и ожидалось.

Я только что протестировал его с помощью библиотеки поддержки Android 23.2.1, и он работает. Наконец-то я могу иметь более чистый код!

Итак, решение состоит в том, чтобы начать использовать последнюю библиотеку поддержки Android.

у меня есть поиск источника FragmentActivity.Я нахожу эти факты.

  1. когда фрагмент вызывает startActivityForResult (), он фактически вызывает его Родительский FragmentActivity startAcitivityFromFragment ().
  2. когда фрагмент запускает активность для результата, код запроса должен быть ниже 65536 (16 бит).
  3. в FragmentActivity startActivityFromFragment () он вызывает Activity.startActivityForResult (), эта функция также нуждается в requestCode,но этот requestCode не равен к исходному коду запроса.
  4. на самом деле requestCode в FragmentActivity имеет две части. более высокое значение 16 бит (индекс фрагмента в FragmentManager) + 1.Нижний 16 бит равен исходному коду запроса. вот почему код запроса должен быть ниже 65536.

смотреть код в FragmentActivity.

public void startActivityFromFragment(Fragment fragment, Intent intent,
        int requestCode) {
    if (requestCode == -1) {
        super.startActivityForResult(intent, -1);
        return;
    }
    if ((requestCode&0xffff0000) != 0) {
        throw new IllegalArgumentException("Can only use lower 16 bits for requestCode");
    }
    super.startActivityForResult(intent, ((fragment.mIndex+1)<<16) + (requestCode&0xffff));
}

когда результат обратно.FragmentActivity переопределяет функцию onActivityResult и проверяет, не является ли более высокий 16-битный код запроса 0, чем pass в метод onActivityResult, чтобы дети его фрагмент.

    @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    mFragments.noteStateNotSaved();
    int index = requestCode>>16;
    if (index != 0) {
        index--;
        final int activeFragmentsCount = mFragments.getActiveFragmentsCount();
        if (activeFragmentsCount == 0 || index < 0 || index >= activeFragmentsCount) {
            Log.w(TAG, "Activity result fragment index out of range: 0x"
                    + Integer.toHexString(requestCode));
            return;
        }
        final List<Fragment> activeFragments =
                mFragments.getActiveFragments(new ArrayList<Fragment>(activeFragmentsCount));
        Fragment frag = activeFragments.get(index);
        if (frag == null) {
            Log.w(TAG, "Activity result no fragment exists for index: 0x"
                    + Integer.toHexString(requestCode));
        } else {
            frag.onActivityResult(requestCode&0xffff, resultCode, data);
        }
        return;
    }

    super.onActivityResult(requestCode, resultCode, data);
}

так вот как FragmentActivity отправить onActivityResult событие.

когда у вас есть fragmentActivity, он содержит fragment1, а fragment1 содержит fragment2. Поэтому onActivityResult может только перейти к fragment1.

и чем я найду способ решить эту проблему. Сначала создайте фрагмент расширения NestedFragment переопределите startActivityForResult функция.

public abstract class NestedFragment extends Fragment {

@Override
public void startActivityForResult(Intent intent, int requestCode) {

    List<Fragment> fragments = getFragmentManager().getFragments();
    int index = fragments.indexOf(this);
    if (index >= 0) {
        if (getParentFragment() != null) {
            requestCode = ((index + 1) << 8) + requestCode;
            getParentFragment().startActivityForResult(intent, requestCode);
        } else {
            super.startActivityForResult(intent, requestCode);
        }

    }
}

}

чем создать MyFragment расширить фрагмент переопределить функцию onActivityResult.

public abstract class MyFragment extends Fragment {

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    int index = requestCode >> 8;
    if (index > 0) {
        //from nested fragment
        index--;

        List<Fragment> fragments = getChildFragmentManager().getFragments();
        if (fragments != null && fragments.size() > index) {
            fragments.get(index).onActivityResult(requestCode & 0x00ff, resultCode, data);
        }
    }
}

}

вот и все! Использование:

  1. фрагмент1 расширить MyFragment.
  2. фрагмент2 расширить NestedFragment.
  3. больше не отличается.Просто вызовите startActivityForResult () на fragment2 и переопределите функцию onActivityResult ().

Варинг!!!!!!!! Код запроса должен ниже 512(8 бит), потому что мы пролили код запроса в 3 части

16 бит | 8 бит | 8 бит

чем выше 16bit Android уже используется, средний 8bit должен помочь fragment1 найти fragment2 в его массиве fragmentManager.самый низкий 8 бит-это исходный код запроса.

для основной деятельности вы пишете OnActivityForResult с суперклассом вроде как

  @Override
public void onActivityResult(int requestCode, int resultCode, Intent result) {
    super.onActivityResult(requestCode, resultCode, result);
    if (requestCode == Crop.REQUEST_PICK && resultCode == RESULT_OK) {
        beginCrop(result.getData());
    } }

для активности напишите намерение без getActivity() вроде как

 Intent pickContactIntent = new Intent(Intent.ACTION_PICK, Uri.parse("content://contacts"));
        pickContactIntent.setType(ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE); // Show user only contacts w/ phone numbers
       startActivityForResult(pickContactIntent, 103);

OnActivityResult для фрагмента

   @Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == 101 && resultCode == getActivity().RESULT_OK && null != data) {

}}

я столкнулся с той же проблемой! Я решил его с помощью статического ChildFragment на ParentFragment, например:

private static ChildFragment mChildFragment;

protected void onActivityResult(int requestCode, int resultCode, Intent data) {

    mChildFragment.onActivityResult(int requestCode, int resultCode, Intent data);

}