如何在对方法 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.xml 和 fragment_notifications.xml 与 fragment_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 调用上指定消息?