1aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn/* 2aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn * Copyright (C) 2011 The Android Open Source Project 3aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn * 4aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn * Licensed under the Apache License, Version 2.0 (the "License"); 5aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn * you may not use this file except in compliance with the License. 6aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn * You may obtain a copy of the License at 7aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn * 8aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn * http://www.apache.org/licenses/LICENSE-2.0 9aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn * 10aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn * Unless required by applicable law or agreed to in writing, software 11aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn * distributed under the License is distributed on an "AS IS" BASIS, 12aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn * See the License for the specific language governing permissions and 14aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn * limitations under the License. 15aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn */ 16aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackbornpackage com.example.android.supportv4.app; 17aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn 18aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackbornimport android.content.Context; 19aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackbornimport android.os.Bundle; 20aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackbornimport android.support.v4.app.Fragment; 21aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackbornimport android.support.v4.app.FragmentActivity; 22aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackbornimport android.support.v4.app.FragmentPagerAdapter; 23aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackbornimport android.support.v4.view.ViewPager; 24aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackbornimport android.view.View; 25865abe20dd1a639efe9a013240e61613a52b6648Dianne Hackbornimport android.view.ViewGroup; 26aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackbornimport android.widget.TabHost; 27865abe20dd1a639efe9a013240e61613a52b6648Dianne Hackbornimport android.widget.TabWidget; 28aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn 299c76e9550ada0475f1c750e5672128630d2a028cAurimas Liutikasimport com.example.android.supportv4.R; 309c76e9550ada0475f1c750e5672128630d2a028cAurimas Liutikas 31560d99a32ec737d928cbca48a0fc2235181fe922Adam Powellimport java.util.ArrayList; 32560d99a32ec737d928cbca48a0fc2235181fe922Adam Powell 33aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn/** 34aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn * Demonstrates combining a TabHost with a ViewPager to implement a tab UI 35aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn * that switches between tabs and also allows the user to perform horizontal 36aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn * flicks to move between the tabs. 37aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn */ 38aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackbornpublic class FragmentTabsPager extends FragmentActivity { 39aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn TabHost mTabHost; 40aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn ViewPager mViewPager; 41aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn TabsAdapter mTabsAdapter; 42aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn 43aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn @Override 44aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn protected void onCreate(Bundle savedInstanceState) { 45aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn super.onCreate(savedInstanceState); 46aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn 47aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn setContentView(R.layout.fragment_tabs_pager); 48aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn mTabHost = (TabHost)findViewById(android.R.id.tabhost); 49aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn mTabHost.setup(); 50aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn 51aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn mViewPager = (ViewPager)findViewById(R.id.pager); 52aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn 53aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn mTabsAdapter = new TabsAdapter(this, mTabHost, mViewPager); 54aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn 55aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn mTabsAdapter.addTab(mTabHost.newTabSpec("simple").setIndicator("Simple"), 56aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn FragmentStackSupport.CountingFragment.class, null); 57aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn mTabsAdapter.addTab(mTabHost.newTabSpec("contacts").setIndicator("Contacts"), 58aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn LoaderCursorSupport.CursorLoaderListFragment.class, null); 59aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn mTabsAdapter.addTab(mTabHost.newTabSpec("custom").setIndicator("Custom"), 60aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn LoaderCustomSupport.AppListFragment.class, null); 61aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn mTabsAdapter.addTab(mTabHost.newTabSpec("throttle").setIndicator("Throttle"), 62aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn LoaderThrottleSupport.ThrottledLoaderListFragment.class, null); 63aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn 64aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn if (savedInstanceState != null) { 65aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn mTabHost.setCurrentTabByTag(savedInstanceState.getString("tab")); 66aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn } 67aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn } 68aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn 69aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn @Override 70aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn protected void onSaveInstanceState(Bundle outState) { 71aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn super.onSaveInstanceState(outState); 72aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn outState.putString("tab", mTabHost.getCurrentTabTag()); 73aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn } 74aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn 75aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn /** 76aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn * This is a helper class that implements the management of tabs and all 77aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn * details of connecting a ViewPager with associated TabHost. It relies on a 78aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn * trick. Normally a tab host has a simple API for supplying a View or 79aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn * Intent that each tab will show. This is not sufficient for switching 80aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn * between pages. So instead we make the content part of the tab host 81aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn * 0dp high (it is not shown) and the TabsAdapter supplies its own dummy 82aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn * view to show as the tab content. It listens to changes in tabs, and takes 83aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn * care of switch to the correct paged in the ViewPager whenever the selected 84aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn * tab changes. 85aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn */ 86aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn public static class TabsAdapter extends FragmentPagerAdapter 87aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener { 88aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn private final Context mContext; 89aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn private final TabHost mTabHost; 90aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn private final ViewPager mViewPager; 91aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>(); 92aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn 93aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn static final class TabInfo { 94aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn private final String tag; 95aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn private final Class<?> clss; 96aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn private final Bundle args; 97aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn 98aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn TabInfo(String _tag, Class<?> _class, Bundle _args) { 99aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn tag = _tag; 100aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn clss = _class; 101aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn args = _args; 102aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn } 103aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn } 104aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn 105aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn static class DummyTabFactory implements TabHost.TabContentFactory { 106aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn private final Context mContext; 107aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn 108aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn public DummyTabFactory(Context context) { 109aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn mContext = context; 110aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn } 111aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn 112aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn @Override 113aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn public View createTabContent(String tag) { 114aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn View v = new View(mContext); 115aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn v.setMinimumWidth(0); 116aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn v.setMinimumHeight(0); 117aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn return v; 118aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn } 119aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn } 120aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn 121aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn public TabsAdapter(FragmentActivity activity, TabHost tabHost, ViewPager pager) { 122aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn super(activity.getSupportFragmentManager()); 123aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn mContext = activity; 124aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn mTabHost = tabHost; 125aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn mViewPager = pager; 126aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn mTabHost.setOnTabChangedListener(this); 127aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn mViewPager.setAdapter(this); 1289c76e9550ada0475f1c750e5672128630d2a028cAurimas Liutikas mViewPager.addOnPageChangeListener(this); 129aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn } 130aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn 131aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args) { 132aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn tabSpec.setContent(new DummyTabFactory(mContext)); 133aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn String tag = tabSpec.getTag(); 134aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn 135aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn TabInfo info = new TabInfo(tag, clss, args); 136aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn mTabs.add(info); 137aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn mTabHost.addTab(tabSpec); 138aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn notifyDataSetChanged(); 139aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn } 140aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn 141aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn @Override 142aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn public int getCount() { 143aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn return mTabs.size(); 144aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn } 145aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn 146aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn @Override 147aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn public Fragment getItem(int position) { 148aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn TabInfo info = mTabs.get(position); 149aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn return Fragment.instantiate(mContext, info.clss.getName(), info.args); 150aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn } 151aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn 152aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn @Override 153aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn public void onTabChanged(String tabId) { 154aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn int position = mTabHost.getCurrentTab(); 155aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn mViewPager.setCurrentItem(position); 156aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn } 157aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn 158aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn @Override 159aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 160aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn } 161aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn 162aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn @Override 163aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn public void onPageSelected(int position) { 164865abe20dd1a639efe9a013240e61613a52b6648Dianne Hackborn // Unfortunately when TabHost changes the current tab, it kindly 165865abe20dd1a639efe9a013240e61613a52b6648Dianne Hackborn // also takes care of putting focus on it when not in touch mode. 166865abe20dd1a639efe9a013240e61613a52b6648Dianne Hackborn // The jerk. 167865abe20dd1a639efe9a013240e61613a52b6648Dianne Hackborn // This hack tries to prevent this from pulling focus out of our 168865abe20dd1a639efe9a013240e61613a52b6648Dianne Hackborn // ViewPager. 169865abe20dd1a639efe9a013240e61613a52b6648Dianne Hackborn TabWidget widget = mTabHost.getTabWidget(); 170865abe20dd1a639efe9a013240e61613a52b6648Dianne Hackborn int oldFocusability = widget.getDescendantFocusability(); 171865abe20dd1a639efe9a013240e61613a52b6648Dianne Hackborn widget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); 172aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn mTabHost.setCurrentTab(position); 173865abe20dd1a639efe9a013240e61613a52b6648Dianne Hackborn widget.setDescendantFocusability(oldFocusability); 174aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn } 175560d99a32ec737d928cbca48a0fc2235181fe922Adam Powell 176560d99a32ec737d928cbca48a0fc2235181fe922Adam Powell @Override 177560d99a32ec737d928cbca48a0fc2235181fe922Adam Powell public void onPageScrollStateChanged(int state) { 178560d99a32ec737d928cbca48a0fc2235181fe922Adam Powell } 179aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn } 180aefd2819f5d9f972a6c6dc33a3c66d6d2891d4e8Dianne Hackborn} 181