使用replace()方法执行FragmentTransaction后,findFragmentByTag()返回null

37
我的安卓应用包含三个片段:A、B和C。它们加载在MainActivity布局中定义的两个容器中。
当应用启动时,左侧容器中加载fragmentA,右侧容器中加载fragmentC。
如果您按下fragmentA中的按钮,则FragmentTransaction通过FragmentB更改FragmentC。
目前一切正常。但是,当我尝试使用findFragmentByTag()获取已加载的fragmentB的引用时出现问题,因为它返回null。我已经在FragmentTransaction中使用了replace方法,并以commit()结束,但无法调用FragmentB方法。我的代码:
MainActivity.java:
 public class MainActivity extends Activity{
 static String fragmentTag = "FRAGMENTB_TAG";

 @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);


    //Adds the left container's fragment
    getFragmentManager().beginTransaction().add(R.id.left_container, new FragmentA()).commit(); //Adds the fragment A to the left container

    //Adds the right container's fragment
    getFragmentManager().beginTransaction().add(R.id.right_container, new FragmentC()).commit(); //Adds the Fragment C to the right container
 }

 /**
 * Called when the button "Activate Fragment B" is pressed
 */
public void buttonListener(View v){

        FragmentTransaction ft = getFragmentManager().beginTransaction();
        ft.replace(R.id.right_container, new FragmentB(),fragmentTag); //Replaces the Fragment C previously in the right_container with a new Fragment B
        ft.commit(); //Finishes the transaction


        //!!HERE THE APP CRASHES (java.lang.NullPointerException = findFragmentByTag returns null
        ((FragmentB) getFragmentManager().findFragmentByTag(fragmentTag)).testView();


    }
}

FragmentB.java:

public class FragmentB extends Fragment {

public View onCreateView(LayoutInflater inflater,
        ViewGroup container,
        Bundle savedInstanceState) {
    return inflater.inflate(R.layout.fragment_b, container,false);

}


/**
 * Gets a reference to the text_fragment_b TextView and calls its method setText(), changing "It doesn't work" text by "It works!"
 */
public void testView(){
    TextView tv = (TextView)getView().findViewById(R.id.text_fragment_b);
    tv.setText("It works!");
}

}

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >

<FrameLayout android:id="@+id/left_container" android:layout_width="0px" android:layout_weight="50" android:layout_height="match_parent"/>    
<FrameLayout android:id="@+id/right_container" android:layout_width="0px" android:layout_weight="50" android:layout_height="match_parent"/>

</LinearLayout>

fragment_b.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" 
android:layout_margin="5sp">

<TextView 
    android:id="@+id/text_fragment_b"
    android:text="It doesn't works!"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

</LinearLayout>

请帮助我!我是Android开发的初学者!

8个回答

70

我已经解决了!在执行事务后,我调用了 getSupportFragmentManager().executePendingTransactions() ,结果成功了!调用该方法后,我可以使用 findFragmentById()findFragmentByTag() 方法获取片段。


2
我宁愿重写 onAttachFragment 方法,并在片段准备好时执行。 - Guillaume
4
在调用 FragmentTransaction 的 .commit() 方法之后,您需要添加 executePendingTransactions()。这会强制系统执行事务(如果尚未执行)。请注意,不要改变原来的意思。 - Guillermo Barreiro
9
对我没有起作用。 在 commit() 之后添加它,仍然具有 null 值片段。 - Sami Eltamawy
1
对我不起作用。在某些情况下调用executePendingTransactions()时会出现“FragmentManager已经执行事务”的异常,并且一些其他片段会混乱。 - Jonas
4
仍然无法成功。即使在 .commit() 调用之后放置了 getFragmentManager().executePendingTransactions();,仍然得到空值。 - Hephaestus
显示剩余4条评论

3
如果您使用了setRetainInstance(true),那么在Activity的onCreate方法中就无法使用findFragmentByTag()。请在onResume方法中执行此操作。
请参阅文档:setRetainInstance

1
我有同样的问题,意识到解决方法非常简单。在使用标签时,请确保添加


fragmentTransaction.addToBackStack(null); 

在开发者指南中提到,可以使用一种方法,使得您的Fragment在恢复而不是被销毁时被显示。

如果您执行删除Fragment的事务时没有调用addToBackStack(),则该Fragment将在提交事务时被销毁,并且用户无法返回到它。相反,如果您在删除Fragment时调用addToBackStack(),则该Fragment将停止并在用户返回时恢复。

您可以在本节末尾找到此信息。

每次我试图引用我创建的Fragment时,它都已经被销毁了,所以我花了大约30分钟来弄清楚为什么我的Fragment不能通过简单的findFragmentByTag();调用找到。

希望这可以帮助您!


1
我先道歉,因为我自己也很新手...
我认为问题可能出在 fragmentTag 静态字符串的声明上,它没有被类的实例正确地访问,只需将该行更改为:
private final static String FRAGMENT_TAG = "FRAGMENTB_TAG"; // using uppercase since it's a constant

此外,在声明实例时,我会更加明确,例如:
public void buttonListener(View v){

    FragmentTransaction ft = getFragmentManager().beginTransaction();
    ft.replace(R.id.right_container, new FragmentB(), FRAGMENT_TAG);
    ft.commit();

    FragmentB fragB = (FragmentB) getFragmentManager().findFragmentByTag(FRAGMENT_TAG);
    fragB.testView();
}

我希望你能解决这个问题,因为我早先看到这个问题的发布并惊讶于它仍未得到任何回应。
此外,这里有一些关于替换的Android文档链接: Android培训-替换 Android参考-替换

1
它没有起作用。尽管您告诉我进行了更改,但findFragmentByTag()方法仍然返回null。 - Guillermo Barreiro
尝试将FRAGMENT_TAG替换为普通字符串值,例如 ft.replace(R.id.right_container, new FragmentB(), "fragmentB"); - Kenny164
两者都不起作用。我在代码中找不到错误,尝试了很多更改,但仍然在方法findFragmentByTag()中得到了一个空片段。 - Guillermo Barreiro
1
@Kenny164 FRAGMENT_TAG 本来就是一个字符串常量。替换它的意义何在? - IgorGanapolsky

0

请确保以正确的方式添加或替换片段

下一个语句将添加片段,但当使用getFragmentManager().findFragmentByTag(tag)时它将返回null:

transaction.add(R.id.mainContent, fragment);

这样它就能工作了;

transaction.add(R.id.mainContent, fragment, tag);

0
在我的情况下,我使用了代码来替换并添加到BackStack中,但设置了错误的标签:
val fragment = { SomeFragment.newInstance() }
fragmentManager?.replaceAndAddToBackStack(R.id.container, fragment, WrongAnotherFragment.TAG)

当然,supportFragmentManager.findFragmentByTag(SomeFragment.TAG) 没有找到 SomeFragment

0

对我来说,可能是一个新手错误,我在尝试使用findFragmentByTag访问Fragment之后才调用了super.onCreate(savedInstanceState);

我将super.onCreate(savedInstanceState)的位置提前,然后它开始正常工作了。


0
我们也遇到了这个问题,但原因略有不同。https://dev59.com/lWEi5IYBdhLWcg3w7_7P#21170693提出的解决方案对我们来说不起作用。
void updateFragment(Fragment newFragment) {
    FragmentTransaction ft = getFragmentManager().beginTransaction();

    // We have these 2 lines extra
    Fragment current = getChildFragmentManager().findFragmentByTag(fragmentTag);           
    if (current != null) { ft.remove(current); }

    ft.replace(R.id.right_container, newFragment, fragmentTag); //Replaces the Fragment C previously in the right_container with a new Fragment B
    ft.commit(); //Finishes the transaction

    //!!HERE THE APP CRASHES (java.lang.NullPointerException = findFragmentByTag returns null
    ((FragmentB) getFragmentManager().findFragmentByTag(fragmentTag)).testView();
}

阅读有关替换的文档后:

替换已添加到容器中的现有片段。这本质上与为所有当前添加的具有相同containerViewId的片段调用remove(Fragment)然后使用此处给出的相同参数调用add(int,Fragment,String)相同。

我意识到remove调用是不必要的,因为它会被replace自动完成。所以删除ft.remove(current)之后,它可以正常工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接