如何获取当前显示的片段?


444

我正在玩Android中的片段。

我知道我可以使用以下代码来更改片段:

FragmentManager fragMgr = getSupportFragmentManager();
FragmentTransaction fragTrans = fragMgr.beginTransaction();

MyFragment myFragment = new MyFragment(); //my custom fragment

fragTrans.replace(android.R.id.content, myFragment);
fragTrans.addToBackStack(null);
fragTrans.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
fragTrans.commit();

我的问题是,在Java文件中,如何获取当前显示的Fragment实例?



Answers:


453

在事务中添加片段时,应使用标签。

fragTrans.replace(android.R.id.content, myFragment, "MY_FRAGMENT");

...以及以后,如果您想检查片段是否可见:

MyFragment myFragment = (MyFragment)getFragmentManager().findFragmentByTag("MY_FRAGMENT");
if (myFragment != null && myFragment.isVisible()) {
   // add your code here
}

另请参见http://developer.android.com/reference/android/app/Fragment.html


74
好的,但是我需要一种方法来立即获取当前显示的Fragment实例,而不是迭代检查所有片段,然后决定现在在屏幕上显示哪个片段。我认为您的答案需要我的代码来反复检查我的每个片段,并找出可见的片段……
Leem.fin 2012年

33
是的,但是一次可以看到多个片段。因此,没有什么比“唯一的活动片段”更....
ramdroid

6
好的,即使显示了几个片段,我仍然需要一种方法来获取它们……这比迭代检查每个片段更好。我担心要像getDisplayedFragment()这样的方法“获取它们”,但是不确定Android中是否存在这样的方法
Leem.fin 2012年

2
我不知道为什么这对您来说是个问题...您通常在一个活动中只有几个片段,检查它们是否可见并不是真正的问题。
ramdroid

12
为什么每个人都赞成这个答案?当然,这是一段不错的代码,但是没有回答按标签获取特定片段的问题。但是Asker想要当前显示的片段,这意味着他不知道显示哪个片段。是的,可能有多个片段,但阅读该问题推断出UI上仅显示了一个片段...。抱歉,由于它不能回答问题上下文,因此不得不投下反对票。
AngryITguy

410

我知道这是一个旧帖子,但是以前也遇到了麻烦。找到了一个解决方案,可以在onBackStackChanged()监听功能中做到这一点

  @Override
    public void onBackPressed() {
        super.onBackPressed();

         Fragment f = getActivity().getFragmentManager().findFragmentById(R.id.fragment_container);
      if(f instanceof CustomFragmentClass) 
        // do something with f
        ((CustomFragmentClass) f).doSomething();

    }

这对我很有用,因为我不想遍历每个片段,而不必寻找可见的片段。希望它也能帮助其他人。


13
正是我在使用导航抽屉旋转屏幕后处理onBackPressed()时要寻找的东西。感谢您回来分享。
ShortFuse

1
findFragmentById不会在内部遍历所有片段吗?:)
Andrew Senner 2015年

46
如果.getFragmentManager报告不兼容的类型,因为它在我的情况一样,那是因为我用的是android.support.app.v4.Fragment,在这种情况下使用正确的方法是getSupportFragmentManager
的Răzvan巴尔布

3
或者您可以使用findFragmentByTag()代替findFragmentById,如果您在将标签添加到片段时提供了片段标签的话fragmentManager.beginTransaction() .add(containerID, frag, fragmentTag) .addToBackStack(fragmentTag) .commit();
DeltaCap019

@AndrewSenner,他的意思是他不想明确地做:)
Farid

159

这是我的解决方案,适用于低片段场景

public Fragment getVisibleFragment(){
    FragmentManager fragmentManager = MainActivity.this.getSupportFragmentManager();
    List<Fragment> fragments = fragmentManager.getFragments();
    if(fragments != null){
        for(Fragment fragment : fragments){
            if(fragment != null && fragment.isVisible())
                return fragment;
        }
    }
    return null;
}

13
fragment在某些情况下(例如,当您弹出后堆栈时)可以为null。因此更好地使用if (fragment != null && fragment.isVisible())
cprcrack 2014年

8
方法不完整,因为可能不仅可见一个片段
letroll

如果看不到任何片段,fragmentManager.getFragments()将返回NULL,这将导致NPE说“试图在NULL对象引用上调用接口方法'java.util.Iterator java.util.List.iterator()'”。因此,for循环应该用一个简单的检查包围:if(fragments!= null)
Pranav Mahajan 2015年

如果可见多个片段怎么办?
Shivaraj Patil

5
从技术上讲,这应根据问题标记为答案。询问者想要当前显示的片段,而不知道它是哪个片段。其他答案不断按标签分类
-AngryITguy

76

每次显示片段时,都必须将其标记放回堆栈:

FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.setTransition(FragmentTransaction.TRANSIT_ENTER_MASK);       
ft.add(R.id.primaryLayout, fragment, tag);
ft.addToBackStack(tag);
ft.commit();        

然后,当您需要获取当前片段时,可以使用以下方法:

public BaseFragment getActiveFragment() {
    if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
        return null;
    }
    String tag = getSupportFragmentManager().getBackStackEntryAt(getSupportFragmentManager().getBackStackEntryCount() - 1).getName();
    return (BaseFragment) getSupportFragmentManager().findFragmentByTag(tag);
}

4
我相当确定该技术不适用于与ViewPager一起使用的片段,因为它们不是通过标签添加的。只是扔在那里。
loeschg 2014年

3
这似乎仅在您调用时起作用addToBackStack(tag),但是如果您不希望将片段添加到后堆栈中怎么办?
cprcrack 2014年

1
仅当您的标签恰好与后堆栈名称相同时,这才起作用,这看起来很不正常。

如果在同一笔交易中添加5个片段会怎样?
凯文·林

可以说,如果您为不需要名称的某些Fragment调用addToBackStack(null),则方法getName()可以返回null并得到一个NPE。@ dpk,docu说:创建该条目时获取提供给FragmentTransaction.addToBackStack(String)的名称。另外,添加一个空检查,如果EntryCount为0,则您尝试接收顶部的条目。
JacksOnF1re 2014年

23

下面的代码中我用来查找当前显示片段的内容。它很简单,现在对我有用。它在保存片段的活动中运行

    FragmentManager fragManager = this.getSupportFragmentManager();
    int count = this.getSupportFragmentManager().getBackStackEntryCount();
    Fragment frag = fragManager.getFragments().get(count>0?count-1:count);

3
您应该检查以确保count> 0,以便get(count-1)不会引发IndexOutofBoundsException
kehers

@tainy这不会给出被替换但未被添加到backstack的片段。如果是,那么我可以使用tag参数获取它吗?
cafebabe1991

在调用popBackStack(null,FragmentManager.POP_BACK_STACK_INCLUSIVE)并添加更多片段之后,getFragments调用不会为您提供片段添加顺序。
LEHO

如果活动有多个可见的片段,则您的方法是错误的。例如:带有片段的ViewPager。如我所知,getBackStackEntryCount()返回事务的数量,而不是片段的数量
HungNM2

您应该检查fragManager.getFragments().size()而不是getBackStackEntryCount()因为它们不一定相等(这是我的情况)。否则,您可能会因IndexOutOfBoundsException而崩溃
RustamG

19

科特林方式;

val currentFragment = supportFragmentManager.fragments.last()

仅适用于API 26及更高版本
ibit

1
@ibit是什么意思,仅适用于API 26及更高版本?我尝试在模拟器API 19上正常运行。
HendraWD

@HendraWD你是正确的!我指的是框架函数中的getFragments()函数,该函数是API 26及更高版本。
ibit

2
照顾自己。如果在您之前没有任何片段堆栈,则可能导致空异常!
Jetwiz

这行不通吗?java.lang.ClassCastException:androidx.navigation.fragment.NavHostFragment无法转换为Fragment
coolcool1994

13

反应方式:

Observable.from(getSupportFragmentManager().getFragments())
    .filter(fragment -> fragment.isVisible())
    .subscribe(fragment1 -> {
        // Do something with it
    }, throwable1 -> {
        // 
    });

7
那反应如何?您使用可观察到的东西,但只会发出一次
蒂姆

6
这只是一个过分的杀伤力。一个简单的增强型forloop可以做到相同,甚至更快。
Peterstev Uremgba '18

仅对流api使用反应流不是那么聪明,实际上就像他们说的那样,如果您想将列表视为流并应用过滤器api
Steam Java8

13

我的方法基于这样的try / catch:

MyFragment viewer = null;
    if(getFragmentManager().findFragmentByTag(MY_TAG_FRAGMENT) instanceOf MyFragment){
    viewer = (MyFragment) getFragmentManager().findFragmentByTag(MY_TAG_FRAGMENT);
}

但是也许有更好的方法...


1
我的问题是如何获取当前显示的片段,这意味着可能会有很多片段,我只想获取当前显示的片段的实例,为什么要使用(MyFragment)?我的意思是它可以是屏幕上的任何片段显示...我想获得当前显示的片段。
Leem.fin '02

MY_TAG_FRAGMENT是我在使用替换之前创建的片段的标签,例如:fragmentTransaction.replace(R.id.FL_MyFragment,MyFragment,MY_TAG_FRAGMENT);
Thordax

如果我有多个片段,调用.replace(...)时是否可以对所有片段使用一个标签?
Leem.fin 2012年

是的,没问题,请使用以下代码:fragmentTransaction.replace(R.id.FL_MyFragment,MyFragment,MY_TAG_FRAGMENT); 可能会顺利进行。
Thordax 2012年

1
好吧,我真的不明白你的意思,我的意思是我需要一种方法来立即获取当前显示的Fragment实例,而不是反复检查所有片段,然后决定现在在屏幕上显示哪个片段。
Leem.fin

11

嗯,这个问题引起了很多关注和关注,但是从我的角度来看,它仍然没有包含最简单的解决方案-使用getFragments()。

            List fragments = getSupportFragmentManager().getFragments();
            mCurrentFragment = fragments.get(fragments.size() - 1);

1
不幸的是,这并不总是正确的getSupportFragmentManager().getFragments() ,如果刚从backstack中弹出了最新的片段,它会给您返回一个列表,但可以包含空值。方程:如果您在方法内部进行检查onBackStackChanged()并在popBackStack()某处调用,则mCurrentFragment将会为null
Stumi

@Stumi感谢您提供此信息。是的,听起来像是Android中许多怪异的生命周期问题之一。
Nativ,

9

您可以查询哪个片段加载到“活动”内容框架中,并获取片段类或片段“简单名称”(作为字符串)。

public String getCurrentFragment(){
     return activity.getSupportFragmentManager().findFragmentById(R.id.content_frame).getClass().getSimpleName();
}

用法:

Log.d(TAG, getCurrentFragment());

输出:

D/MainActivity: FragOne

8

有点晚了,但是对于任何感兴趣的人:如果您知道FragmentManager中所需片段的索引,只需获取对其的引用并检查isMenuVisible()函数即可!这里 :

getSupportFragmentManager().getFragments().get(0).isMenuVisible()

如果true它对用户可见,等等!


1
奇怪的是,这是这里列出的唯一对我有用的解决方案。与此同时,多个片段可以有isVisible() == truegetView() != null。另外,在我的代码getBackStackEntryCount中始终为零。尽管我不使用菜单,isMenuVisible()但到目前为止,这是唯一可以可靠地指向当前“活动”片段的判别式。ty
parvus

7

1)

ft.replace(R.id.content_frame, fragment, **tag**).commit();

2)

FragmentManager fragmentManager = getSupportFragmentManager();
Fragment currentFragment = fragmentManager.findFragmentById(R.id.content_frame);

3)

if (currentFragment.getTag().equals(**"Fragment_Main"**))
{
 //Do something
}
else
if (currentFragment.getTag().equals(**"Fragment_DM"**))
{
//Do something
}

6

如果到达这里并且您正在使用Kotlin:

var fragment = supportFragmentManager.findFragmentById(R.id.fragment_container)

R.id.fragment_containeridfragment是展示自己的activity

或者,如果您需要更好的解决方案:

supportFragmentManager.findFragmentById(R.id.content_main)?.let {
    // the fragment exists

    if (it is FooFragment) {
        // The presented fragment is FooFragment type
    }
}

5

受到泰尼的回答的启发,这是我的两分钱。与大多数其他实现相比,修改很少。

private Fragment getCurrentFragment() {
    FragmentManager fragmentManager = myActivity.getSupportFragmentManager();
    int stackCount = fragmentManager.getBackStackEntryCount();
    if( fragmentManager.getFragments() != null ) return fragmentManager.getFragments().get( stackCount > 0 ? stackCount-1 : stackCount );
    else return null;
}

"myActivity"如果这是您当前的活动或使用对您的活动的引用,请替换为“ this”。


5

有一个叫方法findFragmentById()SupportFragmentManager。我在活动容器中使用它,例如:

public Fragment currentFragment(){
    return getSupportFragmentManager().findFragmentById(R.id.activity_newsfeed_frame);
}

这就是获取最新消息的方法Fragment。如果您有自定义项,Fragment并且需要检查Fragment它是什么,我通常使用instanceof

if (currentFragment() instanceof MyFrag){
    // Do something here
}


4

这是获取当前片段的简单方法。

getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
  @Override public void onBackStackChanged() {
    currentFragment = fragmentManager.findFragmentById(R.id.content);
    if (currentFragment !=  null && (currentFragment instanceof LoginScreenFragment)) {
      logout.setVisibility(View.GONE);
    } else {
      logout.setVisibility(View.VISIBLE);
    }
  }
});

4

Sev的答案适用于您按下后退按钮或更改后退堆栈的情况。

不过,我做了一些稍有不同的事情。我在基本Fragment及其派生片段上有一个Backstack Change侦听器设置,此代码在侦听器中:

Fragment f = getActivity().getSupportFragmentManager().findFragmentById(R.id.container);

if (f.getClass().equals(getClass())) {
    // On back button, or popBackStack(),
    // the fragment that's becoming visible executes here,
    // but not the one being popped, or others on the back stack

    // So, for my case, I can change action bar bg color per fragment
}

3

这对我来说是工作。我希望这会帮助某人。

FragmentManager fragmentManager = this.getSupportFragmentManager();  
        String tag = fragmentManager
                    .getBackStackEntryAt(
                    fragmentManager
                    .getBackStackEntryCount() - 1)
                    .getName();
              Log.d("This is your Top Fragment name: ", ""+tag);

3

如果您是从父活动中获取Fragment的当前实例,则可以

findFragmentByID(R.id.container);

实际上,这是填充在视图上的当前片段实例。我遇到过同样的问题。我必须加载相同的片段两次,将一个片段保留在后堆栈上。

以下方法无效。它只是得到一个带有标签的片段。不要在这种方法上浪费时间。我敢肯定它有它的用途,但是获取同一Fragment的最新版本不是其中之一。

findFragmentByTag()

3

签出此解决方案。它为我工作,以获取当前的片段。

if(getSupportFragmentManager().getBackStackEntryCount() > 0){
        android.support.v4.app.Fragment f = 
         getSupportFragmentManager().findFragmentById(R.id.fragment_container);
        if(f instanceof ProfileFragment){
            Log.d(TAG, "Profile Fragment");
        }else if(f instanceof SavedLocationsFragment){
            Log.d(TAG, "SavedLocations Fragment");
        }else if(f instanceof AddLocationFragment){
            Log.d(TAG, "Add Locations Fragment");
        }

2
final FragmentManager fm=this.getSupportFragmentManager();
final Fragment fragment=fm.findFragmentByTag("MY_FRAGMENT");

if(fragment != null && fragment.isVisible()){
      Log.i("TAG","my fragment is visible");
}
else{
      Log.i("TAG","my fragment is not visible");
}

这适用于Jetpack吗?
IgorGanapolsky

2
getSupportFragmentManager().findFragmentById(R.id.content_frame).getClass().getSimpleName();

好吧,我想这是对这个问题最直接的答案。我希望这有帮助。


2

这是Kotlin解决方案:

if ( this.getSupportFragmentManager().getBackStackEntryCount()>0 ) {
    var fgmt = this.getSupportFragmentManager().fragments[this.getSupportFragmentManager().getBackStackEntryCount()-1]
    if( fgmt is FgmtClassName ) {
        (fgmt as FgmtClassName).doSth()
    }
}

简化方式:

with ( this.getSupportFragmentManager() ) {
    if ( getBackStackEntryCount()>0 ) {
        var fgmt = fragments[getBackStackEntryCount()-1]
        if ( fgmt is FgmtClassName ) {
            (fgmt as FgmtClassName).doSth()
        }
    }
}

2

做到这一点的简单方法:

Fragment fr=getSupportFragmentManager().findFragmentById(R.id.fragment_container);
String fragmentName = fr.getClass().getSimpleName();

1

在主活动中,将新片段附加到活动时,将调用onAttachFragment(Fragment fragment)方法。在这种方法中,您可以获取当前片段的实例。但是,当片段从后堆栈弹出时,即按下后退按钮以使顶部片段位于堆栈顶部时,不会调用onAttachFragment方法。我仍在寻找当活动中出现片段时在主活动中触发的回调方法。


好亲近!:-)
布隆德尔

我们使用Jetpack导航库
IgorGanapolsky

1

对于滚动片段,如果使用ViewPager类的实例(假设为mVeiwPager),则可以调用mViewPager.getCurrentItem()获取当前片段的int编号。

在MainLayout.xml中

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical"
    tools:context="unidesign.photo360.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:fitsSystemWindows="false"
        android:theme="@style/AppTheme.AppBarOverlay"
        app:expanded="false">

        <android.support.v7.widget.Toolbar
            android:id="@+id/main_activity_toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            app:popupTheme="@style/AppTheme.PopupOverlay"
            app:title="@string/app_name">

        </android.support.v7.widget.Toolbar>

    </android.support.design.widget.AppBarLayout>


    <android.support.v4.view.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

    </android.support.v4.view.ViewPager>
    
</android.support.design.widget.CoordinatorLayout>

在MainActivity.kt中

class MainActivity : AppCompatActivity() {
    
        lateinit var mViewPager: ViewPager
        lateinit var pageAdapter: PageAdapter
        
//      ...
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            
            pageAdapter = PageAdapter(supportFragmentManager)
            mViewPager = findViewById(R.id.view_pager)
//          ...
            }
            
        override fun onResume() {
          super.onResume()
          var currentFragment = pageAdapter.getItem(mViewPager.currentItem)
//         ...
          }


1

我发现findFragmentByTag那不方便。如果您有String currentFragmentTag自己的Activity父母Fragment,则需要将其保存onSaveInstanceState并还原onCreate。即使您这样做,在Activity重新创建时onAttachFragment也会在之前调用onCreate,因此您不能currentFragmentTag在中使用onAttachFragment(例如,根据currentFragmentTag),因为它可能尚未还原。

我使用以下代码:

Fragment getCurrentFragment() {
    List<Fragment> fragments = getSupportFragmentManager().getFragments();
    if(fragments.isEmpty()) {
        return null;
    }
    return fragments.get(fragments.size()-1);
}

的文件FragmentManager状态

列表中片段的顺序是添加或附加片段的顺序。

当您需要根据当前片段类型执行操作时,只需使用getCurrentFragment() instance of MyFragment代替currentFragmentTag.equals("my_fragment_tag")

请注意,getCurrentFragment()onAttachFragment不会获得Fragment附件,而是前一个附件。


1

我最近不得不这样做,这里没有一个答案真的适合这种情况。

如果您确信只有一个片段是可见的(全屏显示),那么您真的想查找后端堆栈顶部的内容。举例来说,作为一个KotlinFragment

import androidx.fragment.app.Fragment

fun Fragment.setVisibilityChangeListener(clazz: Class<out Fragment>, listener: (Boolean) -> Unit) {
    fragmentManager?.run {
        addOnBackStackChangedListener {
            val topFragmentTag = getBackStackEntryAt(backStackEntryCount - 1).name
            val topFragment = findFragmentByTag(topFragmentTag)
            listener(topFragment != null && topFragment::class.java == clazz)
        }
    }
}

并像这样使用它:

class MyFragment: Fragment {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        setVisibilityChangeListener(this::class.java) { visible -> 
            // Do stuff
        }
    }
}

1

您也可以使用logcat中的URL轻松完成此操作,该URL会将您重定向到当前片段源代码的源代码。首先,您需要在主机活动中添加OnBackStackChangedListener,例如-

activity.getChildFragmentManager().addOnBackStackChangedListener(backStackListener);

并且OnBackStackChangedListener实现是-

    public FragmentManager.OnBackStackChangedListener backStackListener = () -> {

    String simpleName = "";
    String stackName = getStackTopName().trim();

    if (Validator.isValid(stackName) && stackName.length() > 0) {

      simpleName = stackName.substring(Objects.requireNonNull(stackName).lastIndexOf('.') + 1).trim();

      List<Fragment >
       fragmentList = getChildFragmentManager().getFragments();
      Fragment myCurrentFragment;

      for (int i = 0; i < fragmentList.size(); i++) {
       myCurrentFragment= fragmentList.get(i);
       if (myCurrentFragment.getClass().getSimpleName().equals(simpleName)) {
        //Now you get the current displaying fragment assigned in myCurrentFragment.
        break;
       }
       myFragment = null;
      }
     }


     //The code below is for the source code redirectable logcat which would be optional for you.
     StackTraceElement stackTraceElement = new StackTraceElement(simpleName, "", simpleName + ".java", 50);
     String fileName = stackTraceElement.getFileName();
     if (fileName == null) fileName = "";
     final String info = "Current Fragment is:" + "(" + fileName + ":" +
     stackTraceElement.getLineNumber() + ")";
     Log.d("now", info + "\n\n");
    };

getStackTopName() 方法是-

public String getStackTopName() {
    FragmentManager.BackStackEntry backEntry = null;
    FragmentManager fragmentManager = getChildFragmentManager();
    if (fragmentManager != null) {
        if (getChildFragmentManager().getBackStackEntryCount() > 0)
            backEntry = getChildFragmentManager().getBackStackEntryAt(
                    getChildFragmentManager().getBackStackEntryCount() - 1
            );
    }
    return backEntry != null ? backEntry.getName() : null;
}
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.