Android导航架构组件-获取当前可见的片段


103

在尝试导航组件之前,我曾经手动执行片段事务,并使用fragment标签来获取当前片段。

val fragment:MyFragment = supportFragmentManager.findFragmentByTag(tag):MyFragment

现在在我的主要活动布局中,我有类似以下内容:

<fragment
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/nav_host"
        app:navGraph= "@navigation/nav_item"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:defaultNavHost= "true"
        />

如何通过导航组件检索当前显示的片段?在做

supportFragmentManager.findFragmentById(R.id.nav_host)

返回a NavHostFragment,我想检索显示的“ MyFragment”。

谢谢。


4
没有办法检索当前片段stackoverflow.com/a/50689587/1268507 您试图实现什么?也许还有其他解决方案。
Alex

我需要从片段中开始摄影意图,拍照并使用所拍摄的图像。为此,我开始了一个活动,然后等待onActivityResult主活动中的结果。由于我无法获取片段,因此我只是将所有这些都移入了片段本身,并且似乎可以正常工作。
Alin

是否要对该片段对象执行操作?或者只是您想要显示哪个片段?
Ranjan '18

Answers:


106

我设法找到一种方法,如下所示:

NavHostFragment navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host);
navHostFragment.getChildFragmentManager().getFragments().get(0);

当然,如果您知道这是第一个片段。我仍在研究没有这种方法。我同意这不是最好的方法,但是现在应该是这样。


7
看到有效的解决方案真是太好了。我放弃了这一点,开始在活动和片段之间使用共享的viewModel,这意味着我可以在它们之间传递事件,而无需回调或直接访问。
阿林

1
如此错误报告问题issuetracker.google.com/issues/119800853所述,这不是正确的方法。请考虑更改正确答案!
尼古拉斯·彼得里

2
如前所述,这不是解决方案,而是找到合适的解决方案之前的解决方法。
安德烈·图德(Andrei Toader)

1
这行得通,非常感谢。我尝试按照bug跟踪程序的建议尝试getPrimaryNavigationFragment(),但无法完成工作。
杰里·胡

1
基于此解决方案,我创建了一个kotlin函数,该函数可以检查当前片段是否为特定类型(而不是id):navHostFragment!!.childFragmentManager.fragments.any { it.javaClass == fragmentClass && it.isVisible }工作原理非常相似。希望它能帮助任何人!
P Kuijpers

50

我找不到找到当前片段实例的方法。但是,您可以使用以下代码获取最近添加的片段的ID。

navController.currentDestination?.getId()

它在导航文件中返回ID。希望这可以帮助。


谢谢您的回答。我需要检索该片段,以便可以与它进行交互。由于navController无法返回它,因此我结束了使用共享的viewmodel在活动及其片段之间进行对话
Alin,

8
我希望返回的ID与导航xml中的ID匹配,但是不匹配,因此,您不能检查navController.getCurrentDestination()。getId()== R.id.myfrag
Leres Aldtai

4
@LeresAldtai它与导航文件中的片段ID匹配。
slhddn

1
谢谢@slhddn。您的评论对我有所帮助,并能够从片段中检索ID并完成这项工作。在此处添加了我的代码的答案,以防对他人有用。干杯。
Francislainy Campos

36

您应该查看导航片段的childFragmentManager primaryNavigationFragment属性,这是引用当前显示的片段的位置。

val navHost = supportFragmentManager.findFragmentById(R.id.main_nav_fragment)
    navHost?.let { navFragment ->
        navFragment.childFragmentManager.primaryNavigationFragment?.let {fragment->
                //DO YOUR STUFF
        }
    }
}

3
这种方法现在记录在fragmentManager issuetracker.google.com/issues/119800853
Nicolas Pietri

这似乎是一个问题。处理导航我必须设置操作栏才能调用onSupportNaigateUp()
filthy_wizard,

20

这似乎是最干净的解决方案。更新:将扩展功能更改为属性。感谢Igor Wojda

1.创建以下扩展

import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager

val FragmentManager.currentNavigationFragment: Fragment?
    get() = primaryNavigationFragment?.childFragmentManager?.fragments?.first()

2.ActivityNavHostFragment使用这样的:

val currentFragment = supportFragmentManager.currentNavigationFragment

2
尼斯-我将要更改的一个-我将扩展定义为属性FragmentManager.currentNavigationFragment(而不是函数)
Igor Wojda

17

用于addOnDestinationChangedListener具有NavHostFragment

对于Java

 NavController navController= Navigation.findNavController(MainActivity.this,R.id.your_nav_host);

        navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
            @Override
            public void onDestinationChanged(@NonNull NavController controller, @NonNull NavDestination destination, @Nullable Bundle arguments) {
                Log.e(TAG, "onDestinationChanged: "+destination.getLabel());
            }
        });

对于科特林

var navController :NavController=Navigation.findNavController(this, R.id.nav_host_fragment)
    navController.addOnDestinationChangedListener { controller, destination, arguments ->
        Log.e(TAG, "onDestinationChanged: "+destination.label);

    }

9

这可能已经晚了,但对于仍在寻找的人

val currentFragment = NavHostFragment.findNavController(nav_host_fragment).currentDestination?.id

然后你可以做类似的事情

if(currentFragment == R.id.myFragment){
     //do something
}

请注意,这是针对Kotlin的,Java代码应该几乎相同



5
navController().currentDestination?.id

会给你你当前Fragment的身份证

private fun navController() = Navigation.findNavController(this, R.id.navHostFragment)

此ID是您在Navigation Graph XML文件中fragment标记下提供的ID。您也可以currentDestination.label根据需要进行比较。


4

找到了可行的解决方案。

private fun getCurrentFragment(): Fragment? {
    val currentNavHost = supportFragmentManager.findFragmentById(R.id.nav_host_id)
    val currentFragmentClassName = ((this as NavHost).navController.currentDestination as FragmentNavigator.Destination).className
    return currentNavHost?.childFragmentManager?.fragments?.filterNotNull()?.find {
        it.javaClass.name == currentFragmentClassName
    }
}

4

我的要求是从Parent活动中获取当前可见的片段,我的解决方案是

 private LoginFragment getCurrentVisibleFragment() {
        NavHostFragment navHostFragment = (NavHostFragment)getSupportFragmentManager().getPrimaryNavigationFragment();
        FragmentManager fragmentManager = navHostFragment.getChildFragmentManager();
        Fragment loginFragment = fragmentManager.getPrimaryNavigationFragment();
        if(loginFragment instanceof LoginFragment){
            return (LoginFragment)loginFragment;
        }
        return null;
    }

这可能会帮助某些具有相同问题的人。


有Kotlin郎?
IgorGanapolsky '20

1
这对我来说非常理想,不确定其他答案是怎么回事?也许以前没有用过,但是现在就像魅力一样。
Wess

3

在使用的活动中NavHostFragment,您可以使用下面的代码来检索的实例Active Fragment

val fragmentInstance = myNavHostFragment?.childFragmentManager?.primaryNavigationFragment

2

根据@slhddn的回答和评论,这就是我使用它并从nav_graph.xml文件中检索片段ID的方式

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    setSupportActionBar(findViewById(R.id.toolbar))

    val navController = findNavController(this, R.id.nav_host_fragment)

    ivSettingsCog.setOnClickListener {

        if (navController.currentDestination?.id == R.id.updatesFragment) // Id as per set up on nav_graph.xml file
        {
            navController.navigate(R.id.action_updatesFragment_to_settingsFragment)
        }

    }

}

}

1

你能getChildFragmentManager()打电话给 我吗

NavHostFragment fragment = supportFragmentManager.findFragmentById(R.id.nav_host);
MyFragment frag = (MyFragment) fragment.getChildFragmentManager().findFragmentByTag(tag);

1

制作:

•在片段的某些按钮中:

val navController = findNavController(this)
  navController.popBackStack()

•在您的活动中onBackPressed()

override fun onBackPressed() {
        val navController = findNavController(R.id.nav_host_fragment)
        val id = navController.currentDestination?.getId()
        if (id == R.id.detailMovieFragment){
            navController.popBackStack()
            return
        }

        super.onBackPressed()
    } 

1

如果需要片段的实例,可以使用:

(活动)=> supportFragmentManager.fragments.first().childFragmentManager.fragments.first()

丑陋,但您可以从那里检查它是否是您要播放的片段的实例


1

在Androidx上,我尝试了许多方法来从中找出所需的片段NavHostFragment。最后我可以用下面的方法破解它

public Fragment getFunctionalFragment (String tag_name)
{
    NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
    FragmentManager navHostManager = Objects.requireNonNull(navHostFragment).getChildFragmentManager();

    Fragment fragment=null;
    List fragment_list = navHostManager.getFragments();

    for (int i=0; i < fragment_list.size() ; i ++ )
    {
        if ( fragment_list.get(i) instanceof HomeFragment && tag_name.equals("frg_home")) {
            fragment = (HomeFragment) fragment_list.get(i);
            break;
        }
    }

    return fragment;
}

1
欢迎来到SO!请在发布答案之前,先检查一下...代码不清楚。另一个问题:如果您只是带来代码,请解释一下您的解决方案,如果还有其他16个答案,则最多解释一下,因此您应该解释答案的优点。
DavidGarcíaBodego,

1

navhost片段中的第一个片段是当前片段

Fragment navHostFragment = getSupportFragmentManager().getPrimaryNavigationFragment();
if (navHostFragment != null) 
{Fragment fragment = navHostFragment.getChildFragmentManager().getFragments().get(0);}

您能否再详细说明您的答案?也许通过解释您的代码。
20年

不鼓励仅使用代码的答案。请提供一个解释为什么以及您的答案可以解决问题的方式。
伊戈尔·F。

1

首先,通过ID获取当前片段,然后根据该ID找到该片段。

int id = navController.getCurrentDestination().getId();
Fragment fragment = getSupportFragmentManager().findFragmentById(id);

0

我将Andrei Toaders答案Mukul Bhardwajs答案OnBackStackChangedListener

作为属性:

private FooFragment fragment;

在我的 onCreate

NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host);
FragmentManager navHostManager = Objects.requireNonNull(navHostFragment).getChildFragmentManager();
FragmentManager.OnBackStackChangedListener listener = () -> {
        List<Fragment> frags = navHostManager.getFragments();
        if(!frags.isEmpty()) {
            fragment = (FooFragment) frags.get(0);
        }
};
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
        if(destination.getId()==R.id.FooFragment){
            navHostManager.addOnBackStackChangedListener(listener);
        } else {
            navHostManager.removeOnBackStackChangedListener(listener);
           fragment = null;
        }
});

这样我可以得到一个片段,稍后将显示它。一个甚至可以循环播放frags并使用来检查多个片段instanceof


0

我需要执行此操作以设置底部导航视图,并且这样做是这样的:

val navController = Navigation.findNavController(requireActivity(), R.id.nav_tabs_home)
        val bottomNavBar: BottomNavigationView = nav_content_view
        NavigationUI.setupWithNavController(bottomNavBar, navController)

0

实现此目的的最佳方法是注册一个片段生命周期回调

按照原始问题,如果您NavHostFragment被定义为...

<fragment
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/nav_host"
        app:navGraph= "@navigation/nav_item"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:defaultNavHost= "true"
        />

然后在您的主要活动中onCreate(..)...

nav_host.childFragmentManager.registerFragmentLifecycleCallbacks(
    object:FragmentManager.FragmentLifecycleCallbacks() {
        override fun onFragmentViewCreated(
            fm: FragmentManager, f: Fragment, v: View,
            savedInstanceState: Bundle?
        ) {
            // callback method always called whenever a
            // fragment is added, or, a back-press returns
            // to the start-destination
        }
    }
)

其他回调方法可能会被覆盖以满足您的要求。


0

此解决方案将在大多数情况下起作用尝试以下操作:

val mainFragment = fragmentManager?.primaryNavigationFragment?.parentFragment?.parentFragment as MainFragment

或这个:

val mainFragment = fragmentManager?.primaryNavigationFragment?.parentFragment as MainFragment

0

这段代码对我有效

NavDestination current = NavHostFragment.findNavController(getSupportFragmentManager().getPrimaryNavigationFragment().getFragmentManager().getFragments().get(0)).getCurrentDestination();

switch (current.getId()) {
    case R.id.navigation_saved:
        // WRITE CODE
        break;
}

0

这对我有用

(navController.currentDestination as FragmentNavigator.Destination).className 

它将返回canonicalName您的片段


1
这看起来像是反射
IgorGanapolsky '20

0

这可能会晚一些,但稍后可能会帮助某个人。

如果您使用嵌套导航,则可以在Kotlin中获得这样的ID

val nav = Navigation.findNavController(requireActivity(), R.id.fragment_nav_host).currentDestination

这是fragment_nav_host.xml中的片段之一

<fragment
    android:id="@+id/sampleFragment"
    android:name="com.app.sample.SampleFragment"
    android:label="SampleFragment"
    tools:layout="@layout/fragment_sample" />

您可以像这样检查所有您需要的

if (nav.id == R.id.sampleFragment || nav.label == "SampleFragment"){}

0

最后,对于仍在搜索的您来说,可行的解决方案实际上,这非常简单,您可以使用回调来实现。

按着这些次序:

  1. 在要从活动onCreate()获取实例的片段内创建一个侦听器

    public class HomeFragment extends Fragment {
    
    private OnCreateFragment createLIstener;
    
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        createLIstener.onFragmentCreated(this);
        View root = inflater.inflate(R.layout.fragment_home, container, false);
        //findViews(root);
        return root;
    }
    
    @Override
    public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
        super.onCreateOptionsMenu(menu, inflater);
        inflater.inflate(R.menu.menu_main, menu);
    
    }
    
    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        if (item.getItemId()==R.id.action_show_filter){
            //do something
        }
        return super.onOptionsItemSelected(item);
    }
    
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        try {
            createLIstener = (OnCreateFragment) context;
        } catch (ClassCastException e) {
            throw new ClassCastException(context.toString() + " must implement OnArticleSelectedListener");
            }
        }
    
        public interface OnCreateFragment{
            void onFragmentCreated(Fragment f);
        }
    }
    
  2. 在主机Fragment / Activity中实现您的侦听器

    public class MainActivity extends AppCompatActivity implements HomeFragment.OnCreateFragment {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            BottomNavigationView navView = findViewById(R.id.nav_view);
           // Passing each menu ID as a set of Ids because each
           // menu should be considered as top level destinations.
            AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
                    R.id.navigation_home,
                    R. ----)
            .build();
            NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
            NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
            NavigationUI.setupWithNavController(navView, navController);
        }
    
        @Override
        public void onFragmentCreated(Fragment f) {
            if (f!=null){
                H.debug("fragment created listener: "+f.getId());
                f.setHasOptionsMenu(true);
            }else {
                H.debug("fragment created listener: NULL");
            }
        }
    }
    

这就是您完成工作所需的一切。希望这个可以帮助某人


0

我可以用1个片段(Kotlin,导航用法)找出最好的方法:

在您的布局文件上,添加标签:“ android:tag =” main_fragment_tag“”

<fragment
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/nav_host"
        app:navGraph= "@navigation/nav_item"
        android:tag="main_fragment_tag"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:defaultNavHost= "true"
        />

在您的活动中:

val navHostFragment = supportFragmentManager.findFragmentByTag("main_fragment_tag") as NavHostFragment
val mainFragment = navHostFragment.childFragmentManager.fragments[0] as MainFragment
mainFragment.mainFragmentMethod()

0
val fragment: YourFragment? = yourNavHost.findFragment()

注意:findFragment扩展功能位于androidx.fragment.app程序包中,它是通用功能


-2

在您的活动中定义一个片段对象。并且通过传递片段对象从每个片段中调用此方法,例如,因此静态片段对象每次都会被当前显示的片段覆盖。

//method in MainActivity
private static Fragment fragment;

public void setFragment(Fragment fragment){
this.fragment = fragment;
}

//And from your fragment class, you can call
((<yourActivity in which fragment present>)getActivity()).setFragment(<your fragment object>);

//if you want to set it directly;
 ((<yourActivity in which fragment present>)getActivity()).fragment=<your fragment object>;

您还可以设置从片段到活动的回调,以获得更好的方法并传递当前的片段对象。


2
感谢您的时间。我一直在寻找导航组件本身要使用的东西。
阿林
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.