ActionBarSherlock + Tabs + 多个 Fragments?

17
我已经努力尝试使actionbarsherlock+标签+片段(fragments)能够正常工作。
我只能将其设置为静态,但我希望能像安卓市场应用程序一样创建可滑动的标签页。
当需要填充具有多个片段的布局时,我陷入了困境。
在Support4demos中,我使用FragmentsTabsPager作为示例进行跟随。

如果你遇到了问题,你需要发布代码和 LogCat(如果有异常被抛出的话)。如果你想要在 Fragments 之间划动,你需要使用 ViewPager - adneal
https://github.com/JakeWharton/ActionBarSherlock/blob/master/samples/fragments/src/com/actionbarsherlock/sample/fragments/FragmentTabsPager.java - Thomas Dignan
3个回答

38

我实际上成功地实现了这个功能,只使用了ABS库和支持库。以下是我的代码:

public class ActionBarTabs extends SherlockFragmentActivity {
CustomViewPager mViewPager;
TabsAdapter mTabsAdapter;
TextView tabCenter;
TextView tabText;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN);

    mViewPager = new CustomViewPager(this);
    mViewPager.setId(R.id.pager);

    setContentView(mViewPager);
    ActionBar bar = getSupportActionBar();
    bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    bar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);

    mTabsAdapter = new TabsAdapter(this, mViewPager);

    mTabsAdapter.addTab(bar.newTab().setText("Home"),
            ToolKitFragment.class, null);
    mTabsAdapter.addTab(bar.newTab().setText("FujiSan"),
            FujiFragment.class, null);
}

public static class TabsAdapter extends FragmentPagerAdapter implements
        ActionBar.TabListener, ViewPager.OnPageChangeListener {
    private final Context mContext;
    private final ActionBar mActionBar;
    private final ViewPager mViewPager;
    private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();

    static final class TabInfo {
        private final Class<?> clss;
        private final Bundle args;

        TabInfo(Class<?> _class, Bundle _args) {
            clss = _class;
            args = _args;
        }
    }

    public TabsAdapter(SherlockFragmentActivity activity, ViewPager pager) {
        super(activity.getSupportFragmentManager());
        mContext = activity;
        mActionBar = activity.getSupportActionBar();
        mViewPager = pager;
        mViewPager.setAdapter(this);
        mViewPager.setOnPageChangeListener(this);
    }

    public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args) {
        TabInfo info = new TabInfo(clss, args);
        tab.setTag(info);
        tab.setTabListener(this);
        mTabs.add(info);
        mActionBar.addTab(tab);
        notifyDataSetChanged();
    }

    @Override
    public int getCount() {
        return mTabs.size();
    }

    @Override
    public Fragment getItem(int position) {
        TabInfo info = mTabs.get(position);
        return Fragment.instantiate(mContext, info.clss.getName(),
                info.args);
    }

    @Override
    public void onPageScrolled(int position, float positionOffset,
            int positionOffsetPixels) {
    }

    @Override
    public void onPageSelected(int position) {
        mActionBar.setSelectedNavigationItem(position);
    }

    @Override
    public void onPageScrollStateChanged(int state) {
    }

    @Override
    public void onTabSelected(Tab tab, FragmentTransaction ft) {
        Object tag = tab.getTag();
        for (int i = 0; i < mTabs.size(); i++) {
            if (mTabs.get(i) == tag) {
                mViewPager.setCurrentItem(i);
            }
        }
    }

    @Override
    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
    }

    @Override
    public void onTabReselected(Tab tab, FragmentTransaction ft) {
    }
}
}

您可以按照以下方式加载标签页内容。

mTabsAdapter.addTab(bar.newTab().setText("Home"),
            YOURFRAGMENTHERE.class, null);  
这将通过使用ABS、支持库和片段,为您提供如你所说的可爱的“滑动选项卡”效果。ABS使这几乎与使用本机库相同。我实际上是直接从Google的分页选项卡示例中复制了此代码,并对其进行了微小的修改以适应ABS。
希望对您有所帮助!

1
我能理解你的问题,但我的问题在于fragment类,它总是崩溃,你能否分享或提供一种方法来创建这个包含2个或更多片段的fragment类?谢谢。 - Marckaraujo
太棒了,谢谢。这比ABS自带的示例要好得多,因为它提供了具有可爱的新ICS标签的片段,而ABS示例在使用片段时似乎会恢复到旧标签。 - Steven Elliott
2
谢谢提供示例代码,这正是我所需要的!不过有一件事我好像没太明白,"CustomViewPager" 有什么用呢?它似乎和默认的 ViewPager 一样能够正常工作。 - Glenn85
1
老兄,你真是个救命恩人。我一直在苦苦挣扎,试图让ViewPager、ABS和Swipe正常工作。谢谢! - Tony
希望我能多谢你一些,之前我用了一个旧版本的tabhost来实现类似的功能,结果在2.x设备上样式非常糟糕。这个新方法真是节省了我很多时间。谢谢! - Mike P.

10
你需要正确的库来实现你想要的功能。
基本上,你需要一个ViewPager库。这是我的建议:
1. ActionbarSherlock
它非常容易使用,我就不多作解释了。
2. ViewPagerExtensions
你可以在这里找到它。下载ZIP文件并从中创建一个新项目。
实现来自该项目的com.astuetz.viewpager.extensions.SwipeyTabsView。有易于跟随的示例,它恰好符合您的要求。甚至有其他选项卡样式可供选择(包括与ICS一起提供的新的People选项卡)。此外,很容易将其样式设置为与您的应用程序相匹配。
3. FragmentPagerAdapter 最后,您可以使用支持库(v4)中的该类。
祝你好运,如果需要更多帮助,请随时问我。
不需要覆盖FragmentPagerAdapter中的instantiateItem方法,如果您正在使用我建议的内容。 您只需要:
1. 提供一个构造函数,并调用super实现;
2. 重写getCount以返回pager中片段的数量,以及
3. getItem,这是您用于返回要创建的片段的方法。
以下是我这里的代码示例(完整的工作生产示例)。 它是实现pager的activity的内部类:
static class MyFragmentPagerAdapter extends FragmentPagerAdapter {

    public MyFragmentPagerAdapter(FragmentManager fm) {
        super(fm);
    }

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

    @Override
    public Fragment getItem(int position) {
        Fragment f;
        switch(position) {
        case 0:
            f= new ItemSalesDataFragment();
            break;
        case 1:
            f= new DepartmentChooserFragment();
            break;
        default:
            throw new IllegalArgumentException("not this many fragments: " + position);
        }
        return f;
    }
}

正如您所看到的,此分页程序处理两个“页面”,左侧显示销售数据,右侧允许用户选择部门。这些在getItem方法中返回。可以使用适当的片段(可以使用您已经拥有的任何片段,不需要修改)。

按预期,您需要将所有内容组合在一起,方法是1)创建一个实例化MyFragmentPagerAdapter的对象,并2)将适配器设置为您刚刚实例化的类的ViewPager对象。正如您所看到的,它的getItem方法将向分页程序“提供”您需要的所有片段。例如:

mFragmentPagerAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager());
mViewPager.setAdapter(mFragmentPagerAdapter);

当然,还有其他事情要做,比如创建选项卡按钮并将所有这些连接起来以进行交叉通信。我建议查看从GitHub下载的ViewPagerExtensions ZIP文件中提供的示例。这只是回答您评论中的问题。正如您所看到的,使用这个库的代码并不多。


我推荐使用ViewPagerExtensions库,因为我喜欢它生成的代码,而且它提供了几种不同的选项卡类型可供选择,除此之外还非常容易进行样式定制。如果你发现Rymnel方法在功能上有所限制,一定要试试这个库。 - davidcesarino
谢谢,这真的很有帮助。现在我需要学习如何为每个位置内的两个布局充气:Object instantiateItem(View container,int position),你有任何教程吗?谢谢。 - Marckaraujo
更新答案以展示如何使用FragmentpagerAdapter返回所需的片段。如果您需要任何其他帮助,请随时提问。 - davidcesarino
我在想:我们如何将这个与布局支持演示相结合?因为我有一个选项卡,我也想实现它... - Jeroen
@Jeroen,不确定您的意思。如果您能更具体地说出哪个布局,我可以帮助您。否则,您可以始终提出更一般的问题来解决您的问题。 - davidcesarino
你好David,感谢你的回复。我会先看一下,如果遇到任何问题,我会让你知道(正如你所说,我会创建一个新的问题 :))。 - Jeroen

3
大多数示例在切换回以前选择的选项卡时通常会抛出异常(IllegalStateException:指定的子项已经有父项。您必须先在子项的父项上调用removeView())。
我喜欢使用FragmentStatePagerAdapter来适应Google的示例,而不是使用普通的FragmentPagerAdapter,它会为您替换片段并解决此错误。通常,它将销毁它认为可以删除以节省空间的片段; 如果您希望始终保留片段,则覆盖destroyItem(ViewGroup、int、Object),并使用空块。
以下是一个示例:
public class ActionBarTabs extends SherlockFragmentActivity {
    CustomViewPager mViewPager;
    TabsAdapter mTabsAdapter;
    TextView tabCenter;
    TextView tabText;
    ActionBar mActionBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN);

        mViewPager = new CustomViewPager(this);
        mViewPager.setId(R.id.pager);

        setContentView(mViewPager);
        mActionBar = getSupportActionBar();
        mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
        mActionBar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);

        mTabsAdapter = new TabsAdapter(this, mViewPager);

        mTabsAdapter.addTab(mActionBar.newTab().setText("Page 1"), 
            YOURFRAGMENT_A.class, null);
        mTabsAdapter.addTab(mActionBar.newTab().setText("Page 2"), 
            YOURFRAGMENT_B.class, null);
        mTabsAdapter.addTab(mActionBar.newTab().setText("Page 3"), 
            YOURFRAGMENT_C.class, null);
    }

    public static class TabsAdapter extends FragmentPagerAdapter
            implements ActionBar.TabListener, ViewPager.OnPageChangeListener {
        private final Context mContext;
        private final ActionBar mActionBar;
        private final ViewPager mViewPager;
        private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();

        static final class TabInfo {
            private final Class<?> clss;
            private final Bundle args;

            TabInfo(Class<?> _class, Bundle _args) {
                clss = _class;
                args = _args;
            }
        }

        public TabsAdapter(SherlockFragmentActivity activity, ViewPager pager) {
            super(activity.getSupportFragmentManager());
            mContext = activity;
            mActionBar = activity.getSupportActionBar();
            mViewPager = pager;
            mViewPager.setAdapter(this);
            mViewPager.setOnPageChangeListener(this);
        }

        public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args) {
            TabInfo info = new TabInfo(clss, args);
            tab.setTag(info);
            tab.setTabListener(this);
            mTabs.add(info);
            mActionBar.addTab(tab);
            notifyDataSetChanged();
        }

        @Override
        public int getCount() {
            return mTabs.size();
        }

        @Override
        public Fragment getItem(int position) {
            TabInfo info = mTabs.get(position);
            return Fragment.instantiate(mContext,
                info.clss.getName(), info.args);
        }

        @Override
        public void onPageScrolled(int position, float positionOffset,
            int positionOffsetPixels) {
        }

        @Override
        public void onPageSelected(int position) {
            mActionBar.setSelectedNavigationItem(position);
        }

        @Override
        public void onPageScrollStateChanged(int state) {
        }

        @Override
        public void onTabSelected(Tab tab, FragmentTransaction ft) {
            Object tag = tab.getTag();
            for (int i = 0; i < mTabs.size(); i++) {
                if (mTabs.get(i) == tag) {
                    mViewPager.setCurrentItem(i);
                }
            }
        }

        @Override
        public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        }

        @Override
        public void onTabReselected(Tab tab, FragmentTransaction ft) {
        }
    }
}

这个功能与ABS兼容性良好,实现起来相对简单。


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