Implementing a tabhost is a great way to add functionality to your app. Sadly, tabhost forces the user to click on the tabs to switch between screens. But thankfully we are programmers and can make our programs do what we want them to do. In this tutorial, Ill show you how to implement a GestureDetector to handle the left and right swipes from user to switch between tabs. Lets start with our XML layout of the main window:
main.xml
<?xml version="1.0" encoding="utf-8"?> <TabHost xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/tabhost" android:layout_width="fill_parent" android:layout_height="fill_parent" > <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" android:padding="5dp" > <TabWidget android:id="@android:id/tabs" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <FrameLayout android:id="@android:id/tabcontent" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="4" android:padding="5dp" > <com.daish.viewtest.TestListView android:id="@+id/custom_list" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </FrameLayout> </LinearLayout> </TabHost>
Next we have our main activity. I created a method called switchTabs(boolean direction) to tell tabhost which direction to switch tabs. I also implemented a listener on tabchanged. Once a tab is changed, the listener is fired and I then change the content on listview.
import android.app.TabActivity; import android.os.Bundle; import android.widget.TabHost; import android.widget.TabHost.OnTabChangeListener; public class ViewTestActivity extends TabActivity { private TestListView testListView = null; private TabHost tabHost = null; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); tabHost = getTabHost(); // The activity TabHost TabHost.TabSpec spec; // Resusable TabSpec for each tab spec = tabHost.newTabSpec("Tab 1 Tag").setIndicator("Tab 1") .setContent(R.id.custom_list); tabHost.addTab(spec); testListView = (TestListView) findViewById(R.id.custom_list); spec = tabHost.newTabSpec("Tab 2 Tag").setIndicator("Tab 2") .setContent(R.id.custom_list); tabHost.addTab(spec); //Done to make list update itself on a tab change tabHost.setOnTabChangedListener(new TabChangeListener()); tabHost.setCurrentTab(1); } public void switchTabs(boolean direction) { if (direction) // true = move left { if (tabHost.getCurrentTab() == 0) tabHost.setCurrentTab(tabHost.getTabWidget().getTabCount() - 1); else tabHost.setCurrentTab(tabHost.getCurrentTab() - 1); } else // move right { if (tabHost.getCurrentTab() != (tabHost.getTabWidget().getTabCount() - 1)) tabHost.setCurrentTab(tabHost.getCurrentTab() + 1); else tabHost.setCurrentTab(0); } } private class TabChangeListener implements OnTabChangeListener { @Override public void onTabChanged(String tabId) { testListView.init(tabHost.getCurrentTab()); } } }
Next we have the heart of the content; the listview. The goal is to allow the user to swipe left and right and our program would switch tabs. Although, we have to make sure we did not remove the functionality of clicking on an item and scrolling up and down on list. On my first attempt, I made an @Override onTouchEvent(MotionEvent event) and I tried to handle the swipes. The problem my implementation had was if the user swiped, the item on the next tab would automatically click after user lifted their finger. As such, I had to find an alternative. This is where GestureDetector came in. This class gave me the ability to analyze the touch event. I kept the original @Override onTouchEvent(MotionEvent event), except I passed the event to the GestureDetector and let the class handle the left and right swipes. If the GestureDetector analysis of the touch event appeared to be anything other than left or right swipes, let ListView handle it (ex. scroll up and down as well as clicks). The result was the functionality I was looking for.
import android.app.AlertDialog; import android.content.Context; import android.util.AttributeSet; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import android.view.GestureDetector.OnGestureListener; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ListView; public class TestListView extends ListView { Context context; private ArrayAdapter<String> test; String[] testItems = {"Larry", "John", "Gary","Larry", "John", "Gary","Larry", "John", "Gary","Larry", "John", "Gary","Larry", "John", "Gary","Larry", "John", "Gary"}; String[] anotherTestItems = {"Homer", "Leslie", "Gary","Beaver","Homer", "Leslie", "Gary","Beaver","Homer", "Leslie", "Gary","Beaver","Homer", "Leslie", "Gary","Beaver"}; //If built programmatically public TestListView(Context context) { super(context); this.context = context; } //This example uses this method since being built from XML public TestListView(Context context, AttributeSet attrs) { super(context, attrs); this.context = context; } //Build from XML layout public TestListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); this.context = context; } public void init(int i) { if(i == 0) test = new ArrayAdapter<String>(getContext(),R.layout.row, R.id.label , testItems); else test = new ArrayAdapter<String>(getContext(),R.layout.row, R.id.label , anotherTestItems); setAdapter(test); setOnItemClickListener(new ListSelection()); } //GestureDetector used to for analyze touch even from user private GestureDetector gesture = new GestureDetector(new GestureListener()); //Method is called when ListView detects an onTouchEvent @Override public boolean onTouchEvent(MotionEvent event) {//Sends event to GestureDetector to see if we can use event boolean result = gesture.onTouchEvent(event); //if result is true, then we need to handle event, not listview //false means listview can handle event to pass it on if(result) return result; return super.onTouchEvent(event); } private float startX= 0; private float endX = 0; //Class which will analyze touch event and determine what user did private class GestureListener implements OnGestureListener { @Override public boolean onDown(MotionEvent e) { return false; } @Override public void onShowPress(MotionEvent e) { } @Override public boolean onSingleTapUp(MotionEvent e) { return false; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { return false; } @Override public void onLongPress(MotionEvent e) { } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { float x = velocityX < 0 ? -1*velocityX : velocityX; float y = velocityY < 0 ? -1*velocityY : velocityY; if(x > y) { System.out.println("Moving" + " " + (startX - endX)); startX = e1.getX(); endX = e2.getX(); if ((startX - endX) < -120) { ((ViewTestActivity) context).switchTabs(true); } else if ((startX - endX) > 120) { ((ViewTestActivity) context).switchTabs(false); } return true; } return false; } } private class ListSelection implements OnItemClickListener { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); builder.setMessage("You pressed item #" + (position+1)); builder.setPositiveButton("OK", null); builder.show(); } } }
Hope this was helpful. Enjoy!