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

Posted

技术标签:

【中文标题】如何获取当前显示的片段?【英文标题】:How do I get the currently displayed fragment? 【发布时间】:2012-03-06 20:48:03 【问题描述】:

我在 android 中玩 Fragments。

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

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 实例?

【问题讨论】:

相关 - ***.com/a/21104084/80428 Get the current fragment object的可能重复 【参考方案1】:

我最近不得不这样做

public Fragment getCurrentFragment() 
     return fragmentManager.findFragmentById(R.id.container);

最后我得到了这个容器上的最后一个片段。

【讨论】:

【参考方案2】:

如果是滚动片段,当您使用 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_
    android:layout_
    android:fitsSystemWindows="true"
    android:orientation="vertical"
    tools:context="unidesign.photo360.MainActivity">

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

        <android.support.v7.widget.Toolbar
            android:id="@+id/main_activity_toolbar"
            android:layout_
            android:layout_
            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_
        android:layout_>

    </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)
//         ...
          

【讨论】:

【参考方案3】:

我发现findFragmentByTag 不是很方便。如果您的Activity 或父Fragment 中有String currentFragmentTag,则需要将其保存在onSaveInstanceState 中并在onCreate 中恢复它。即使你这样做了,当Activity重新创建时,onAttachFragment会在onCreate之前调用,所以你不能在onAttachFragment中使用currentFragmentTag(例如,根据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")

注意onAttachFragment中的getCurrentFragment()不会得到附加的Fragment,而是之前附加的。

【讨论】:

【参考方案4】:

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

如果您确信只有一个片段可见(全屏),那么真的很想找到 backstack 顶部的内容。例如,作为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
        
    

【讨论】:

【参考方案5】:

这是一个 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()
        
    

【讨论】:

尝试在片段之间导航并按回,应用会崩溃!【参考方案6】:

如果您使用的是 Jetpack 导航库:

val currentFragment = defaultNavigator.currentDestination

【讨论】:

【参考方案7】:

在主活动中,当新的片段附加到活动时,会调用onAttachFragment(Fragment fragment) 方法。在这个方法中,您可以获得当前片段的实例。然而,onAttachFragment(Fragment fragment) 方法不会在片段从返回堆栈中弹出时调用,即当按下返回按钮以获取堆栈顶部的顶部片段时。我仍在寻找当片段在活动中可见时在主活动中触发的回调方法。

【讨论】:

如此接近! :-) 我们使用 Jetpack Navigation 库【参考方案8】:

也许最简单的方法是:

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

它对我有用

【讨论】:

【参考方案9】:

在这些情况下使用事件总线(如Otto、EventBus 或RxJava Bus)特别方便。

虽然这种方法不一定将当前可见的片段作为对象传递给您(虽然这也可以做到,但会导致更长的调用链),但它允许您对当前可见的片段执行操作(即通常你想知道当前可见的片段做什么)。

    确保尊重片段生命周期并在适当的时间注册/取消注册事件总线 此时您需要知道当前可见的片段并执行一组操作。使用事件总线拍摄事件。

所有已注册到总线的可见片段都将执行必要的操作。

【讨论】:

能否详细解释一下? 在这种情况下我们如何使用事件总线?你能不能给我分享一个例子。 如果您想返回一个值,这将无法正常工作【参考方案10】:

我遇到了类似的问题,我想知道按下返回键时最后显示的是哪个片段。我使用了一个对我有用的非常简单的解决方案。每次打开片段时,在 onCreate() 方法中,我都会在我的单例中设置一个变量(将“myFragment”替换为片段的名称)

MySingleton.currentFragment = myFragment.class;

变量在单例中声明为

public static Class currentFragment = null; 

然后在 onBackPressed() 中检查

    if (MySingleton.currentFragment == myFragment.class)
        // do something
        return;
    
    super.onBackPressed();

确保调用 super.onBackPressed();在“返回”之后,否则应用程序将处理返回键,这在我的情况下导致应用程序终止。

【讨论】:

【参考方案11】:

有点奇怪,但我查看了 FragmentManager$FragmentManagerImpl 并且以下对我有用:

public static Fragment getActiveFragment(Activity activity, int index)

    Bundle bundle = new Bundle();
    String key = "i";
    bundle.putInt(key,index);
    Fragment fragment=null;
    try
    
        fragment = activity.getFragmentManager().getFragment(bundle, key);
     catch(Exception e)
    return fragment;

要获取第一个活动片段,请使用 0 作为索引

【讨论】:

【参考方案12】:
public Fragment getVisibleFragment() 
    FragmentManager fragmentManager = getSupportFragmentManager();
    List<Fragment> fragments = fragmentManager.getFragments();
    if(fragments != null) 
        for (Fragment fragment : fragments) 
            if (fragment != null && fragment.isVisible())
                return fragment;
        
    
    return null;

这对我有用。您需要在遍历片段列表之前执行空检查。可能存在堆栈中没有加载任何片段的情况。

可以将返回的片段与您要放入堆栈的片段进行比较。

【讨论】:

【参考方案13】:

请试试这个方法.....

private Fragment getCurrentFragment()
    FragmentManager fragmentManager = getSupportFragmentManager();
    String fragmentTag = fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount() - 1).getName();
    Fragment currentFragment = getSupportFragmentManager()
.findFragmentByTag(fragmentTag);
    return currentFragment;

【讨论】:

【参考方案14】:

您可以使用以下代码获取当前片段

FragmentClass f = (FragmentClass)viewPager.getAdapter().instantiateItem(viewPager, viewPager.getCurrentItem());

【讨论】:

【参考方案15】:

您可以添加一个类变量 selectedFragment,并且每次更改片段时都会更新该变量。

public Fragment selectedFragment;
public void changeFragment(Fragment newFragment)
    FragmentManager fragMgr = getSupportFragmentManager();
    FragmentTransaction fragTrans = fragMgr.beginTransaction();
    fragTrans.replace(android.R.id.content, newFragment);
    fragTrans.addToBackStack(null);
    fragTrans.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
    fragTrans.commit();
    //here you update the variable
    selectedFragment = newFragment;

那么你可以在任何你想要的地方使用 selectedFragment

【讨论】:

是的。而对于“Back”的处理你有没有忘记?【参考方案16】:

您好,我知道这是一个非常古老的问题,但我想分享我自己的解决方案..

为了获取用户浏览过的片段列表,我创建了一个辅助类:

public class MyHelperClass

    private static ArrayList<Fragment> listFragment = new ArrayList<>();

    public static void addFragment(Fragment f)
        if(!existFragment(f)) 
            listFragment.add(f);
        
    
    public static void removeFragment()
        if(listFragment.size()>0)
            listFragment.remove(listFragment.size()-1);
    
    public static Fragment getCurrentFragment()
        return listFragment.get(listFragment.size()-1);
    
    public static int sizeFragments()
        return listFragment.size();
    
    private static Boolean existFragment(Fragment f)
        Boolean ret = false;
        for(Fragment fragment : listFragment)
            if (fragment.getClass() == f.getClass())
                ret = true;
            
        
        return ret;
    

在主 Activity 中,我重写了 onAttachFragment 方法

@Override
public void onAttachFragment(Fragment f) 
    super.onAttachFragment(f);

    MyHelperClass.addFragment(f);

另外,我重写了 onBackPressed 方法:

@Override
public void onBackPressed() 
        General.removeFragment();
        if(General.sizeFragments()>0)
            Fragment fragment = null;
            Class fragmentClass = General.getCurrentFragment().getClass();

            try 
                fragment = (Fragment) fragmentClass.newInstance();
                fragment.setArguments(General.getCurrentFragment().getArguments());
             catch (Exception e) 
                e.printStackTrace();
            

            fragmentManager.beginTransaction().replace(R.id.flContent, fragment).commit();
        else
            super.onBackPressed();
        

所以通过这种方式,您可以随时使用 MyHelperClass.getCurrentFragment() 获取活动片段

我希望这对任何人都有帮助

问候

【讨论】:

【参考方案17】:

我想你想看看你在哪个片段中 我想有一个解决方案我不认为它是最好的 但它有效 首先


你应该创建你的父片段,它扩展片段


public class Fragment2 extends Fragment 
    private static position;
    public static int getPosition() 
        return position;
    

    public static void setPosition(int position) 
        FragmentSecondParent.position = position;
    

第二

你应该在每个片段和每个 onCreateView 写入中扩展它

setPosition(//your fragmentposition here)

第三


你想在哪里得到你应该得到的当前片段


写这个

Fragment2 fragment= (Fragment2) getSupportFragmentManager()
    .findFragmentById(R.id.fragment_status);

int position = fragment.getPosition();
if(//position condition)

    //your code here

【讨论】:

【参考方案18】:

这是最好的方法:

       android.app.Fragment currentFragment=getFragmentManager().findFragmentById(R.id.main_container);
            if(currentFragment!=null)
            
                String[] currentFragmentName = currentFragment.toString().split("\\");
                if (currentFragmentName[0].toString().equalsIgnoreCase("HomeSubjectFragment"))
                
                    fragment = new HomeStagesFragment();
                    tx = getSupportFragmentManager().beginTransaction();
                    tx.replace(R.id.main_container, fragment);
                    tx.addToBackStack(null);
                    tx.commit();
                
                else if(currentFragmentName[0].toString().equalsIgnoreCase("HomeStagesFragment"))
                
                    new AlertDialog.Builder(this)
                            .setMessage("Are you sure you want to exit?")
                            .setCancelable(false)
                            .setPositiveButton("Yes", new DialogInterface.OnClickListener() 
                                public void onClick(DialogInterface dialog, int id) 
                                    finish();
                                
                            )
                            .setNegativeButton("No", null)
                            .show();
                

            

不要忘记在标题中定义它:

private Fragment fragment;
FragmentTransaction tx;

【讨论】:

【参考方案19】:

如果您使用支持库 v13,则此问题已得到修复,您只需覆盖:

@Override
public void setUserVisibleHint(boolean isVisibleToUser)

    // TODO Auto-generated method stub
    super.setUserVisibleHint(isVisibleToUser);

问题是,你不能将两者混合,因为片段与版本 4 的片段类不兼容。

如果您不是并且您使用的是 V4 支持库,请将 setPrimaryItem 方法覆盖到您的 FragmentStatePagerAdapter。

我用它来更新大列表中的 Actionbat 标题。

【讨论】:

【参考方案20】:

我也坚持这一点。我最后做了什么,只是声明了一个 Fragments 数组:

private static PlaceholderFragment[] arrFrg;

(在我的情况下它是 PlaceholderFragment)并将所有这些 Fragment 打包到这个数组中而没有标记:)

        public static PlaceholderFragment newInstance(int sectionNumber) 
            final PlaceholderFragment fragment = new PlaceholderFragment();
            Bundle args = new Bundle();
            args.putInt(ARG_SECTION_NUMBER, sectionNumber);
            fragment.setArguments(args);
            arrFrg[sectionNumber] = fragment;

            return fragment;

然后就可以很方便的访问当前显示的Fragment了:

arrFrg[mViewPager.getCurrentItem()];

我明白,这可能不是最好的解决方案,但它对我来说非常有效:)

【讨论】:

【参考方案21】:

如果您有嵌套片段,例如 viewpagers 内的 viewpagers 等 并且您想获取所有嵌套的片段。

感谢Matt Mombrea answer 的小改动版本。

private List<Fragment> getVisibleFragments(List<Fragment> searchFragments, List<Fragment> visibleFragments) 
    if (searchFragments != null && searchFragments.size() > 0) 
        for (Fragment fragment : searchFragments) 
            List<Fragment> nestedFragments = new ArrayList<>();
            List<Fragment> childFMFragments = fragment.getChildFragmentManager().getFragments();
            List<Fragment> fmFragments = fragment.getFragmentManager().getFragments();
            fmFragments.retainAll(childFMFragments);
            nestedFragments.addAll(childFMFragments);
            nestedFragments.addAll(fmFragments);
            getVisibleFragments(nestedFragments, visibleFragments);
            if (fragment != null && fragment.isVisible()) 
                visibleFragments.add(fragment);
            
        
    
    return visibleFragments;

这是用法:

List<Fragment> allVisibleFragments = getVisibleFragments(searchFragments, visibleFragments);

例如:

List<Fragment> visibleFragments = new ArrayList<>();
List<Fragment> searchFragments = MainActivity.this.getSupportFragmentManager().getFragments();
Toast.makeText(this, ""+getVisibleFragments(searchFragments, visibleFragments), Toast.LENGTH_LONG).show();

【讨论】:

【参考方案22】:

如果 getFragmentManager() 不起作用,请尝试使用 getSupportFragmentManager() 并在加载片段时添加标签。

public void onBackPressed()

    Fragment fragment=getSupportFragmentManager().findFragmentByTag(/*enter your tag*/);


    if(fragment!=null && fragment.isVisible())
    
        //do your code here
    
    else
    
       //do your code here
    


【讨论】:

【参考方案23】:

在您的活动中,在创建之前初始化您的片段

    MyFragment myFragment = new MyFragment(); // add this
 @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) 
........

然后调用该方法查看你的片段

openFragment(this.myFragment);

方法如下

(R.id.frame_container) 是您在 xml 文件中的片段容器 ID (框架布局)

 private void openFragment(final Fragment fragment)   
        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        transaction.replace(R.id.frame_container, fragment);
        transaction.commit();

    

那么在你的活动中,Override 方法应该是这样的

    public void onBackPressed() 
            if (myFragment.isVisible()) 
                myFragment.onBackPressed(this);
                return;
            
            super.onBackPressed();
        

然后在你的片段里面放这个方法

public  void onBackPressed(Activity activity) 
    Toast.makeText(activity, "Back Pressed inside Fragment", Toast.LENGTH_SHORT).show();

【讨论】:

【参考方案24】:

为此 FragmentManager 返回当前活动的主导航片段。

public @Nullable Fragment getPrimaryNavigationFragment()      
Fragment fragment = fragmentManager.getPrimaryNavigationFragment();  
    

【讨论】:

【参考方案25】:

我只需要这样做,如果您可以访问导航控制器,则可以轻松地从后台堆栈中获取当前片段:

// current fragments label/title:
navController.backStack.last.destination.label

// current fragments name:
navController.backStack.last.destination.displayName

要访问导航控制器(替换为正确的名称):

val navController = findNavController(R.id.nav_host_fragment_activity_main)

【讨论】:

【参考方案26】:

在androidx.fragment:fragment-ktx:1.4 中有一种新方法,我们可以将最近添加的片段添加到容器中。 如果您使用FragmentContainerView 作为您的片段的容器,这将很容易:

val fragmentContainer: FragmentContainerView = ...
val currentFragment: Fragment = fragmentContainer.getFragment()

【讨论】:

以上是关于如何获取当前显示的片段?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 TabLayout 在 ViewPager 中的 Google 地图片段中获取当前位置

如何获取 HTML5 视频中加载的当前片段百分比?

如何从布局中获取膨胀的片段参考?

如何在recyclerview中获取所选项目,何时在片段内?

Android - 如何显示包含从片段中获取的字符串的快餐栏

如何在android的地图中获取当前位置?