Set the current page of both the ViewPager and indicator.
+ * + *This must be used if you need to set the page before + * the views are drawn on screen (e.g., default start page).
+ * + * @param item + */ + public void setCurrentItem(int item); + + /** + * Set a page change listener which will receive forwarded events. + * + * @param listener + */ + public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener); + + /** + * Notify the indicator that the fragment list has changed. + */ + public void notifyDataSetChanged(); +} diff --git a/libs/ViewPagerIndicator/src/com/viewpagerindicator/TabPageIndicator.java b/libs/ViewPagerIndicator/src/com/viewpagerindicator/TabPageIndicator.java new file mode 100644 index 0000000..9fba053 --- /dev/null +++ b/libs/ViewPagerIndicator/src/com/viewpagerindicator/TabPageIndicator.java @@ -0,0 +1,246 @@ +/* + * 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.viewpagerindicator; + +import android.content.Context; +import android.support.v4.view.PagerAdapter; +import android.support.v4.view.ViewPager; +import android.support.v4.view.ViewPager.OnPageChangeListener; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.HorizontalScrollView; +import android.widget.LinearLayout; +import android.widget.TextView; + +/** + * This widget implements the dynamic action bar tab behavior that can change + * across different configurations or circumstances. + */ +public class TabPageIndicator extends HorizontalScrollView implements PageIndicator { + Runnable mTabSelector; + + private OnClickListener mTabClickListener = new OnClickListener() { + public void onClick(View view) { + TabView tabView = (TabView)view; + mViewPager.setCurrentItem(tabView.getIndex()); + } + }; + + private LinearLayout mTabLayout; + private ViewPager mViewPager; + private ViewPager.OnPageChangeListener mListener; + + private LayoutInflater mInflater; + + 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.FILL_PARENT)); + } + + @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(String text, int index) { + //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(mTabClickListener); + + mTabLayout.addView(tabView, new LinearLayout.LayoutParams(0, LayoutParams.FILL_PARENT, 1)); + } + + @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) { + 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(); + } + + public void notifyDataSetChanged() { + mTabLayout.removeAllViews(); + TitleProvider adapter = (TitleProvider)mViewPager.getAdapter(); + final int count = ((PagerAdapter)adapter).getCount(); + for (int i = 0; i < count; i++) { + addTab(adapter.getTitle(i), i); + } + 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; + final int tabCount = mTabLayout.getChildCount(); + for (int i = 0; i < tabCount; i++) { + final View child = mTabLayout.getChildAt(i); + final boolean isSelected = (i == item); + child.setSelected(isSelected); + if (isSelected) { + animateToTab(item); + } + } + } + + @Override + public void setOnPageChangeListener(OnPageChangeListener listener) { + mListener = listener; + } + + 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); + } + + @Override + public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + // 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); + } + } + + public int getIndex() { + return mIndex; + } + } +} diff --git a/libs/ViewPagerIndicator/src/com/viewpagerindicator/TitlePageIndicator.java b/libs/ViewPagerIndicator/src/com/viewpagerindicator/TitlePageIndicator.java new file mode 100644 index 0000000..b4eb8ad --- /dev/null +++ b/libs/ViewPagerIndicator/src/com/viewpagerindicator/TitlePageIndicator.java @@ -0,0 +1,776 @@ +/* + * Copyright (C) 2011 Jake Wharton + * Copyright (C) 2011 Patrik Akerfeldt + * Copyright (C) 2011 Francisco Figueiredo Jr. + * + * 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.viewpagerindicator; + +import java.util.ArrayList; +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.RectF; +import android.graphics.Typeface; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.v4.view.MotionEventCompat; +import android.support.v4.view.PagerAdapter; +import android.support.v4.view.ViewConfigurationCompat; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; + +/** + * A TitlePageIndicator is a PageIndicator which displays the title of left view + * (if exist), the title of the current select view (centered) and the title of + * the right view (if exist). When the user scrolls the ViewPager then titles are + * also scrolled. + */ +public class TitlePageIndicator extends View implements PageIndicator { + /** + * Percentage indicating what percentage of the screen width away from + * center should the underline be fully faded. A value of 0.25 means that + * halfway between the center of the screen and an edge. + */ + private static final float SELECTION_FADE_PERCENTAGE = 0.25f; + + /** + * Percentage indicating what percentage of the screen width away from + * center should the selected text bold turn off. A value of 0.05 means + * that 10% between the center and an edge. + */ + private static final float BOLD_FADE_PERCENTAGE = 0.05f; + + /** + * Interface for a callback when the center item has been clicked. + */ + public static interface OnCenterItemClickListener { + /** + * Callback when the center item has been clicked. + * + * @param position Position of the current center item. + */ + public void onCenterItemClick(int position); + } + + public enum IndicatorStyle { + None(0), Triangle(1), Underline(2); + + public final int value; + + private IndicatorStyle(int value) { + this.value = value; + } + + public static IndicatorStyle fromValue(int value) { + for (IndicatorStyle style : IndicatorStyle.values()) { + if (style.value == value) { + return style; + } + } + return null; + } + } + + private ViewPager mViewPager; + private ViewPager.OnPageChangeListener mListener; + private TitleProvider mTitleProvider; + private int mCurrentPage; + private int mCurrentOffset; + private int mScrollState; + private final Paint mPaintText = new Paint(); + private boolean mBoldText; + private int mColorText; + private int mColorSelected; + private Path mPath; + private final Paint mPaintFooterLine = new Paint(); + private IndicatorStyle mFooterIndicatorStyle; + private final Paint mPaintFooterIndicator = new Paint(); + private float mFooterIndicatorHeight; + private float mFooterIndicatorUnderlinePadding; + private float mFooterPadding; + private float mTitlePadding; + private float mTopPadding; + /** Left and right side padding for not active view titles. */ + private float mClipPadding; + private float mFooterLineHeight; + + private static final int INVALID_POINTER = -1; + + private int mTouchSlop; + private float mLastMotionX = -1; + private int mActivePointerId = INVALID_POINTER; + private boolean mIsDragging; + + private OnCenterItemClickListener mCenterItemClickListener; + + + public TitlePageIndicator(Context context) { + this(context, null); + } + + public TitlePageIndicator(Context context, AttributeSet attrs) { + this(context, attrs, R.attr.vpiTitlePageIndicatorStyle); + } + + public TitlePageIndicator(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + //Load defaults from resources + final Resources res = getResources(); + final int defaultFooterColor = res.getColor(R.color.default_title_indicator_footer_color); + final float defaultFooterLineHeight = res.getDimension(R.dimen.default_title_indicator_footer_line_height); + final int defaultFooterIndicatorStyle = res.getInteger(R.integer.default_title_indicator_footer_indicator_style); + final float defaultFooterIndicatorHeight = res.getDimension(R.dimen.default_title_indicator_footer_indicator_height); + final float defaultFooterIndicatorUnderlinePadding = res.getDimension(R.dimen.default_title_indicator_footer_indicator_underline_padding); + final float defaultFooterPadding = res.getDimension(R.dimen.default_title_indicator_footer_padding); + final int defaultSelectedColor = res.getColor(R.color.default_title_indicator_selected_color); + final boolean defaultSelectedBold = res.getBoolean(R.bool.default_title_indicator_selected_bold); + final int defaultTextColor = res.getColor(R.color.default_title_indicator_text_color); + final float defaultTextSize = res.getDimension(R.dimen.default_title_indicator_text_size); + final float defaultTitlePadding = res.getDimension(R.dimen.default_title_indicator_title_padding); + final float defaultClipPadding = res.getDimension(R.dimen.default_title_indicator_clip_padding); + final float defaultTopPadding = res.getDimension(R.dimen.default_title_indicator_top_padding); + + //Retrieve styles attributes + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TitlePageIndicator, defStyle, R.style.Widget_TitlePageIndicator); + + //Retrieve the colors to be used for this view and apply them. + mFooterLineHeight = a.getDimension(R.styleable.TitlePageIndicator_footerLineHeight, defaultFooterLineHeight); + mFooterIndicatorStyle = IndicatorStyle.fromValue(a.getInteger(R.styleable.TitlePageIndicator_footerIndicatorStyle, defaultFooterIndicatorStyle)); + mFooterIndicatorHeight = a.getDimension(R.styleable.TitlePageIndicator_footerIndicatorHeight, defaultFooterIndicatorHeight); + mFooterIndicatorUnderlinePadding = a.getDimension(R.styleable.TitlePageIndicator_footerIndicatorUnderlinePadding, defaultFooterIndicatorUnderlinePadding); + mFooterPadding = a.getDimension(R.styleable.TitlePageIndicator_footerPadding, defaultFooterPadding); + mTopPadding = a.getDimension(R.styleable.TitlePageIndicator_topPadding, defaultTopPadding); + mTitlePadding = a.getDimension(R.styleable.TitlePageIndicator_titlePadding, defaultTitlePadding); + mClipPadding = a.getDimension(R.styleable.TitlePageIndicator_clipPadding, defaultClipPadding); + mColorSelected = a.getColor(R.styleable.TitlePageIndicator_selectedColor, defaultSelectedColor); + mColorText = a.getColor(R.styleable.TitlePageIndicator_textColor, defaultTextColor); + mBoldText = a.getBoolean(R.styleable.TitlePageIndicator_selectedBold, defaultSelectedBold); + + final float textSize = a.getDimension(R.styleable.TitlePageIndicator_textSize, defaultTextSize); + final int footerColor = a.getColor(R.styleable.TitlePageIndicator_footerColor, defaultFooterColor); + mPaintText.setTextSize(textSize); + mPaintText.setAntiAlias(true); + mPaintFooterLine.setStyle(Paint.Style.FILL_AND_STROKE); + mPaintFooterLine.setStrokeWidth(mFooterLineHeight); + mPaintFooterLine.setColor(footerColor); + mPaintFooterIndicator.setStyle(Paint.Style.FILL_AND_STROKE); + mPaintFooterIndicator.setColor(footerColor); + + a.recycle(); + + final ViewConfiguration configuration = ViewConfiguration.get(context); + mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration); + } + + + public int getFooterColor() { + return mPaintFooterLine.getColor(); + } + + public void setFooterColor(int footerColor) { + mPaintFooterLine.setColor(footerColor); + mPaintFooterIndicator.setColor(footerColor); + invalidate(); + } + + public float getFooterLineHeight() { + return mFooterLineHeight; + } + + public void setFooterLineHeight(float footerLineHeight) { + mFooterLineHeight = footerLineHeight; + mPaintFooterLine.setStrokeWidth(mFooterLineHeight); + invalidate(); + } + + public float getFooterIndicatorHeight() { + return mFooterIndicatorHeight; + } + + public void setFooterIndicatorHeight(float footerTriangleHeight) { + mFooterIndicatorHeight = footerTriangleHeight; + invalidate(); + } + + public float getFooterIndicatorPadding() { + return mFooterPadding; + } + + public void setFooterIndicatorPadding(float footerIndicatorPadding) { + mFooterPadding = footerIndicatorPadding; + invalidate(); + } + + public IndicatorStyle getFooterIndicatorStyle() { + return mFooterIndicatorStyle; + } + + public void setFooterIndicatorStyle(IndicatorStyle indicatorStyle) { + mFooterIndicatorStyle = indicatorStyle; + invalidate(); + } + + public int getSelectedColor() { + return mColorSelected; + } + + public void setSelectedColor(int selectedColor) { + mColorSelected = selectedColor; + invalidate(); + } + + public boolean isSelectedBold() { + return mBoldText; + } + + public void setSelectedBold(boolean selectedBold) { + mBoldText = selectedBold; + invalidate(); + } + + public int getTextColor() { + return mColorText; + } + + public void setTextColor(int textColor) { + mPaintText.setColor(textColor); + mColorText = textColor; + invalidate(); + } + + public float getTextSize() { + return mPaintText.getTextSize(); + } + + public void setTextSize(float textSize) { + mPaintText.setTextSize(textSize); + invalidate(); + } + + public float getTitlePadding() { + return this.mTitlePadding; + } + + public void setTitlePadding(float titlePadding) { + mTitlePadding = titlePadding; + invalidate(); + } + + public float getTopPadding() { + return this.mTopPadding; + } + + public void setTopPadding(float topPadding) { + mTopPadding = topPadding; + invalidate(); + } + + public float getClipPadding() { + return this.mClipPadding; + } + + public void setClipPadding(float clipPadding) { + mClipPadding = clipPadding; + invalidate(); + } + + public void setTypeface(Typeface typeface) { + mPaintText.setTypeface(typeface); + invalidate(); + } + + public Typeface getTypeface() { + return mPaintText.getTypeface(); + } + + /* + * (non-Javadoc) + * + * @see android.view.View#onDraw(android.graphics.Canvas) + */ + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (mViewPager == null) { + return; + } + final int count = mViewPager.getAdapter().getCount(); + if (count == 0) { + return; + } + + //Calculate views bounds + ArrayList