[英]onActivityResult() not called in new nested fragment API
我一直在使用 Android 包含在支持庫中的新嵌套片段API。
我在嵌套片段中面臨的問題是,如果嵌套片段(即通過getChildFragmentManager()
返回的FragmentManager
添加到另一個片段的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:
@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 方法的調用。
此外,正如Eric Brynsvold在類似問題中所指出的,嵌套片段應該使用它的父片段而不是簡單的 startActivityForResult() 調用來啟動活動。 因此,在嵌套片段開始活動中:
getParentFragment().startActivityForResult(intent, requestCode);
是的,嵌套片段中的onActivityResult()
不會通過這種方式調用。
onActivityResult(在Android支持庫中)的調用順序是
Activity.dispatchActivityResult()
。FragmentActivity.onActivityResult()
。Fragment.onActivityResult()
。 第三步,在父Activity
的FragmentMananger
中找到FragmentMananger
。 所以在你的例子中,它是被發現調度onActivityResult()
的容器片段,嵌套片段永遠無法接收事件。
我認為您必須在ContainerFragment.onActivityResult()
實現自己的調度,找到嵌套片段並調用將結果和數據傳遞給它。
這是我解決它的方法。
在活動中:
@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);
}
}
}
2019 年 9 月 2 日更新的答案
這個問題在 Androidx 中又出現了,當您使用單個活動並且在NavHostFragment
嵌套了片段時,不會為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 支持庫 23.2 及更高版本中得到修復。 我們不再需要對 Android 支持庫 23.2+ 中的Fragment
做任何額外的事情。 現在按預期在嵌套Fragment
調用onActivityResult()
。
我剛剛使用 Android 支持庫 23.2.1 對其進行了測試,並且可以正常工作。 終於我可以有更干凈的代碼了!
因此,解決方案是開始使用最新的 Android 支持庫。
我搜索了 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 函數,並檢查 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 擴展 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 擴展 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);
}
}
}
}
就這樣! 用法:
華林!!!!!!!! requestCode 必須低於 512(8 位),因為我們將 requestCode 分為 3 部分
16 位 | 8 位 | 8位
較高的16bit Android已經用過了,中間的8bit是為了幫助fragment1在他的fragmentManager數組中找到fragment2,最低的8bit是源請求碼。
對於 Main Activity,您使用 Super class 編寫 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);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.