Pager Tabs with Icon and text which appears to have fixed width.

We are going to create pager tabs, which unlike default library will be able to show more than 3 tabs in a screen. All the tabs will close to each other.


PagerTab
 
To achieve this purpose we shall need a library located at http://viewpagerindicator.com/. Download the library project, named as JakeWharton-Android-ViewPagerIndicator-2.4.1-0-g8cd549f.zip. It includes a library and sample project. We need library for our app. If you are curious, you may check the sample project as well. Import the library project in eclipse.
 
Create a new Android project and open its properties. Add library project in this project using Android section in properties. Clean library and android project. Add the following in your activity_layout.xml.

<com.viewpagerindicator.TabPageIndicator

                android:id="@+id/indicator"

                style="@style/CustomTabPageIndicator"

                android:layout_width="fill_parent"

                android:layout_height="30dp"

                 android:visibility="gone"/>

<android.support.v4.view.ViewPager

            xmlns:tools="http://schemas.android.com/tools"

            android:id="@+id/pager"

            android:layout_width="match_parent"

            android:layout_height="0dp"

            android:layout_weight="1"

            tools:context=".VMEBaseClass" >

        </android.support.v4.view.ViewPager>


Whereas style.xml will contain:

<style name="CustomTabPageIndicator" parent="Widget.TabPageIndicator">

        <!-- <item name="android:background">@drawable/custom_tab_indicator</item> -->

        <item name="android:textAppearance">@style/CustomTabPageIndicator.Text</item>

        <item name="android:textColor">#cccccc</item>

        <item name="android:textSize">12sp</item>

        <!-- <item name="android:divider">@drawable/vertical_line</item> -->

        <!-- @drawable/custom_tab_indicator_divider</item> -->

        <!-- <item name="android:dividerPadding">10dp</item>

        <item name="android:showDividers">middle</item> -->

        <item name="android:paddingLeft">0dp</item>

        <item name="android:paddingRight">0dp</item>

        <item name="android:fadingEdge">horizontal</item>

        <item name="android:paddingTop">1dp</item>

        <item name="android:paddingBottom">0dp</item>

        <item name="android:fadingEdgeLength">8dp</item>

        <item name="android:layout_marginTop">5dp</item>

        <item name="android:layout_marginBottom">5dp</item>

    </style>


In your FragmentActivity's onCreate(), add the following code.

BasePagerAdapter mSectionsPagerAdapter = new BasePagerAdapter( getSupportFragmentManager( ) );

        

        // Set up the ViewPager with the sections adapter.

        mViewPager = (ViewPager) findViewById( R.id.pager );

        mViewPager.setAdapter( mSectionsPagerAdapter );

        

        TabPageIndicator indicator = (TabPageIndicator) findViewById( R.id.indicator );

        indicator.setVisibility( View.VISIBLE );

        indicator.setViewPager( mViewPager );


Implement the IconPagerAdapter for showing icons and background in both normal and selected states. In this sample we will support 4 pages.

public class BasePagerAdapter extends FragmentPagerAdapter implements IconPagerAdapter {

        

        public VMEBasePagerAdapter( FragmentManager fm ) {

            super( fm );

        }

        

        @Override

        public Fragment getItem( int position ) {

            // getItem is called to instantiate the fragment for the given page.

                       

            if ( position == 0 ) {

                Fragment fragment = new DownloadFragment();

                return fragment;

            } else if ( position == 1 ) {

                Fragment fragment = new FavoriteFragment( favoriteData);

                return fragment;

            } else if ( position == 2 ) {

                Fragment fragment = new FlashFragment();

                return fragment;

            } else if ( position == 3 ) {

                Fragment fragment = null;

                fragment = new DummyFragment();

                return fragment;

            } else {

                return null;

            }

        }

        

        @Override

        public int getCount( ) {

//Return the count of pages.

            return 4;

        }

        

        @Override

        public CharSequence getPageTitle( int position ) {

            switch ( position ) {

                case 0:

                    return getString( R.string.title_download );

                    

                case 1:

                    return getString( R.string.title_favorite );

                    

                case 2:

                    return getString( R.string.title_flash );

                    

                case 3:

                    return getString( R.string.title_dummy );

            }

            return null;

        }

        

        @Override

        public int getIconResIdNormal( int index ) {

switch ( index ) {

                case 0:

                    return R.drawable.ic_normal_download;

                    

                case 1:

                    return R.drawable.ic_normal_favorite;

                    

                case 2:

                    return R.drawable.ic_normal_flash;

                    

                case 3:

                    return R.drawable.ic_normal_dummy;

            }

            return 0;

        }

        

        @Override

        public int getIconResIdSelected( int index ) {

            switch ( index ) {

                case 0:

                    return R.drawable.ic_selected_download;

                    

                case 1:

                    return R.drawable.ic_selected_favorite;

                    

                case 2:

                    return R.drawable.ic_selected_flash;

                    

                case 3:

                    return R.drawable.ic_selected_dummy;

            }

            return 0;

        }

        

        @Override

        public int getBackgroundResIdSelected( int index ) {

            switch ( index ) {

                case 0:

                    return R.drawable.tab_left_pressed;

                case 1:

                case 2:

                    return R.drawable.tab_center_pressed;

                case 3:

                    return R.drawable.tab_right_pressed;

            }

            return 0;

        }

        

        @Override

        public int getBackgroundResIdNormal( int index ) {

            switch ( index ) {

                case 0:

                    return R.drawable.tab_left_normal;

                case 1:

                case 2:

                    return R.drawable.tab_center_normal;

                case 3:

                    return R.drawable.tab_right_normal;

            }

            return 0;

        }

    }


Now its time to modify the library project's TabPageIndicator.java like this.

public class TabPageIndicator extends HorizontalScrollView implements PageIndicator {
    /** Title text used when no title is provided by the adapter. */
    private static final CharSequence EMPTY_TITLE = "";
    //private Vector< TabView > tabViews = new Vector< TabPageIndicator.TabView >( );
    
    /**
     * Interface for a callback when the selected tab has been reselected.
     */
    public interface OnTabReselectedListener {
        /**
         * Callback when the selected tab has been reselected.
         * 
         * @param position
         *            Position of the current center item.
         */
        void onTabReselected( int position );
    }
    
    private Runnable mTabSelector;
    
    private final OnClickListener mTabClickListener = new OnClickListener( ) {
        public void onClick( View view ) {
            //TabView tabView = (TabView) view;
            final int oldSelected = mViewPager.getCurrentItem( );
            final int newSelected = (Integer)view.getTag( );
            mViewPager.setCurrentItem( newSelected );
            if ( oldSelected == newSelected && mTabReselectedListener != null ) {
                mTabReselectedListener.onTabReselected( newSelected );
            }
        }
    };
    
    private final IcsLinearLayout mTabLayout;
    
    private ViewPager mViewPager;
    private ViewPager.OnPageChangeListener mListener;
    
    private int mMaxTabWidth;
    private int mSelectedTabIndex;
    
    private OnTabReselectedListener mTabReselectedListener;
    
    public TabPageIndicator( Context context ) {
        this( context, null );
    }
    
    public TabPageIndicator( Context context, AttributeSet attrs ) {
        super( context, attrs );
        setHorizontalScrollBarEnabled( false );
        
        mTabLayout = new IcsLinearLayout( context, R.attr.vpiTabPageIndicatorStyle );
        addView( mTabLayout, new ViewGroup.LayoutParams( WRAP_CONTENT, MATCH_PARENT ) );
    }
    
    public void setOnTabReselectedListener( OnTabReselectedListener listener ) {
        mTabReselectedListener = listener;
    }
    
    @Override
    public void onMeasure( int widthMeasureSpec, int heightMeasureSpec ) {
        final int widthMode = MeasureSpec.getMode( widthMeasureSpec );
        final boolean lockedExpanded = widthMode == MeasureSpec.EXACTLY;
        setFillViewport( lockedExpanded );
        
        final int childCount = mTabLayout.getChildCount( );
        if ( childCount > 1 && ( widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST ) ) {
            if ( childCount > 2 ) {
                mMaxTabWidth = (int) ( MeasureSpec.getSize( widthMeasureSpec ) * 0.4f );
            } else {
                mMaxTabWidth = MeasureSpec.getSize( widthMeasureSpec ) / 2;
            }
        } else {
            mMaxTabWidth = -1;
        }
        
        final int oldWidth = getMeasuredWidth( );
        super.onMeasure( widthMeasureSpec, heightMeasureSpec );
        final int newWidth = getMeasuredWidth( );
        
        if ( lockedExpanded && oldWidth != newWidth ) {
            // Recenter the tab display if we're at a new (scrollable) size.
            setCurrentItem( mSelectedTabIndex );
        }
    }
    
    private void animateToTab( final int position ) {
        final View tabView = mTabLayout.getChildAt( position );
        if ( mTabSelector != null ) {
            removeCallbacks( mTabSelector );
        }
        mTabSelector = new Runnable( ) {
            public void run( ) {
                final int scrollPos = tabView.getLeft( ) - ( getWidth( ) - tabView.getWidth( ) ) / 2;
                smoothScrollTo( scrollPos, 0 );
                mTabSelector = null;
            }
        };
        post( mTabSelector );
    }
    
    @Override
    public void onAttachedToWindow( ) {
        super.onAttachedToWindow( );
        if ( mTabSelector != null ) {
            // Re-post the selector we saved
            post( mTabSelector );
        }
    }
    
    @Override
    public void onDetachedFromWindow( ) {
        super.onDetachedFromWindow( );
        if ( mTabSelector != null ) {
            removeCallbacks( mTabSelector );
        }
    }
    
    private void addTab( int index, CharSequence text, int iconResId ) {
        LayoutInflater inflater = (LayoutInflater)getContext( ).getSystemService( Context.LAYOUT_INFLATER_SERVICE );
        View view = inflater.inflate( R.layout.tab_layout, null);
        ImageView imageView = (ImageView)view.findViewById( R.id.ivIcon );
        TextView textView = (TextView)view.findViewById( R.id.tvTabText );
        imageView.setImageResource( iconResId );
        textView.setText( text );
        view.setOnClickListener( mTabClickListener );
        view.setTag( index );
        mTabLayout.addView( view, new LinearLayout.LayoutParams( 0, MATCH_PARENT, 1 ) );
        
        /*final TabView tabView = new TabView( getContext( ) );
        tabView.mIndex = index;
        tabView.setFocusable( true );
        tabView.setOnClickListener( mTabClickListener );
        tabView.setText( text );
        
        if ( iconResId != 0 ) {
            tabView.setCompoundDrawablesWithIntrinsicBounds( iconResId, 0, 0, 0 );
        }
        tabViews.add( tabView );
        tabView.setCompoundDrawablePadding (-30 );
        mTabLayout.addView( tabView, new LinearLayout.LayoutParams( 0, MATCH_PARENT, 1 ) );*/
    }
    
    /*public TabView getTabAt( int position ) {
        if ( tabViews != null && tabViews.size( ) > position )
            return tabViews.get( position );
        return null;
    }*/
    
    @Override
    public void onPageScrollStateChanged( int arg0 ) {
        if ( mListener != null ) {
            mListener.onPageScrollStateChanged( arg0 );
        }
    }
    
    @Override
    public void onPageScrolled( int arg0, float arg1, int arg2 ) {
        if ( mListener != null ) {
            mListener.onPageScrolled( arg0, arg1, arg2 );
        }
    }
    
    @Override
    public void onPageSelected( int arg0 ) {
        setCurrentItem( arg0 );
        if ( mListener != null ) {
            mListener.onPageSelected( arg0 );
        }
    }
    
    @Override
    public void setViewPager( ViewPager view ) {
        if ( mViewPager == view ) {
            return;
        }
        if ( mViewPager != null ) {
            mViewPager.setOnPageChangeListener( null );
        }
        final PagerAdapter adapter = view.getAdapter( );
        if ( adapter == null ) {
            throw new IllegalStateException( "ViewPager does not have adapter instance." );
        }
        mViewPager = view;
        view.setOnPageChangeListener( this );
        notifyDataSetChanged( );
    }
    
    public void notifyDataSetChanged( ) {
        mTabLayout.removeAllViews( );
        PagerAdapter adapter = mViewPager.getAdapter( );
        IconPagerAdapter iconAdapter = null;
        if ( adapter instanceof IconPagerAdapter ) {
            iconAdapter = (IconPagerAdapter) adapter;
        }
        final int count = adapter.getCount( );
        for ( int i = 0; i < count; i++ ) {
            CharSequence title = adapter.getPageTitle( i );
            if ( title == null ) {
                title = EMPTY_TITLE;
            }
            int iconResId = 0;
            if ( iconAdapter != null ) {
                iconResId = iconAdapter.getIconResIdNormal( i );
            }
            addTab( i, title, iconResId );
        }
        if ( mSelectedTabIndex > count ) {
            mSelectedTabIndex = count - 1;
        }
        setCurrentItem( mSelectedTabIndex );
        requestLayout( );
    }
    
    @Override
    public void setViewPager( ViewPager view, int initialPosition ) {
        setViewPager( view );
        setCurrentItem( initialPosition );
    }
    
    @Override
    public void setCurrentItem( int item ) {
        if ( mViewPager == null ) {
            throw new IllegalStateException( "ViewPager has not been bound." );
        }
        mSelectedTabIndex = item;
        mViewPager.setCurrentItem( item );
        
        final int tabCount = mTabLayout.getChildCount( );
        for ( int i = 0; i < tabCount; i++ ) {
            final LinearLayout child = (LinearLayout) mTabLayout.getChildAt( i );
            //final TabView child = (TabView) mTabLayout.getChildAt( i );
            final boolean isSelected = ( i == item );
            child.setSelected( isSelected );
            PagerAdapter adapter = mViewPager.getAdapter( );
            IconPagerAdapter iconAdapter = null;
            if ( adapter instanceof IconPagerAdapter ) {
                iconAdapter = (IconPagerAdapter) adapter;
            }
            int iconResId;
            int bgResId;
            if ( iconAdapter != null ) {
                if ( isSelected ) {
                    iconResId = iconAdapter.getIconResIdSelected( i );
                    bgResId = iconAdapter.getBackgroundResIdSelected( i );
                } else {
                    iconResId = iconAdapter.getIconResIdNormal( i );
                    bgResId = iconAdapter.getBackgroundResIdNormal( i );
                }
                
                if ( bgResId != 0 )
                    child.setBackgroundResource( bgResId );
                
                ImageView iv = (ImageView)child.findViewById( R.id.ivIcon );
                if ( iconResId != 0 ) {
                    iv.setVisibility( View.VISIBLE );
                    iv.setImageResource( iconResId );
                }else{
                    iv.setVisibility( View.GONE );
                }
            }
            
            if ( isSelected ) {
                animateToTab( item );
            }
        }
    }
    
    @Override
    public void setOnPageChangeListener( OnPageChangeListener listener ) {
        mListener = listener;
    }    
}


Finally update library's  IconPagerAdapter .java

public interface IconPagerAdapter {
    /**
     * Get icon representing the page at {@code index} in the adapter.
     */
    int getIconResIdNormal(int index);
    /**
     * Get icon representing the page at {@code index} in the adapter This will be used when the tab is selected.
     */
    int getIconResIdSelected(int index);
    // From PagerAdapter
    int getCount();
    /**
     * Get background representing the page at {@code index} in the adapter This will be used when the tab is selected.
     */
    int getBackgroundResIdSelected(int index);
    /**
     * Get background representing the page at {@code index} in the adapter..
     */
    int getBackgroundResIdNormal(int index);
}

Comments

Popular posts from this blog

Curious case of MissingMethodInvocationException and Mockito.when

Building a Retrieval-Augmented Generation (RAG) Application with Ollama 3.2 and Spring Boot

Android aar deployment in Maven - 2022