如何为ViewPager片段设置标签?

27

我正在使用带有4个标签、一个viewpager和每个标签对应的4个fragment类的slidingTabLayout。

当用户点击某个按钮时,我需要重新加载我的项目中的一个fragment(第一个标签页),为此我需要为该fragment设置一个标记。

我的问题是:如何为其中一个viewpager的fragment设置标记?

我的MainActivity标签和viewpager代码:

public class MainActivity extends AppCompatActivity
    implements NavigationView.OnNavigationItemSelectedListener {



Toolbar toolbar;
ViewPager pager;
ViewPagerAdapter adapter;
SlidingTabLayout tabs;
CharSequence Titles[]={"نوع","قیمت","برند","همه"};
int Numboftabs = 4;




@Override
protected void attachBaseContext(Context newBase) {
    super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase));
}



@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);




    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Snackbar.make(view, "درج آگهی رایگان", Snackbar.LENGTH_LONG)
                    .setAction("Action", null).show();
        }
    });



    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);

    NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
    navigationView.setNavigationItemSelectedListener(this);




    // Creating The ViewPagerAdapter and Passing Fragment Manager, Titles fot the Tabs and Number Of Tabs.
    adapter =  new ViewPagerAdapter(getSupportFragmentManager(),Titles,Numboftabs);

    // Assigning ViewPager View and setting the adapter
    pager = (ViewPager) findViewById(R.id.pager);
    pager.setAdapter(adapter);

    // Assiging the Sliding Tab Layout View
    tabs = (SlidingTabLayout) findViewById(R.id.tabs);
    tabs.setDistributeEvenly(true); // To make the Tabs Fixed set this true, This makes the tabs Space Evenly in Available width

    // Setting Custom Color for the Scroll bar indicator of the Tab View
    tabs.setCustomTabColorizer(new SlidingTabLayout.TabColorizer() {
        @Override
        public int getIndicatorColor(int position) {
            return getResources().getColor(R.color.colorWhite);
        }
    });

    // Setting the ViewPager For the SlidingTabsLayout
    tabs.setViewPager(pager);
    pager.setCurrentItem(3);




} and ....

非常感谢你


如果您想使用ViewPager中已有的片段,可以使用适配器的getItem(position)方法。至于标签,我认为您应该在适配器的构造函数中传递标签,然后将其设置到每个片段中。 - Sandip Fichadiya
谢谢你,我的朋友,但是你能否更详细地解释一下如何将标签传递给适配器吗?谢谢。 - K1-Aria
4个回答

33

有几种解决方法。

1). 当FragmentPagerAdapterFragmentManager添加Fragment时,它使用特殊的标签,基于Fragment将被放置的特定位置。因此,您可以使用以下代码行在您的ViewPager中简单地获取当前可见的Fragment

 Fragment fragment = getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":" + ViewPager.getCurrentItem());
 // based on the current position you can then cast the page to the correct Fragment class and call some method inside that fragment to reload the data:
 if (0 == ViewPager.getCurrentItem() && null != fragment) {
      ((TabFragment1)fragment).reloadFragmentData();     
 } 

2). 你应该跟踪 FragmentStatePagerAdapter 中所有活动的片段。示例可以参考第二种方法 这里

3). 你可以使用 EventBus 从任何地方向你的 Fragment 发送事件。你只需要为该特定事件注册你的 Fragment 。有关官方文档,请参见 此处


但我只是使用了ViewPagerAdapter。我该怎么办? - K1-Aria
你的 ViewPagerAdapter 可能是继承自 FragmentPagerAdapter 或 FragmentStatePagerAdapter,不是吗? - Mukesh Rana
不,我从未使用过它!!!有没有其他方法可以为片段设置标签?我不知道为什么这么难!!! - K1-Aria
啊哈!谢谢。现在有了EventBus,我可以给片段打标签吗?或者我可以刷新它们吗? - K1-Aria
你可以刷新它们。你不需要设置任何标记。你只需要注册你的 Fragment 来处理从任何地方发送的重载事件。 - Mukesh Rana
显示剩余13条评论

11

下面是 FragmentPagerAdapter 使用的一种方法来创建 Fragment 的标签名称:

private static String makeFragmentName(int viewId, long id) {
    return "android:switcher:" + viewId + ":" + id;
}

viewIdViewPager 的 ID。
id 是 fragment 在 ViewPager 中的位置。

例如:

String fragmentTag = makeFragmentName(myViewPager.getId(), 2);
Fragment fragment = getSupportFragmentManager().findFragmentByTag(fragmentTag);

完美地工作了,谢谢!对于一些用户的提示:确保您使用正确的方法获取片段管理器。我使用了getChildFragmentManager - DIRTY DAVE

3
如果有人在使用FragmentStateAdapter,那么标签会有一点不同。这是来自FragmentStateAdapter的代码:
mFragmentManager.beginTransaction()
    .add(fragment, "f" + holder.getItemId())
    .setMaxLifecycle(fragment, STARTED)
    .commitNow();

正如您所看到的,这是静态字符串"f"getItemId()的组合。 在适配器中重写fun getItemId(position: Int): Long后,查找特定的片段应该很容易。例如,覆盖fun

getItemId(position: Int): Long {
    return if(position == 0) {
            "my_first_fragment"
    }else "my_second_fragment"
}

我们第一个碎片的标签将是"fmy_first_fragment",可以通过childFragmentManager.findFragmentByTag("fmy_first_fragment") as? MyFirstFragment找到它。
注意,每当寻找碎片时,必须使用与创建它时相同的FragmentManager。因此,我建议您使用FragmentManagerLifeCycle作为构造函数来创建适配器。

没必要这样做 if-else 判断。你可以创建自定义方法,然后只需调用:return "f" + super.getItemId(position);} - deadfish
每当你想调用你的方法时,你必须将适配器解析为 (ViewPager.adapter as? MyAdapter)?.myCustomMethod()。我不是很喜欢这种方式,但这只是个人偏好问题。我的帖子主要是关于它如何与 ViewPager2 一起使用,这与其他人的建议有些不同。感谢你的补充,这是一个有效的观点。请随意在我的帖子上提出修改意见,可以采用两种方法。 - Jakub Kostka

0

虽然这个问题已经有了答案,但我有另一种方法来实现它,这可能是更好的方法,因为代码中标记片段的方式可能会发生变化。此外,要指出的是,使用adapter.getItem(position)不会返回正在显示的片段实例,它将取决于我们自己实现的getItem方法。

所以基本上问题在于我们没有viewPager中存在的片段实例。想法是重写方法

public Object instantiateItem(ViewGroup container, int position) {
    // If we already have this item instantiated, there is nothing
    // to do.  This can happen when we are restoring the entire pager
    // from its saved state, where the fragment manager has already
    // taken care of restoring the fragments we previously had instantiated.
    if (mFragments.size() > position) {
        Fragment f = mFragments.get(position);
        if (f != null) {
            return f;
        }
    }

    if (mCurTransaction == null) {
        mCurTransaction = mFragmentManager.beginTransaction();
    }

    Fragment fragment = getItem(position);
    if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
    if (mSavedState.size() > position) {
        Fragment.SavedState fss = mSavedState.get(position);
        if (fss != null) {
            fragment.setInitialSavedState(fss);
        }
    }
    while (mFragments.size() <= position) {
        mFragments.add(null);
    }
    fragment.setMenuVisibility(false);
    fragment.setUserVisibleHint(false);
    mFragments.set(position, fragment);
    mCurTransaction.add(container.getId(), fragment);

    return fragment;
}

关于FragmentStatePagerAdapter的onCreate方法。如果我们查看此方法的默认实现,它基本上完成了实例化新片段(如果它们不存在)并返回它们的工作。同时,它将它们存储在一个私有的片段列表中。因此,我们可以重写此方法并将片段实例存储在我们自己的列表中。可以按照以下方式实现:

private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {

    private ArrayList<Fragment> fragmentList;

    private final int NUM_PAGES;

    public ScreenSlidePagerAdapter(FragmentManager fm, int numPages) {
        super(fm);
        NUM_PAGES = numPages;
        fragmentList = new ArrayList<>(NUM_PAGES);
    }

    @Override
    public Fragment getItem(int position) {
        Fragment fragment = new CustomFragment();
        //this method can be modified according to the requirement
        return fragment;
    }

    @Override
    public int getCount() {
        return NUM_PAGES;
    }

    public Fragment getFragment(int position){
        return fragmentList.get(position);
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position){
        Fragment fragment = (Fragment) super.instantiateItem(container,position);
        if(fragmentList.size()<NUM_PAGES&&!fragmentList.contains(fragment)){
            fragmentList.add(fragment);
        }
        return fragment;
    }
}

因此,我们将拥有所有片段的实例,不再需要任何标签来查找向用户显示的片段实例。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接