如何在对方法 onDestinationChanged() 的调用中获取选定的 Fragment 实例

Posted

技术标签:

【中文标题】如何在对方法 onDestinationChanged() 的调用中获取选定的 Fragment 实例【英文标题】:How to get the selected Fragment instance inside the call to method onDestinationChanged() 【发布时间】:2021-05-14 16:57:31 【问题描述】:

我使用 android Studio 中的底部导航活动模板创建了一个应用程序。在主要活动的 onCreate() 方法中,我向 NavController 添加了一个 OnDestinationChangedListener。我想在选定的 Fragment 更改后调用它的一些方法。 如何从侦听器中的 onDestinationChanged() 获取选定的片段? onCreate() 代码如下:

@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.id.navigation_dashboard, R.id.navigation_notifications)
                .build();
        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
        NavigationUI.setupWithNavController(navView, navController);

        navController.addOnDestinationChangedListener(
                new NavController.OnDestinationChangedListener() 
                    @Override
                    public void onDestinationChanged(@NonNull NavController controller, @NonNull NavDestination destination, @Nullable Bundle arguments) 
                        switch (destination.getId()) 
                            case R.id.navigation_home: 
                                Log.i(TAG, "navigation_home");
                                // Here is where I want to get the HomeFragment instance.
                                break;
                            
                        
                    
                
        );
    

编辑:请注意我不是自己创建片段,请参阅调用 navController.addOnDestinationChangedListener() 之前的三行。

编辑: 这是 activity_main.xml 中的 XML:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/container"
    android:layout_
    android:layout_
    android:paddingTop="?attr/actionBarSize">

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/nav_view"
        android:layout_
        android:layout_
        android:layout_marginStart="0dp"
        android:layout_marginEnd="0dp"
        android:background="?android:attr/windowBackground"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:menu="@menu/bottom_nav_menu" />

    <fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_
        android:layout_
        app:defaultNavHost="true"
        app:layout_constraintBottom_toTopOf="@id/nav_view"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/mobile_navigation" />

</androidx.constraintlayout.widget.ConstraintLayout>

对于bottom_nav_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/navigation_home"
        android:icon="@drawable/ic_home_black_24dp"
        android:title="@string/title_home" />

    <item
        android:id="@+id/navigation_dashboard"
        android:icon="@drawable/ic_dashboard_black_24dp"
        android:title="@string/title_dashboard" />

    <item
        android:id="@+id/navigation_notifications"
        android:icon="@drawable/ic_notifications_black_24dp"
        android:title="@string/title_notifications" />

</menu>

对于fragment_home.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_
    android:layout_
    tools:context=".ui.home.HomeFragment">

    <TextView
        android:id="@+id/text_home"
        android:layout_
        android:layout_
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:textAlignment="center"
        android:textSize="20sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

fragment_dashboard.xmlfragment_notifications.xmlfragment_home.xml 非常相似。

这是HomeFragment

的代码
public class HomeFragment extends Fragment 
    private final static String TAG = BleService.class.getSimpleName();

    private HomeViewModel homeViewModel;

    public View onCreateView(@NonNull LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) 
        homeViewModel = new ViewModelProvider(this).get(HomeViewModel.class);
        View root = inflater.inflate(R.layout.fragment_home, container, false);

        final TextView textView = root.findViewById(R.id.text_home);
        homeViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() 
            @Override
            public void onChanged(@Nullable String s) 
                textView.setText(s);
            
        );

        return root;
    

对于HomeViewModel

public class HomeViewModel extends ViewModel 

    private MutableLiveData<String> mText;

    public HomeViewModel() 
        mText = new MutableLiveData<>();
        mText.setValue("This is home fragment");
    

    public LiveData<String> getText() 
        return mText;
    

除了我在 MainActivity 中创建一个服务及其使用方面所做的测试之外,项目的其余部分几乎都是我创建项目时由 AndroidStudio 生成的代码.

【问题讨论】:

这能回答你的问题吗? Android Navigation Architecture Component - Get current visible fragment 哈塔,谢谢。在发布我的问题之前,我浏览了该帖子,并尝试了他们在那里提出的建议,但没有成功。就我而言,该片段并未在我的主要活动中的代码中创建。检查调用 navController.addOnDestinationChangedListener() 之前的三行。 【参考方案1】:

实际上你需要像这样在你的 navView 上传递 setNavigationItemSelectedListener navView.setNavigationItemSelectedListener(this);然后将此代码添加到您的 onNavigationItemSelected case R.id.navigation_home: Navigation.findNavController(this,R.id.nav_host_fragment).navigate(R.id.profile); 注意:将 R.id.profile 替换为您的导航图。代码只是您想要实现的示例。 不要忘记在主 Activity 中实现 NavigationView.onNavigationItemSelectedListener

【讨论】:

丹麦语,谢谢。我的意图不是自己创建片段。请参阅调用 navController.addOnDestinationChangedListener() 之前的三行。 明白了老兄!检查更新的答案 丹麦语,再次感谢。使用建议的调用,我可以与用户开玩笑,比如显示一个片段的选定图标,但显示另一个不同片段的内容。但它不允许我获取所选片段的实例。 如果您能详细说明您的问题,那就很容易理解了。 检查此链接它是您需要做的一个示例。 github.com/mitcht***/Dagger-Examples/blob/…【参考方案2】:

显然,在对onDestinationChanged() 的调用中无法获取选定的片段。在创建片段之前调用此侦听器的方法。 另一个similar question 中的几个答案建议使用类似的东西(注意null 检查已删除,为简单起见):

Fragment navHostFragment = getSupportFragmentManager().getPrimaryNavigationFragment();
Fragment currentFragment = navHostFragment.getChildFragmentManager().getFragments().get(0);

但是documentation 代表NavController.OnDestinationChangedListener 状态

导航完成后调用此方法,但关联 过渡可能仍在播放。

所以,在玩上面的代码时,onDestinationChanged() 第一次被调用,navHostFragment.getChildFragmentManager().getFragments() 返回的片段列表是空的,这与之前的文档摘录一致。但是第二次被navController 调用,该列表确实包含 1 个元素,在我的例子中,是在更改之前选择的那个。

应该有更多的方法来实现这一点,但我发现将数据传递给片段的方法是这样的:

    创建一个助手Binder,作为参数传递给Fragment

     public interface IMyAppServices 
         String getSomeText();
     
    
     public class MyAppServicesBinder extends Binder 
         public IMyAppServices getService() 
             return new IMyAppServices() 
                 @Override
                 public String getSomeText() 
                     return "This is some text";
                 
             ;
         
     
    
     private final MyAppServicesBinder _myAppServicesBinder = new MyAppServicesBinder();
    

    NavController的初始化中添加以下代码,注意这仅用于测试目的,参见文档here中的示例:

     NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
     Bundle args = new Bundle();
     args.putString("My string", "Is this");
     args.putBinder("MyAppServices", _myAppServicesBinder);
     navController.setGraph(R.navigation.mobile_navigation, args);
    

    现在在Fragment 中,可以通过像这样调用getArguments() 来读取这些参数:

     @Override
     public void onCreate(@Nullable Bundle savedInstanceState) 
         super.onCreate(savedInstanceState);
         Bundle arguments = getArguments();
         MainActivity.MyAppServicesBinder binder = (MainActivity.MyAppServicesBinder)arguments.getBinder("MyAppServices");
         String someText = binder.getService().getSomeText();
         Log.i(TAG, "onCreate() called with SomeTest = " + someText); 
     
    

这为我提供了从Fragment 访问某些应用程序服务或数据提供程序的方法。 现在的问题是如何做相反的事情,以一种简单的方式,并由所选Fragment 已更改的事件触发。

【讨论】:

以上是关于如何在对方法 onDestinationChanged() 的调用中获取选定的 Fragment 实例的主要内容,如果未能解决你的问题,请参考以下文章

如何在对引发异常的 C# webmethod 的 ajax 调用上指定消息?

如何在对系统的影响最小的情况下为您的 APP 处理成就/徽章/奖励?

无法在对受保护方法进行单元测试的适当方法之间做出决定

是否可以在对 Where 的调用中调用命名方法?

C# 应用程序在对 COM 类的方法调用时崩溃

如何在对 aws dynamodb 表的查询中获取所有结果?