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