新しい ネストされたフラグメント 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>
はい、ネストされたフラグメントのonActivityResult()
はこの方法では呼び出されません。
OnActivityResultの呼び出しシーケンス(in Android support library)は
Activity.dispatchActivityResult()
。FragmentActivity.onActivityResult()
。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メソッドの呼び出しを受け取ります。
また、同様の質問で notedEric 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);
}
}
}
FragmentActivityソースを検索しましたが、これらの事実を見つけました。
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);
}
}
}
}
それで全部です!使用法:
ウォーリング!!!!!!!! requestCodeは3部でrequestCodeを流出させたため、512(8ビット)以下である必要があります
16ビット| 8ビット| 8ビット
上位の16ビットAndroidが既に使用されている場合、中央の8ビットはfragment1がfragmentManager配列でfragment2を見つけるのに役立ちます。最低の8ビットはOrigin requestCodeです。
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 23.2 以降で修正されました。 Fragment
in Android Support Library 23.2+。で追加の操作を行う必要はありません。期待どおり、onActivityResult()
はネストされたFragment
で呼び出されます。 。
Android Support Library 23.2.1を使用してテストしましたが、動作します。最後に、よりクリーンなコードを作成できます!
したがって、解決策は最新のAndroidサポートライブラリを使用することです。
メインアクティビティの場合、次のようなスーパークラスで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) {
}}