Before you start, if you want a systematic view (abeit a long one) to creating lateral navigation, refer to the Android Developer patterns:
I will go through a step-by-step (condensed version of the various google dev docs pages) on how to implement 3 tabs + swipe using 3 fragments. 2 fragments have xml layout files, and one fragment is a Google MapFragment.
*Important* – All the packages that you will see below such as ViewPager, FragmentActivity etc are available natively to Android 3.0+ APIs, however Android has provided a support library that allow the creation of everything described here all the way down to Android 2.2. This is important, because if you are developing on a high API level you will see that many classes can be important from either native API, or from android.support.*. You will have to stick with one of them as they are not interchangeable.
Example below will be using android support, as this provide greater backward compatibility.
Step 1 – Set up the tabs on the main activity
In Android 3.0+, tabs can be built into the ActionBar on any application. The code below is all that is required:
private void setupABar() { // obtain the action bar final ActionBar aBar = getActionBar(); // set it to tab mode aBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); // add a tab listeners to handle changing the current fragment ActionBar.TabListener tabListener = new ActionBar.TabListener() { @Override public void onTabUnselected(Tab tab, FragmentTransaction ft) { } @Override public void onTabSelected(Tab tab, FragmentTransaction ft) { mViewPager.setCurrentItem(tab.getPosition()); } @Override public void onTabReselected(Tab tab, FragmentTransaction ft) { } }; // add the tabs, register the event handler for the tabs aBar.addTab(aBar.newTab().setText("Controls").setTabListener(tabListener)); aBar.addTab(aBar.newTab().setText("Map").setTabListener(tabListener)); aBar.addTab(aBar.newTab().setText("Stats").setTabListener(tabListener)); }
and in onCreate, call:
setupABar();
Most of the code here is self-explanatory, except for the mViewPager variable. This will become clear in the next section, just remember that the code above just sets up the tabs and registers a handler. It is rather detached from the whole fragment and swipe process.
Step 2 – Fragments
We now want to build 2 fragments (the 3rd Google Map fragment will be constructed in code at runtime). Here we will show the creation of only 1 fragment called ControlFragment (for the control panel of the app I was building):
ControlFragment.java:
public class ControlFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // inflat and return the layout return inflater.inflate(R.layout.controls_fragment, container, false); } }
and the layout:
controls_fragment.xml (simplified):
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_vertical|center_horizontal" android:orientation="vertical" > <Button android:id="@+id/btn_turnon" android:layout_width="146dp" android:layout_height="wrap_content" android:text="@string/controls_btn_turnon" /> <Button android:id="@+id/btn_turnoff" android:layout_width="146dp" android:layout_height="wrap_content" android:text="@string/controls_btn_turnoff" /> </LinearLayout>
Step 3 – ViewPager on the MainActivity and swipe
To support multiple views (called fragments), you generally:
- Add a ViewPager to the layout (through either the XML or by code), it is responsible for displaying different fragments
- If you need to control how the ViewPager functions such as disabling swiping on certain fragments, then you need to inherit the default ViewPager
- Register a bunch of fragments to the ViewPager
Step 2 shows the construction of list items 1 and 2. We are building a custom ViewPager as we want to disable scrolling later on a Google MapFragment (so that when you swipe on the map it doesnt jump to the next tab).
MainActivity.xml (Layout XML):
<com.jamesyxu.jwifivisualizer.ViewPagerMapNoScroll xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/pager" android:layout_width="match_parent" android:layout_height="match_parent" />
MainActivity.java:
vertCollectionPagerAdapter mVCollectionPageAdapter; ViewPagerMapNoScroll mViewPager; private void setupPager() { // the page adapter contains all the fragment registrations mVCollectionPageAdapter = new vertCollectionPagerAdapter(getSupportFragmentManager()); mViewPager = (ViewPagerMapNoScroll) findViewById(R.id.pager); // set the contents of the ViewPager mViewPager.setAdapter(mVCollectionPageAdapter); // add a on page change listener to change the actionBar's selected tab # (fragment will then be changed by actionBar's code) // the change listener is called on swiping mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { @Override public void onPageSelected(int pos) { getActionBar().setSelectedNavigationItem(pos); } } ); }
and in onCreate of MainActivity:
setupPager();
*Swiping* – We see that we can enable swiping very easily by implementing a ViewPager’s setOnPageChangeListener, this is triggered whenever a ViewPager detects enough side scrolling, and also has determined that the current fragment can be scrolled (see ViewPagerMapNoScroll.java:canScroll() below).
vertCollectionpagerAdapter.java:
public class vertCollectionPagerAdapter extends FragmentPagerAdapter { private static final int FRAGMENT_COUNT = 3; private List<Fragment> mFragments = new ArrayList<Fragment>(); // Google's Android MAP API 2 has MapFragment and the Android Support library's equivalent, SupportMapFragment private SupportMapFragment mMapFragment; private FragmentManager mFM; public boolean disableSwipe = false; public vertCollectionPagerAdapter(FragmentManager fm) { super(fm); mFM = fm; // add fragments mMapFragment = SupportMapFragment.newInstance(); mFragments.add(new ControlFragment()); mFragments.add(mMapFragment); mFragments.add(new StatsFragment()); } @Override public int getCount() { return FRAGMENT_COUNT; } // This is called by the ViewPager to get the fragment at tab position pos // ViewPager calls this when the Tab handler handles a tab change @Override public Fragment getItem(int pos) { Fragment f = mFragments.get(pos); // we want to disable swiping on the map fragment if (f instanceof SupportMapFragment) disableSwipe = true; else disableSwipe = false; return f; } public Fragment getActiveFragment(ViewPager container, int pos) { String name = "android:switcher:" + container.getId() + ":" + pos; return mFM.findFragmentByTag(name); } }
and ViewPagerMapNoScroll.java:
public class ViewPagerMapNoScroll extends ViewPager { public ViewPagerMapNoScroll(Context context) { super(context); } public ViewPagerMapNoScroll(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) { if (v instanceof ViewPagerMapNoScroll) { // the vertCollectionPagerAdapter has logic on getItem that determines if the next fragment should have swipe disabled vertCollectionPagerAdapter a = (vertCollectionPagerAdapter) ((ViewPagerMapNoScroll)v).getAdapter(); if (a.disableSwipe) return false; else return true; } return super.canScroll(v, checkV, dx, x, y); } }
This completes a really fast, code-centric tutorial on how to build Tab + Swipe using fragments and Android Support library.
As of now, the MapFragment would not work because that require additional setup, see the next blog post on that