Перед тем, как попробовать компонент Navigation, я вручную выполнял транзакции фрагментов и использовал тег фрагмента, чтобы получить текущий фрагмент.
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)
возвращает NavHostFragment, и я хочу получить показанный мной MyFragment.
Спасибо.
Мне нужно было из моего фрагмента запустить замысел камеры, сделать снимок и использовать полученное изображение. Для этого я начал действие и ждал результата в onActivityResult моей основной деятельности. Поскольку получить фрагмент не удалось, я просто переместил все это в сам фрагмент и вроде работает.
Вы хотите выполнить операцию с этим объектом-фрагментом. Или просто хотите, какой фрагмент показан?
Можете ли вы проверить с помощью getChildFragmentManager(), позвоните с
NavHostFragment fragment = supportFragmentManager.findFragmentById(R.id.nav_host);
MyFragment frag = (MyFragment) fragment.getChildFragmentManager().findFragmentByTag(tag);
Я не могу найти способ получить текущий экземпляр фрагмента. Однако вы можете получить идентификатор последнего добавленного фрагмента, используя приведенный ниже код.
navController.currentDestination?.getId()
Он возвращает идентификатор в файле навигации. Надеюсь это поможет.
Спасибо за ваш ответ. Мне нужно было получить фрагмент, чтобы я мог с ним взаимодействовать. Поскольку navController не может его вернуть, я закончил использовать общую модель просмотра, чтобы разговаривать между активностью и ее фрагментами.
Я ожидал, что возвращаемый идентификатор соответствует идентификатору в nav xml, но это не так, поэтому, другими словами, вы не можете проверить navController.getCurrentDestination (). getId () == R.id.myfrag
@LeresAldtai Соответствует идентификатору фрагмента в файле навигации.
Спасибо @slhddn. Ваш комментарий помог мне и смог получить идентификатор из фрагмента и выполнить эту работу. Добавил сюда ответ с моим кодом на случай, если это будет полезно кому-то другому. Ваше здоровье.
Я все еще не понимал, как мы можем сопоставить, если мы находимся на нужном фрагменте. findNavController (). Значение currentDestination.id - 2131296642, значение R.id.myFragment - 1000319. Хотя идентификаторы не имеют значения, отправьте его для понимания. В основном я хочу проверить и сопоставить, на каком фрагменте я нахожусь, из BaseFragment для настройки общего обратного вызова для всех фрагментов.
В своей деятельности определите объект-фрагмент. И из каждого фрагмента вызовите этот метод, передав объект фрагмента, например Таким образом, статический объект фрагмента будет заменяться текущим отображаемым фрагментом каждый раз.
//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>;
Вы также можете установить обратный вызов от фрагмента к действию для лучшего подхода и передать текущий объект фрагмента.
Спасибо за ваше время. Я искал что-то, что можно было бы использовать в самом навигационном компоненте.
На данный момент мне удалось найти способ, и он выглядит следующим образом:
NavHostFragment navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host);
navHostFragment.getChildFragmentManager().getFragments().get(0);
Если вы, конечно, знаете, что это первый фрагмент. Я все еще ищу способ без этого. Я согласен, что это не лучший способ, но на данный момент это должно быть что-то особенное.
Приятно видеть рабочее решение. Я отказался от этого и начал использовать общую модель представления между активностью и фрагментом. Это означает, что я могу передавать события между ними без обратных вызовов или прямого доступа.
Как упоминалось в этом сообщении об ошибке Issuesetracker.google.com/issues/119800853, это неправильный метод. Пожалуйста, подумайте об изменении правильного ответа!
Как уже упоминалось, это не решение, это обходной путь, пока не будет найдено правильное решение.
В моем случае верхний фрагмент не по нулевому индексу, а по последнему индексу
В общем, этого делать не нужно. Если вам нужно это сделать, в вашей архитектуре есть изъян. По крайней мере, если совместить с навигационным графиком.
Андрей: в моем случае у меня есть ProgressBar (ожидающий счетчик) в основном действии, и теперь я хочу добавить кнопку отмены на счетчике. Кнопка отмены в основном действии должна отправить сообщение фрагменту для отмены операции. Должен ли я отказаться от этого подхода и вместо этого создать представление ProgressBar внутри каждого фрагмента? Мне кажется, что «несовершенная архитектура» может быть другим словом, означающим «это сложно сделать с этой (несовершенной?) Платформой».
Добавьте это как отдельный вопрос и свяжите здесь.
java.lang.IndexOutOfBoundsException: Индекс: 3
В документации говорится: «Порядок фрагментов в списке - это порядок, в котором они были добавлены или прикреплены». Я сделал это, и это должно быть немного безопаснее. Но все равно не лучшим образом .. navHostFragment.childFragmentManager.fragments.firstOrNull { it.isVisible }
Это работает, большое спасибо. Я попробовал getPrimaryNavigationFragment (), как было предложено в системе отслеживания ошибок, но это не помогло.
getPrimaryNavigationFragment () не может найти navHostFragment, когда вы программно устанавливаете график и находитесь в startDestination. findViewById не потерпит неудачу в этой ситуации.
На основе этого решения я создал функцию kotlin, которая может проверять, имеет ли текущий фрагмент определенный тип (вместо идентификатора): navHostFragment!!.childFragmentManager.fragments.any { it.javaClass == fragmentClass && it.isVisible } Работает очень похоже. Надеюсь, это кому-нибудь поможет!
Вы должны посмотреть свойство childFragmentManager primaryNavigationFragment вашего навигационного фрагмента, именно здесь есть ссылка на текущий отображаемый фрагмент.
val navHost = supportFragmentManager.findFragmentById(R.id.main_nav_fragment)
navHost?.let { navFragment ->
navFragment.childFragmentManager.primaryNavigationFragment?.let {fragment->
//DO YOUR STUFF
}
}
}
Этот метод теперь задокументирован в fragmentManager Issuesetracker.google.com/issues/119800853.
это кажется небольшой проблемой. обработка навигации вверх, мне нужно настроить панель действий, чтобы получить вызов onSupportNaigateUp ()
Нашел рабочее решение.
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
}
}
Использовать 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);
}
это может быть поздно, но для тех, кто все еще ищет
val currentFragment = NavHostFragment.findNavController(nav_host_fragment).currentDestination?.id
тогда вы можете сделать что-то вроде
if (currentFragment == R.id.myFragment){
//do something
}
обратите внимание, что это для котлина, Java-код должен быть почти таким же
Вот код Java, stackoverflow.com/a/60539704/4291272
Прекрасно работает. Спасибо.
Я объединил Андрей Тоадерс отвечает и Ответ Мукула Бхардваджа с 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.
Мне нужно сделать это, чтобы настроить нижний вид навигации, и я сделал это следующим образом:
val navController = Navigation.findNavController(requireActivity(), R.id.nav_tabs_home)
val bottomNavBar: BottomNavigationView = nav_content_view
NavigationUI.setupWithNavController(bottomNavBar, navController)
Сначала вы получаете текущий фрагмент по идентификатору, затем вы можете найти фрагмент в зависимости от этого идентификатора.
int id = navController.getCurrentDestination().getId();
Fragment fragment = getSupportFragmentManager().findFragmentById(id);
Это кажется самым чистым решением. Обновлять: Функция расширения изменена на свойство. Спасибо Игорь Войда.
1. Создайте следующее расширение
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
val FragmentManager.currentNavigationFragment: Fragment?
get() = primaryNavigationFragment?.childFragmentManager?.fragments?.first()
2. В Activity с NavHostFragment используйте это так:
val currentFragment = supportFragmentManager.currentNavigationFragment
Хорошо - я бы это изменил - я бы определил расширение как свойство FragmentManager.currentNavigationFragment (а не как функцию)
Это рабочее решение. Спасибо!
Лучший способ добиться этого - зарегистрировать обратный вызов жизненного цикла фрагмента.
Согласно исходному вопросу, если ваш 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
}
}
)
Другие методы обратного вызова могут быть переопределены в соответствии с вашими требованиями.
Делать :
• в какой-то кнопке в вашем фрагменте:
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()
}
Если вам нужен экземпляр вашего фрагмента, вы можете использовать:
(Активность) => supportFragmentManager.fragments.first().childFragmentManager.fragments.first()
Это очень некрасиво, но оттуда вы можете проверить, является ли это экземпляром фрагмента, с которым вы хотите поиграть
На 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;
}
Добро пожаловать в SO! Пожалуйста, прежде чем размещать свой ответ, проверьте его ... Код не понятен. Другая проблема: если вы просто приводите код, немного объясните свое решение, максимум, когда есть еще 16 ответов, поэтому вы должны объяснить плюсы своего ответа.
navController().currentDestination?.id
предоставит вам текущий идентификатор Fragment, где
private fun navController() = Navigation.findNavController(this, R.id.navHostFragment)
этот идентификатор - это идентификатор, который вы указали в своем файле Navigation Graph XML в теге fragment. Вы также можете сравнить currentDestination.label, если хотите.
Из Activity, в котором используется NavHostFragment, вы можете использовать приведенный ниже код для получения экземпляра Active Fragment.
val fragmentInstance = myNavHostFragment?.childFragmentManager?.primaryNavigationFragment
Первый фрагмент во фрагменте navhost - это текущий фрагмент
Fragment navHostFragment = getSupportFragmentManager().getPrimaryNavigationFragment();
if (navHostFragment != null)
{Fragment fragment = navHostFragment.getChildFragmentManager().getFragments().get(0);}
Не могли бы вы подробнее рассказать о своем ответе? Может быть, объяснив свой код.
Ответы только на код не приветствуются. Объясните, почему и как ваш ответ решает проблему.
Это правильный ответ. Просто проверьте это. Сначала мы получаем первичный фрагмент навигации, который используется для выделения фрагментов, которые мы раздуваем при навигации. Во-вторых, мы получаем диспетчер дочерних фрагментов, который является частной записью, которую первичный фрагмент построил внутри для управления различными фрагментами, которые мы раздуваем. Наконец, все, что нам нужно сделать, это получить последний раздутый фрагмент. Проверьте работающий пример котлина: val pF = supportFragmentManager.primaryNavigationFragment val cF = pF !!. ChildFragmentManager.fragments.last () cF.updateMethod ()
Этот код работает у меня
NavDestination current = NavHostFragment.findNavController(getSupportFragmentManager().getPrimaryNavigationFragment().getFragmentManager().getFragments().get(0)).getCurrentDestination();
switch (current.getId()) {
case R.id.navigation_saved:
// WRITE CODE
break;
}
Это решение будет работать в большинстве ситуаций. Попробуй это:
val mainFragment = fragmentManager?.primaryNavigationFragment?.parentFragment?.parentFragment as MainFragment
или это:
val mainFragment = fragmentManager?.primaryNavigationFragment?.parentFragment as MainFragment
Это работает для меня
(navController.currentDestination as FragmentNavigator.Destination).className
Он вернет canonicalName вашего фрагмента
Это похоже на отражение
Мое требование - получить текущий видимый фрагмент из родительской активности, мое решение
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 lang?
Это отлично работает для меня, не уверен, что с другими ответами? Может быть, раньше это не работало, но сейчас работает как шарм.
это может быть поздно, но может помочь кому-то позже.
если вы используете вложенную навигацию, вы можете получить такой идентификатор в Котлине
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"){}
В соответствии с ответом и комментарием @slhddn вот как я его использую и получаю фрагмент файла id из nav_graph.xml:
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)
}
}
}
}
Наконец-то рабочее решение для тех из вас, кто все еще ищет На самом деле это очень просто, и вы можете добиться этого с помощью обратного вызова.
Следуй этим шагам:
Создайте слушателя внутри фрагмента, экземпляр которого вы хотите получить из активности 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);
}
}
Реализуйте свой слушатель в хосте 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");
}
}
}
И это все, что вам нужно для работы. Хоп, это поможет кому-нибудь
Лучший способ, которым я мог разобраться, с использованием 1 фрагмента, Kotlin, Navigation:
В файл макета добавьте тег: "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()
val fragment: YourFragment? = yourNavHost.findFragment()
Примечание. Функция расширения findFragment находится в пакете androidx.fragment.app и является общей функцией.
Используя компоненты навигации, я получил текущий отображаемый фрагмент с помощью этой строки кода:
val fragment = parentFragmentManager.primaryNavigationFragment
Вот мое решение для java
NavHostFragment navHostFragment= (NavHostFragment) getSupportFragmentManager().getFragments().get(0);
MyFragment fragment = (MyFragment) navHostFragment.getChildFragmentManager().getFragments().get(0);
fragment.doSomething();
В рамках вашей деятельности, на которой размещен NavHostFragment, мы можем написать следующий фрагмент кода для получения экземпляра отображаемого в данный момент фрагмента.
Fragment fragment = myNavHostFragment.getChildFragmentManager().findFragmentById(R.id.navHostFragmentId)
if (fragment instanceOf MyFragment) {
//Write your code here
}
Здесь R.id.navHostFragmentId - это resId для NavHostFragment в вашей деятельности xml.
Невозможно получить текущий фрагмент. stackoverflow.com/a/50689587/1268507 Чего вы пытаетесь достичь? возможно, есть какое-то другое решение.