com.resonos.apps.library.tabviewpager.TabPageIndicator.java Source code

Java tutorial

Introduction

Here is the source code for com.resonos.apps.library.tabviewpager.TabPageIndicator.java

Source

/*
 * Copyright (C) 2011 The Android Open Source Project
 * Copyright (C) 2011 Jake Wharton
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.resonos.apps.library.tabviewpager;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.text.Layout.Alignment;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.resonos.app.library.R;

/**
 * This is a modified version of a class from Jake Wharton's ViewPagerIndicator.
 * 
 * This widget implements the dynamic action bar tab behavior that can change
 * across different configurations or circumstances.
 */
public class TabPageIndicator extends HorizontalScrollView implements PageIndicator, OnClickListener {

    // constants
    private static final int MAX_TAB_WIDTH = 800;

    // objects
    private LinearLayout mTabLayout;
    private ViewPager mViewPager;
    protected ViewPager.OnPageChangeListener mListener, mListener2 = null;
    private LayoutInflater mInflater;
    private Runnable mTabSelector;
    private TextPaint mTextPaint;

    // vars
    private int mTextPadding = 0, mTotalMinWidth = 0, mTargetWidth = 0;
    private int[] mAllMinWidths;
    private boolean mRescaleTabs = false;
    private int mMaxTabWidth;
    private int mSelectedTabIndex;

    public TabPageIndicator(Context context) {
        this(context, null);
    }

    public TabPageIndicator(Context context, AttributeSet attrs) {
        super(context, attrs);
        setHorizontalScrollBarEnabled(false);

        mInflater = LayoutInflater.from(context);

        mTabLayout = new LinearLayout(getContext());
        addView(mTabLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.MATCH_PARENT));
    }

    @Override
    public void onClick(View view) {
        if (view instanceof TabView) {
            TabView tabView = (TabView) view;
            setCurrentView(tabView.getIndex());
        }
    }

    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mViewPager == null) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            return;
        }
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        final boolean lockedExpanded = widthMode == MeasureSpec.EXACTLY;

        mTargetWidth = MeasureSpec.getSize(widthMeasureSpec);
        mRescaleTabs = mTotalMinWidth < mTargetWidth;
        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 = (int) (MeasureSpec.getSize(widthMeasureSpec) * 0.55f);
            }
        } 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.
            setCurrentItemImpl(mSelectedTabIndex); // bypass any override
        }
    }

    /**
     * Return the tab at a given position
     * @param position : the tab index
     * @return a {@link TabView} object, or null if not found
     */
    public TabView getTabAt(int position) {
        return (TabView) mTabLayout.getChildAt(position);
    }

    /**
     * Animate to a given tab position and select it
     * @param position : the tab index
     */
    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);
        }
    }

    /**
     * Add a tab to the indicator
     */
    private void addTab(String text, Drawable drawable, int index, boolean visible) {
        //Workaround for not being able to pass a defStyle on pre-3.0
        final TabView tabView = (TabView) mInflater.inflate(R.layout.vpi__tab, null);
        tabView.init(this, text, index);
        tabView.setFocusable(true);
        tabView.setOnClickListener(this);
        if (drawable != null)
            tabView.setIcon(drawable);
        tabView.setVisibility(visible ? View.VISIBLE : View.GONE);

        mTabLayout.addView(tabView, new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1));
    }

    @Override
    public void onPageScrollStateChanged(int arg0) {
        if (mListener != null)
            mListener.onPageScrollStateChanged(arg0);
        if (mListener2 != null)
            mListener2.onPageScrollStateChanged(arg0);
    }

    @Override
    public void onPageScrolled(int arg0, float arg1, int arg2) {
        if (mListener != null)
            mListener.onPageScrolled(arg0, arg1, arg2);
        if (mListener2 != null)
            mListener2.onPageScrolled(arg0, arg1, arg2);
    }

    @Override
    public void onPageSelected(int view) {
        setCurrentItem(view);
        if (mListener != null)
            mListener.onPageSelected(view);
        if (mListener2 != null)
            mListener2.onPageSelected(view);
    }

    @Override
    public void setViewPager(ViewPager view) {
        final PagerAdapter adapter = view.getAdapter();
        if (adapter == null) {
            throw new IllegalStateException("ViewPager does not have adapter instance.");
        }
        if (!(adapter instanceof TitleProvider)) {
            throw new IllegalStateException(
                    "ViewPager adapter must implement TitleProvider to be used with TitlePageIndicator.");
        }
        mViewPager = view;
        view.setOnPageChangeListener(this);
        notifyDataSetChanged();
    }

    /**
     * This is a great way to set up subcategories for a "sub-tab bar"
     *  using the hidden tabs feature of {@link TabViewPagerFragment}
     * @param view : the view pager
     * @param parentTPI : the master tab page indicator, that will likely
     *    have some hidden tabs
     */
    public void setViewPager(ViewPager view, TabPageIndicator parentTPI) {
        final PagerAdapter adapter = view.getAdapter();
        if (adapter == null) {
            throw new IllegalStateException("ViewPager does not have adapter instance.");
        }
        mViewPager = view;
        mListener2 = parentTPI;
        view.setOnPageChangeListener(this);
        notifyDataSetChanged();
        setCurrentItem(mViewPager.getCurrentItem());
    }

    @Override
    public void notifyDataSetChanged() {
        mTabLayout.removeAllViews();

        // for measuring
        doPreMeasure();

        TitleProvider adapter = getTitleProvider();
        final int count = adapter.getCount();
        for (int i = 0; i < count; i++) {
            addTab(adapter.getTitle(i), adapter.getIcon(i), i, adapter.isVisible(i));
        }
        if (mSelectedTabIndex > count) {
            mSelectedTabIndex = count - 1;
        }
        setCurrentItemImpl(mSelectedTabIndex);
        requestLayout();
    }

    /**
     * Get the {@link TitleProvider} for this tab indicator
     */
    protected TitleProvider getTitleProvider() {
        return (TitleProvider) mViewPager.getAdapter();
    }

    /**
     * Does a bit of calculations to better measure the size of tabs.
     *  This is to fix a bug where text would occasionally get cut off.
     */
    private void doPreMeasure() {
        TabView tabView = (TabView) mInflater.inflate(R.layout.vpi__tab, null);
        TextView textView = (TextView) tabView.findViewById(android.R.id.text1);
        ImageView imageView = (ImageView) tabView.findViewById(R.id.img);
        mTextPaint = textView.getPaint();
        mTextPadding = textView.getTotalPaddingLeft() + textView.getTotalPaddingRight() + tabView.getPaddingLeft()
                + tabView.getPaddingRight();
        int mImagePadding = imageView.getPaddingLeft() + imageView.getPaddingRight();

        int t = 0;
        int count = getTitleProvider().getCount();
        mAllMinWidths = new int[count];
        String text;
        Drawable d;
        for (int i = 0; i < count; i++) {
            d = getTitleProvider().getIcon(i);
            text = getTitleProvider().getTitle(i);
            if (d != null) {
                mAllMinWidths[i] = d.getIntrinsicWidth() + mImagePadding;
            } else {
                StaticLayout layout = new StaticLayout(text, mTextPaint, MAX_TAB_WIDTH, Alignment.ALIGN_NORMAL,
                        1.0f, 0.0f, false);
                int w = (int) (layout.getLineWidth(0) + 1);
                mAllMinWidths[i] = mTextPadding + w;
            }
            if (getTitleProvider().isVisible(i))
                t += mAllMinWidths[i];
        }
        mTotalMinWidth = t;
    }

    @Override
    public void setViewPager(ViewPager view, int tab) {
        setViewPager(view);
        setCurrentItem(tab);
    }

    @Override
    public void setCurrentItem(int item) {
        setCurrentItemImpl(item);
    }

    /**
     * Method to set the current item that cannot be overridden
     * @param item
     */
    private final void setCurrentItemImpl(int item) {
        if (mViewPager == null) {
            throw new IllegalStateException("ViewPager has not been bound.");
        }
        mSelectedTabIndex = item;
        final int tabCount = mTabLayout.getChildCount();
        View lastVisibleChild = null;
        int lastVisibleItem = -1;
        for (int i = 0; i < tabCount; i++) {
            final View child = mTabLayout.getChildAt(i);
            if (child.getVisibility() == View.VISIBLE) {
                lastVisibleChild = child;
                lastVisibleItem = i;
            }
            final boolean isSelected = (i == item);
            if (lastVisibleChild != null) {
                if (lastVisibleItem == i || isSelected) {
                    lastVisibleChild.setSelected(isSelected);
                    if (isSelected)
                        animateToTab(lastVisibleItem);
                }
            }
        }
    }

    /**
     * Method called to change the viewpager's pager from the tab indicator,
     *  exposed to allow overriding
     * @param view : the page index
     */
    protected void setCurrentView(int view) {
        mViewPager.setCurrentItem(view);
    }

    @Override
    public void setOnPageChangeListener(OnPageChangeListener listener) {
        mListener = listener;
    }

    /**
     * A short and simple class to represent a tab
     */
    public static class TabView extends LinearLayout {
        private TabPageIndicator mParent;
        private int mIndex;

        public TabView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }

        public void init(TabPageIndicator parent, String text, int index) {
            mParent = parent;
            mIndex = index;

            TextView textView = (TextView) findViewById(android.R.id.text1);
            textView.setText(text);
            textView.setVisibility(View.VISIBLE);

            ImageView imageView = (ImageView) findViewById(R.id.img);
            imageView.setVisibility(View.GONE);
        }

        public void setIcon(Drawable drawable) {
            TextView textView = (TextView) findViewById(android.R.id.text1);
            textView.setVisibility(View.GONE);

            ImageView imageView = (ImageView) findViewById(R.id.img);
            imageView.setImageDrawable(drawable);
            imageView.setVisibility(View.VISIBLE);
        }

        public void setText(CharSequence txt) {
            TextView textView = (TextView) findViewById(android.R.id.text1);
            textView.setVisibility(View.VISIBLE);
            textView.setText(txt);
        }

        @Override
        public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);

            int minWidth = mParent.mRescaleTabs ? (int) FloatMath.ceil(
                    ((float) mParent.mAllMinWidths[mIndex] / (float) mParent.mTotalMinWidth * mParent.mTargetWidth))
                    : mParent.mAllMinWidths[mIndex];

            // Re-measure if we went beyond our maximum size.
            if (mParent.mMaxTabWidth > 0 && getMeasuredWidth() > mParent.mMaxTabWidth) {
                super.onMeasure(MeasureSpec.makeMeasureSpec(mParent.mMaxTabWidth, MeasureSpec.EXACTLY),
                        heightMeasureSpec);
            } else if (getMeasuredWidth() < minWidth) {
                super.onMeasure(MeasureSpec.makeMeasureSpec(minWidth, MeasureSpec.EXACTLY), heightMeasureSpec);
            }
        }

        public int getIndex() {
            return mIndex;
        }
    }
}