web-dev-qa-db-ja.com

onActivityResult()は新しいネストされたフラグメントAPIで呼び出されません

新しい ネストされたフラグメント APIを使用しています。これは、Androidサポートライブラリに含まれています。

ネストされたフラグメントが直面している問題は、ネストされたフラグメント(つまり、getChildFragmentManager()によって返されるFragmentManagerを介して別のフラグメントに追加されたフラグメント)が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>
75

はい、ネストされたフラグメントのonActivityResult()はこの方法では呼び出されません。

OnActivityResultの呼び出しシーケンス(in Android support library)は

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

3番目のステップで、フラグメントは親FragmentManangerActivityにあります。したがって、あなたの例では、onActivityResult()をディスパッチすることがわかっているのはコンテナフラグメントであり、ネストされたフラグメントはイベントを受信できません。

ContainerFragment.onActivityResult()に独自のディスパッチを実装し、ネストされたフラグメントを見つけて、結果とデータを渡します。

57
faylon

次のコードでこの問題を解決しました(サポートライブラリを使用):

コンテナフラグメントでは、この方法で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メソッドの呼び出しを受け取ります。

また、同様の質問で notedEric Brynsvold のように、ネストされたフラグメントは、単純なstartActivityForResult()呼び出しではなく、親フラグメントを使用してアクティビティを開始する必要があります。したがって、ネストされたフラグメントでは、次のアクティビティを開始します。

getParentFragment().startActivityForResult(intent, requestCode);
140
pvshnik

解決方法は次のとおりです。

活動中:

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

FragmentActivityソースを検索しましたが、これらの事実を見つけました。

  1. フラグメントがstartActivityForResult()を呼び出すとき、実際には親FragmentActivityのstartAcitivityFromFragment()を呼び出します。
  2. フラグメントが結果のアクティビティを開始するとき、requestCodeは65536(16ビット)未満でなければなりません。
  3. fragmentActivityのstartActivityFromFragment()では、Activity.startActivityForResult()を呼び出します。この関数にはrequestCodeも必要ですが、このrequestCodeはOriginのrequestCodeと等しくありません。
  4. 実際、FragmentActivityのrequestCodeには2つの部分があります。上位の16ビット値は(FragmentManagerのフラグメントのインデックス)+ 1です。下位の16ビットはOrigin requestCodeと同じです。そのため、requestCodeは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関数をオーバーライドし、onActivityResultを子フラグメントに渡すよりも、requestCodeの上位16ビットが0ではないかどうかを確認します。

    @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にのみ渡すことができます。

そして、この問題を解決する方法を見つけるよりも。最初に、startActivityForResult関数をオーバーライドするNestedFragment extend Fragmentを作成します。

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 create Fragment override 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()関数をオーバーライドするだけです。

ウォーリング!!!!!!!! requestCodeは3部でrequestCodeを流出させたため、512(8ビット)以下である必要があります

16ビット| 8ビット| 8ビット

上位の16ビットAndroidが既に使用されている場合、中央の8ビットはfragment1がfragmentManager配列でfragment2を見つけるのに役立ちます。最低の8ビットはOrigin requestCodeです。

4
leikaiyi

NavHostFragmentを使用するナビゲーションコンポーネントを使用するAndroidxの場合

2019年9月2日に回答を更新しました

単一のアクティビティを使用しており、NavHostFragment内にフラグメントをネストしている場合、この問題はAndroidxに戻ります。NavHostFragmentの子フラグメントに対してonActivityResult()は呼び出されません。

これを修正するには、ホストアクティビティのonActivityResult()から子フラグメントのonActivityResult()への呼び出しを手動でルーティングする必要があります。

Kotlinコードを使用してそれを行う方法を次に示します。これは、NavHostFragmentをホストするメインアクティビティのonActivityResult()の中に入ります。

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        val navHostFragment = supportFragmentManager.findFragmentById(R.id.your_nav_Host_fragment)
        val childFragments = navHostFragment?.childFragmentManager?.fragments
        childFragments?.forEach { it.onActivityResult(requestCode, resultCode, data) }
    }

これにより、子フラグメントのonActivityResult()が正常に呼び出されるようになります。

Android Support Library

古い回答

この問題は Android Support Library 23.2 以降で修正されました。 Fragment in Android Support Library 23.2+。で追加の操作を行う必要はありません。期待どおり、onActivityResult()はネストされたFragmentで呼び出されます。 。

Android Support Library 23.2.1を使用してテストしましたが、動作します。最後に、よりクリーンなコードを作成できます!

したがって、解決策は最新のAndroidサポートライブラリを使用することです。

4

メインアクティビティの場合、次のようなスーパークラスで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) {

}}

0
Binesh Kumar