onActivityResult()未在新的嵌套片段API中调用

时间:2012-11-27 08:28:26

标签: android android-fragments android-nested-fragment

我一直在使用Android在支持库中包含的新nested fragment API。

我遇到嵌套片段的问题是,如果嵌套片段(即通过FragmentManager返回的getChildFragmentManager()添加到另一个片段的片段)调用{{ 1}},不调用嵌套片段的startActivityForResult()方法。但是,父片段的onActivityResult()和活动的onActivityResult()都会被调用。

我不知道我是否遗漏了有关嵌套片段的内容,但我没想到所描述的行为。下面是重现此问题的代码。如果有人能指出我正确的方向并向我解释我做错了什么,我将非常感激:

onActivityResult()

test_nested_fragment_container.xml是:

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

7 个答案:

答案 0 :(得分:140)

我使用以下代码解决了这个问题(使用了支持库):

在容器片段中以这种方式覆盖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方法的调用。

此外,在类似问题中,noted Eric Brynsvold,嵌套片段应该使用它的父片段而不是简单的startActivityForResult()调用来启动活动。因此,在嵌套片段启动活动中使用:

getParentFragment().startActivityForResult(intent, requestCode);

答案 1 :(得分:57)

是的,嵌套片段中的onActivityResult()不会以这种方式调用。

onActivityResult的调用序列(在Android支持库中)是

  1. Activity.dispatchActivityResult()
  2. FragmentActivity.onActivityResult()
  3. Fragment.onActivityResult()
  4. 在第3步中,片段位于父FragmentMananger的{​​{1}}中。所以在你的例子中,它是发现调度Activity的容器片段,嵌套片段永远不会收到事件。

    我认为你必须在onActivityResult()中实现自己的调度,找到嵌套片段并调用将结果和数据传递给它。

答案 2 :(得分:8)

以下是我如何解决它。

活动:

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

答案 3 :(得分:4)

此问题已在davidfowl开始修复。 我们不需要在Android支持库23.2+中使用Fragment执行任何额外操作。现在按预期在嵌套onActivityResult()中调用Fragment

我刚刚使用Android支持库23.2.1对其进行了测试,但它确实有效。最后,我可以拥有更清晰的代码!

因此,解决方案是开始使用最新的Android支持库。

答案 4 :(得分:3)

我搜索了FragmentActivity源。我发现了这些事实。

  1. 当片段调用startActivityForResult()时,它实际上调用了他的父FragmentActivity的startAcitivityFromFragment()。
  2. 当结果的片段开始活动时,requestCode必须低于65536(16bit)。
  3. 在FragmentActivity的startActivityFromFragment()中,它调用Activity.startActivityForResult(),这个函数也需要一个requestCode,但是这个requestCode不等于origin requestCode。
  4. 实际上FragmentActivity中的requestCode有两个部分。较高的16位值是(FragmentManager中的片段索引)+ 1.较低的16位等于原始requestCode。这就是requestCode必须低于65536的原因。
  5. 观看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函数,并检查requestCode的16bit是否高于0,而不是将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 extend Fragment覆盖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 extend Fragment覆盖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. fragment1扩展MyFragment。
    2. fragment2扩展NestedFragment。
    3. 没有更多的不同。只需在fragment2上调用startActivityForResult(),并覆盖onActivityResult()函数。
    4. 韦林!!!!!!!! requestCode必须低于512(8位),因为我们将requestCode分为3部分

      16bit | 8bit | 8位

      已经使用的高16位Android,中间8位是帮助fragment1在他的fragmentManager数组中找到fragment2。最低8位是原始requestCode。

答案 5 :(得分:0)

对于主要活动,您使用Super类编写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) {

}}

答案 6 :(得分:-2)

我遇到了同样的问题!我在ChildFragment中使用静态ParentFragment解决了这个问题,如下所示:

private static ChildFragment mChildFragment;

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

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

}