FragmentManager findFragmentById返回null

时间:2014-04-29 15:57:28

标签: android android-fragments

activity_fragment.xml的代码:

<?xml version="1.0" encoding="utf-8" ?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragmentContainer"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
/>

CrimeListActivity.java的代码

package com.sinory.criminalintent;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;

public class CrimeListActivity extends SingleFragmentActivity {

    @Override
    protected Fragment createFragment() {
        return new CrimeListFragment();
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Log.d("CrimeListActivity", "create");
    }

}

SingleFragmentActivity.java的代码

package com.sinory.criminalintent;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;

public abstract class SingleFragmentActivity extends FragmentActivity {

    protected abstract Fragment createFragment();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment);

        FragmentManager fm = getSupportFragmentManager();
        Fragment fragment = fm.findFragmentById(R.id.fragmentContainer);

        if (null == fragment) {
            fragment = new CrimeListFragment();
            fm.beginTransaction().add(R.id.fragmentContainer, fragment).commit();
        }
    }
}

刚开始学习Android。为什么Fragment fragment = fm.findFragmentById(R.id.fragmentContainer)为空?请帮帮我。

7 个答案:

答案 0 :(得分:11)

首先,您不需要检查片段是否为空,因为当一个活动销毁所有片段时也会被销毁。因此在活动onCreate中将没有片段,您需要在每次重新创建活动时添加它。
其次不要使用findFragmentById。在xml文件中定义片段时,只能使用此方法。既然您动态添加了片段,则需要指定字符串标记,然后使用该标记来查找片段。

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_fragment);

    FragmentManager fm = getSupportFragmentManager();
    fm.beginTransaction().add(R.id.fragmentContainer, fragment,"TagName").commit();
}

如果您需要找到您的片段,只需使用此代码

Fragment fragment = fm.findFragmentByTag("TagName");


2019年1月9日更新
自2018年起,您只需使用Google导航库在应用中的页面(片段)之间导航即可。它将处理所有事务,并使导航更容易和更清洁。看看这里 https://developer.android.com/topic/libraries/architecture/navigation/

答案 1 :(得分:1)

当没有带有该ID的片段时,它将为null。这是正常的流程。然而,更好的方法是使用findFragmentByTag。由于您在创建片段时将标记设置为任何内容并在管理器上添加/替换它们,因此很容易通过标记找到并跟踪它们。

答案 2 :(得分:0)

没关系,它是空的。它告诉你片段还没有创建。这就是为什么之后你会创建它的几行。

重新启动/重新创建活动时可以调用onCreate方法,在这种情况下,用于存在的片段仍然存在,然后引用将不为空。

答案 3 :(得分:0)

我按照建议这样做了,我仍然得到一个空的返回值。

   FragmentManager fragMan = getSupportFragmentManager();
        FragmentTransaction fragTran = fragMan.beginTransaction();

        ControlsFragment ctrlFrag = new ControlsFragment();

        fragTran.add(R.id.fragment_container, ctrlFrag, "ctrl");
        fragTran.commit(); // commit additions

        FragmentManager fm = this.getSupportFragmentManager();
        Fragment test = fm.findFragmentByTag("ctrl");
        System.out.println(test);
        Spinner spin1 = (Spinner)test.getView().findViewById(R.id.rythmspinner2);
    System.out.println(spin1);
        if (spin1 != null) spin1.setSelection(3);

答案 4 :(得分:0)

我在我调用public class PlayerAdapter extends RecyclerView.Adapter<PlayerAdapter.PlayerHolder>{ private List<Players> playerList; public PlayerAdapter(List<Players> list) { playerList = list; } /* ViewHolder for each item */ public class PlayerHolder extends RecyclerView.ViewHolder { EditText playerName; PlayerHolder(View itemView) { super(itemView); Log.e("Holder","setview"); playerName = itemView.findViewById(R.id.name); MyTextWatcher textWatcher = new MyTextWatcher(playerName); playerName.addTextChangedListener(textWatcher); } } @Override public PlayerHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()) .inflate(R.layout.new_player_item_layout, parent, false); return new PlayerHolder(itemView); } @Override public void onBindViewHolder(PlayerHolder holder, int position) { Players playerItem = playerList.get(position); //Sets Text //holder.playerName.setText(playerItem.getName()); holder.playerName.setTag(R.string.listSize, playerList.size()); holder.playerName.setTag(R.string.position, position); } @Override public int getItemCount() { return playerList.size(); } public void updateList(List<Players> newList){ playerList = newList; notifyDataSetChanged(); } } 的行之前添加了以下代码行并获取空值,以便为我提供存在的片段列表:

findFragmentById

然后,我通过点击左侧的列添加了一个断点。然后,我以调试模式运行应用程序。当我点击该断点并在列表中看到我想要的片段时,我在调试器中查看了List<Fragment> listFrags = getActiveFragments(); 。我找了以下两个值:

  • listFrags(由mTag返回)
  • findFragmentByTag(由mFragmentId返回)

通过检查这种方式,我了解到我需要使用findFragmentById来获取我想要的片段,而不是findFragmentByTag。我希望这个解决方案和/或这些调试步骤对你们中的一个或多个人有所帮助。

答案 5 :(得分:0)

在我的情况下,问题是我在添加片段之后调用了“ findFragmentById()”方法,因此事务未完成。

因此,在调用此方法之前,我立即调用了“ excutePedingTransactions()” 问题就解决了。

答案 6 :(得分:0)

Sinory,这个故事比公认的11个答案深得多!

让我开始说:所有Android开发人员必须深刻理解我们不拥有Android组件。这意味着我们不会创建它们,也不会销毁它们。这些都是由Android系统完成的。我们唯一可以做的就是获取回调。这些是我们可以运行代码的挂钩。您可能会以为我不喜欢这个话题了,但是很快您就会明白为什么我从这个段落开始了。

已接受的答案中的问题:“它将始终为null”否,绝对错误。

现在让我们了解您为何编写此代码以及您理解的错误(不仅是您的代码):
您有回调onCreate(),然后您想到了哦!以前我添加了一个片段。也许是第一次,它将为null,下次添加后,我将在FragmentManager中获得该实例! 您之所以这么想,是因为您错过了了解谁杀死了他们的活动!

您知道吗,如果Android破坏了您的活动,那么在休闲时您会发现您的Fragment!实际上,在尝试重新添加时,您将使代码崩溃。但是请记住,这些都是新实例,它们只是模仿先前的被杀死对象,以使UI上的用户不会感到困惑。这是怎么发生的?读取Activity和片段的onSaveInstanceState和onRestoreInstanceState作为Activity的信息,并读取onViewStateRestored作为片段的方法。

所以我真的回到了被杀害的INSTACE!

让我们看看如何杀死该活动

  1. 按用户:通过按“后退”按钮,活动从任务的后退堆栈中弹出,因此该对象被破坏。现在用户打算销毁,所以故事到此结束。重新启动活动后,一切都将开始全新!没有在先前实例上执行的任何先前动作的痕迹。这有道理吧?因为您想离开屏幕,所以为什么还要期待为您设置相同的场景!说得通?

  2. 通过Android:发生这种情况的原因有很多,请阅读有关它的文档。简而言之,当您单击“主页”按钮并且用户的活动不在表面上,用户无法进行交互时,Android很难找到用户可能正在做的更重要工作的内存。然后它开始杀死背景。并在那里杀死您的活动! ....但是,嘿,现在您又是用户,当您再次返回该被杀死的活动时,这次您希望此活动完全正确!!但是对象被Android杀死了。

Android进行的对象娱乐:所以现在,当Android杀死它时,它负责做一些聪明的事情,并试图在他离开时将其呈现给用户,但是还是要!作为开发人员,Android需要您的帮助,以便在正确的回调钩子等处编写代码以完成任务。所以它的作用是:

  1. 在终止实例之前,它将活动实例的状态保存在捆绑包中,这为开发人员提供了必要时保存其数据的机会。通过onSaveInstanceState回调。
  2. 新活动实例已创建。
  3. 因此,还将创建新的片段管理器。 现在,这两个实例都具有以前的状态,因此它们可以复制相同的内容以完全模仿,就像什么也没有发生一样。

,在这种情况下,新的fragmentManager实例仍可以在其中找到该片段,并且您的片段将不为空!!!欢呼

因此,可以通过代码获取非null片段,但是编写此代码的正确方法是确定谁杀死了实例。一种方法是检查savedInstanceBundle是否为null。如果为null,则表示用户杀死了该实例,或者它是有史以来第一个实例!

if (savedInstanceState != null) {
        // Activity was killed by system.
        // Activity state was saved before killing the previous instance,
        // Doing so it also saved all the fragments in the fragmentManager!.
        // NOTE: It won't save the transaction history! so don't expect that 
        // any fragment that was added, and then later in the flow removed will
        // be saved and later found. ok. keep that in mind very important.


        // So when new Activity is created, new FragmentManager is also created, and it also restores its state with the previous snapshot. i.e bundle. by creating a new instance of fragment and adding to itself.

,因此您找到了片段。但是,嘿,新发现的片段是一个NEW INSTANCE。而不是以前的。记住,每个人都在尝试模仿。

        testFragment = (TestFragment) fm.findFragmentById(R.id.fragment_container);
    } else {
        // User killed, or its the first time ever this activity is created. So           simply create one instance of fragment and add.
        testFragment = new TestFragment();
        fm.beginTransaction().add(R.id.fragment_container, testFragment).commitNow();
    }

我确定您是否足够阅读,理解和推理。它将帮助您建立对Android的理解,并从长期中受益。