如何获取当前显示的片段?
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 顶部的内容。例如,作为Kotlin
的Fragment
:
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 地图片段中获取当前位置
如何在recyclerview中获取所选项目,何时在片段内?