2012-11-27 34 views
73

我一直在使用Android在支持庫中包含的新的nested fragment API。onActivityResult()未在新的嵌套片段中調用API

,我嵌套片段面臨的問題是,如果一個嵌套片段(即,已經被添加到由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> 
+0

一週前發現相同的錯誤。必須在嵌套的Fragment上手動調用onActivityResult()。我個人認爲這是一個錯誤。實際上沒有檢查過它是否可以在框架中實現,但我認爲它可能是。他們只需要檢查頂級片段的所有子片段,並在每個片段上調用onActivityResult()。如果在框架內沒有辦法做到這一點,那麼好的,就是這樣。但如果是這樣,我認真考慮這個錯誤。 –

回答

57

是的,在嵌套片段onActivityResult()將不會被這種方式調用。

onActivityResult的調用序列(Android中支持庫)是

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

在第3步中,在親本ActivityFragmentMananger中找到該片段。因此,在您的示例中,發現容器片段調度爲onActivityResult(),嵌套片段永遠不會收到該事件。

我想你必須在ContainerFragment.onActivityResult()中實現自己的調度,找到嵌套的片段並調用將結果和數據傳遞給它。

+2

好吧,所以問題是......這是一種預期的行爲,還是它的某種錯誤?對我來說,至少不需要直觀地看到嵌套的片段沒有收到活動的結果。 –

+0

好的。你可以把它看成一個bug,因爲android支持庫和版本4.2的本地代碼都不能將結果分派給嵌套的片段。試着自己解決這個問題,不是很難... – faylon

+0

嗯,我想這就是我要做的。我試圖避免這種情況,以便不在主機活動和其片段之間引入更多的耦合。 –

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); 
     } 
    } 
} 
+0

感謝您的訣竅!但是你可以通過編輯你的答案來解釋代碼中的_IHandleActivityResult_接口嗎? +1 –

+1

IHandleActivityResult只是一個空的界面。希望傳遞給它的handleResult的片段可以實現空的接口。這不需要,但是我發現它很有用,所以並不是所有的片段都得到了回調。 – whizzle

-2

我面臨同樣的問題!我解決它通過使用靜態ChildFragmentParentFragment,像這樣:

private static ChildFragment mChildFragment; 

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

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

} 
+1

mChildFragment不必要的是靜態的。 – foo64

+4

可怕的解決方案 – swooby

136

我解決了這個問題用下面的代碼(支持庫被使用):

在容器片段覆蓋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); 
+0

感謝+100000 getParentFragment()!!,但onActivityResult實現給出錯誤! http://paste.ubuntu.com/6565045/,但是實現像Mr.Whizzle(ans中的其中一個)在我的活動中一樣的工作! –

+2

僅供參考我在firstfragment使用pagertabstrip與viewpager和viewpager –

+1

裝載許多孩子片段堆棧跟蹤說NullPoinerException在FirstFragment.onActivityResult()方法的地方發生。我想,你可以在這個方法中放置斷點,並找出哪條線產生這個異常,以及爲什麼會發生這種情況。 – pvshnik

4

此問題已被固定在Android Support Library 23.2起。 Android支持庫23.2+中的Fragment無需額外再做任何操作。如預期的,onActivityResult()現在在嵌套Fragment中調用。

我剛剛使用Android支持庫23.2.1進行了測試,它的工作原理。最後我可以有更乾淨的代碼!

因此,解決方案是開始使用最新的Android支持庫。

+0

可以請你分享任何官方鏈接? –

+1

@RaviRupareliya我已經用鏈接更新了答案。 –

+3

接受的答案應該改爲這個嗎? – kaay

0

爲主要活動你寫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) { 

}}

3

我有搜索FragmentActivity來源。我找到這些事實。

  1. 當片段調用startActivityForResult()時,它實際上會調用其父FragmentActivity的startAcitivityFromFragment()。
  2. 當fragment開始活動結果時,requestCode必須低於65536(16bit)。
  3. 在FragmentActivity的startActivityFromFragment()中,它調用Activity.startActivityForResult(),這個函數也需要一個requestCode,但這個requestCode並不等於原始requestCode。
  4. 實際上FragmentActivity中的requestCode有兩部分。更高的16位值是(片段管理器中的片段索引)+1。低16位等於原始請求代碼。這就是爲什麼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)); 
} 

當結果back.FragmentActivity覆蓋onActivityResult功能,並檢查requestCode的高16位不爲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延伸片段覆蓋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必須低於512(8bit),因爲我們將requestCode溢出了3部分

16bit | 8bit | 8位

高16位的Android已經使用的,中間8位是幫助片段1找到他的fragmentManager的fragment2 array.the最低8位爲原點requestCode。

相關問題