Tag Archives: fragments

Android Layout Finder online tool

Hi guys, hope you had a great new years!

If you’ve ever developed an Android app, you’ve probably used the findViewById() method for finding views and findFragmentById() for finding fragments from your inflated layout. This has to be done with every View and Fragment that you want to access from your code.

After getting tired of writing the same code over and over again, I created a tool to generate that code for me from an arbitrary XML layout:

Behold: The Android Layout Finder!

You can try it out on a sample XML layout by pressing the ‘Paste example XML’ button in the right side of the screen.

Android Layout Finder is open source and licensed under the GNU General Public License. The code can be found on GitHub: http://www.github.com/jesperborgstrup/android-layout-finder. Enjoy!

Tab swipe between fragments using ActionBarSherlock

Introduction

First post in my new Android blog, so I thought I should start out with something lightweight, yet pretty cool: Swiping between action bar tabs using ActionBarSherlock.

The Android Developer tools include a wizard to make this for the native action bar, but this requires your project to break backwards compatibility for devices that aren’t updated to API 14 (Ice Cream Sandwich).

This easily extendable snippet of code allows you to implement swiping between tabs on all devices in any project that uses ActionBarSherlock.

The custom activity to inherit from

Instead of letting your activity inherit from SherlockActivity or any of the likes, now let your activity inherit from the following TabSwipeActivity:

TabSwipeActivity.java (on GitHub):

import java.util.ArrayList;
import java.util.List;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.ViewPager;

import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.ActionBar.Tab;
import com.actionbarsherlock.app.ActionBar.TabListener;
import com.actionbarsherlock.app.SherlockFragmentActivity;

public abstract class TabSwipeActivity extends SherlockFragmentActivity {

    private ViewPager mViewPager;
    private TabsAdapter adapter;

    @Override
    public void onCreate(Bundle savedInstanceState) {
    	/*
    	 * Create the ViewPager and our custom adapter
    	 */
        mViewPager = new ViewPager(this);
        adapter = new TabsAdapter( this, mViewPager );
        mViewPager.setAdapter( adapter );
        mViewPager.setOnPageChangeListener( adapter );

        /*
         * We need to provide an ID for the ViewPager, otherwise we will get an exception like:
         *
         * java.lang.IllegalArgumentException: No view found for id 0xffffffff for fragment TestFragment{40de5b90 #0 id=0xffffffff android:switcher:-1:0}
		 * at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:864)
		 *
		 * The ID 0x7F04FFF0 is large enough to probably never be used for anything else
         */
        mViewPager.setId( 0x7F04FFF0 );

        super.onCreate(savedInstanceState);

        /*
         * Set the ViewPager as the content view
         */
        setContentView(mViewPager);
    }

    /**
     * Add a tab with a backing Fragment to the action bar
     * @param titleRes A string resource pointing to the title for the tab
     * @param fragmentClass The class of the Fragment to instantiate for this tab
     * @param args An optional Bundle to pass along to the Fragment (may be null)
     */
    protected void addTab(int titleRes, Class fragmentClass, Bundle args ) {
        adapter.addTab( getString( titleRes ), fragmentClass, args );
    }
    /**
     * Add a tab with a backing Fragment to the action bar
     * @param titleRes A string to be used as the title for the tab
     * @param fragmentClass The class of the Fragment to instantiate for this tab
     * @param args An optional Bundle to pass along to the Fragment (may be null)
     */
    protected void addTab(CharSequence title, Class fragmentClass, Bundle args ) {
        adapter.addTab( title, fragmentClass, args );
    }

    private static class TabsAdapter extends FragmentPagerAdapter implements TabListener, ViewPager.OnPageChangeListener {

        private final SherlockFragmentActivity mActivity;
        private final ActionBar mActionBar;
        private final ViewPager mPager;

        /**
         * @param fm
         * @param fragments
         */
        public TabsAdapter(SherlockFragmentActivity activity, ViewPager pager) {
            super(activity.getSupportFragmentManager());
            this.mActivity = activity;
            this.mActionBar = activity.getSupportActionBar();
            this.mPager = pager;

            mActionBar.setNavigationMode( ActionBar.NAVIGATION_MODE_TABS );
        }

        private static class TabInfo {
            public final Class fragmentClass;
            public final Bundle args;
            public TabInfo(Class fragmentClass,
                    Bundle args) {
                this.fragmentClass = fragmentClass;
                this.args = args;
            }
        }

        private List mTabs = new ArrayList();

        public void addTab( CharSequence title, Class fragmentClass, Bundle args ) {
            final TabInfo tabInfo = new TabInfo( fragmentClass, args );

            Tab tab = mActionBar.newTab();
            tab.setText( title );
            tab.setTabListener( this );
            tab.setTag( tabInfo );

            mTabs.add( tabInfo );

            mActionBar.addTab( tab );
            notifyDataSetChanged();
        }

        @Override
        public Fragment getItem(int position) {
            final TabInfo tabInfo = mTabs.get( position );
            return (Fragment) Fragment.instantiate( mActivity, tabInfo.fragmentClass.getName(), tabInfo.args );
        }

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

        public void onPageScrollStateChanged(int arg0) {
        }

        public void onPageScrolled(int arg0, float arg1, int arg2) {
        }

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

        public void onTabSelected(Tab tab, FragmentTransaction ft) {
            TabInfo tabInfo = (TabInfo) tab.getTag();
            for ( int i = 0; i < mTabs.size(); i++ ) {
                if ( mTabs.get( i ) == tabInfo ) {
                    mPager.setCurrentItem( i );
                }
            }
        }

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

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

Using TabSwipeActivity

The only extra method(s) that TabSwipeActivity supports are the addTab() methods:

void addTab(int titleRes, Class fragmentClass, Bundle args );
void addTab(CharSequence title, Class fragmentClass, Bundle args );

Each of these will add a tab and assign a fragment to that tab. Only difference between the two is that the first accepts a string resource ID for the tab title, and the second accepts any CharSequence.

Example usage

TabSwipeTestActivity.java:

import android.os.Bundle;

public class TabSwipeTestActivity extends TabSwipeActivity {

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		addTab( "Tab 1", TestFragment.class, TestFragment.createBundle( "Fragment 1") );
		addTab( "Tab 2", TestFragment.class, TestFragment.createBundle( "Fragment 2") );
		addTab( "Tab 3", TestFragment.class, TestFragment.createBundle( "Fragment 3") );
	}

}

Note that your activity shouldn’t call setContentView(). TabSwipeActivity does that for you!

TestFragment.java:

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class TestFragment extends Fragment {

	public static final String EXTRA_TITLE = "title";

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		TextView txt = new TextView( inflater.getContext() );
		txt.setGravity( Gravity.CENTER );
		txt.setText( "Fragment" );

		if ( getArguments() != null && getArguments().containsKey( EXTRA_TITLE ) ) {
			txt.setText( getArguments().getString( EXTRA_TITLE ) );
		}
		return txt;
	}

	public static Bundle createBundle( String title ) {
		Bundle bundle = new Bundle();
		bundle.putString( EXTRA_TITLE, title );
		return bundle;
	}

}