# HG changeset patch # User Da Risk # Date 1368610503 -7200 # Node ID ae7bea3238644d3caf6a9c4770f6c7c338afdd47 # Parent a1f3a20c87f8b610c17f1a03a01dbf491d0a54de Add Android PagerSlidingTabStrip library, an Interactive paging indicator widget This library can be found at https://github.com/astuetz/PagerSlidingTabStrip/ and we currently use the v1.0 tag diff -r a1f3a20c87f8 -r ae7bea323864 res/drawable/background_tab.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/res/drawable/background_tab.xml Wed May 15 11:35:03 2013 +0200 @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff -r a1f3a20c87f8 -r ae7bea323864 res/values/attrs_pagersliding.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/res/values/attrs_pagersliding.xml Wed May 15 11:35:03 2013 +0200 @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff -r a1f3a20c87f8 -r ae7bea323864 res/values/colors_pagersliding.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/res/values/colors_pagersliding.xml Wed May 15 11:35:03 2013 +0200 @@ -0,0 +1,5 @@ + + + + #6633B5E5 + diff -r a1f3a20c87f8 -r ae7bea323864 src/com/astuetz/viewpager/extensions/PagerSlidingTabStrip.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/astuetz/viewpager/extensions/PagerSlidingTabStrip.java Wed May 15 11:35:03 2013 +0200 @@ -0,0 +1,618 @@ +/* + * Copyright (C) 2013 Andreas Stuetz + * + * 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.astuetz.viewpager.extensions; + +import java.util.Locale; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.Typeface; +import android.os.Build; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.v4.view.ViewPager; +import android.support.v4.view.ViewPager.OnPageChangeListener; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.view.ViewTreeObserver.OnGlobalLayoutListener; +import android.widget.HorizontalScrollView; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.beem.project.beem.R; + +public class PagerSlidingTabStrip extends HorizontalScrollView { + + public interface IconTabProvider { + public int getPageIconResId(int position); + } + + // @formatter:off + private static final int[] ATTRS = new int[] { + android.R.attr.textSize, + android.R.attr.textColor + }; + // @formatter:on + + private LinearLayout.LayoutParams defaultTabLayoutParams; + private LinearLayout.LayoutParams expandedTabLayoutParams; + + private final PageListener pageListener = new PageListener(); + public OnPageChangeListener delegatePageListener; + + private LinearLayout tabsContainer; + private ViewPager pager; + + private int tabCount; + + private int currentPosition = 0; + private float currentPositionOffset = 0f; + + private Paint rectPaint; + private Paint dividerPaint; + + private boolean checkedTabWidths = false; + + private int indicatorColor = 0xFF666666; + private int underlineColor = 0x1A000000; + private int dividerColor = 0x1A000000; + + private boolean shouldExpand = false; + private boolean textAllCaps = true; + + private int scrollOffset = 52; + private int indicatorHeight = 8; + private int underlineHeight = 2; + private int dividerPadding = 12; + private int tabPadding = 24; + private int dividerWidth = 1; + + private int tabTextSize = 12; + private int tabTextColor = 0xFF666666; + private Typeface tabTypeface = null; + private int tabTypefaceStyle = Typeface.BOLD; + + private int lastScrollX = 0; + + private int tabBackgroundResId = R.drawable.background_tab; + + private Locale locale; + + public PagerSlidingTabStrip(Context context) { + this(context, null); + } + + public PagerSlidingTabStrip(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public PagerSlidingTabStrip(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + setFillViewport(true); + setWillNotDraw(false); + + tabsContainer = new LinearLayout(context); + tabsContainer.setOrientation(LinearLayout.HORIZONTAL); + tabsContainer.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); + addView(tabsContainer); + + DisplayMetrics dm = getResources().getDisplayMetrics(); + + scrollOffset = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, scrollOffset, dm); + indicatorHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, indicatorHeight, dm); + underlineHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, underlineHeight, dm); + dividerPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dividerPadding, dm); + tabPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, tabPadding, dm); + dividerWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dividerWidth, dm); + tabTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, tabTextSize, dm); + + // get system attrs (android:textSize and android:textColor) + + TypedArray a = context.obtainStyledAttributes(attrs, ATTRS); + + tabTextSize = a.getDimensionPixelSize(0, tabTextSize); + tabTextColor = a.getColor(1, tabTextColor); + + a.recycle(); + + // get custom attrs + + a = context.obtainStyledAttributes(attrs, R.styleable.PagerSlidingTabStrip); + + indicatorColor = a.getColor(R.styleable.PagerSlidingTabStrip_indicatorColor, indicatorColor); + underlineColor = a.getColor(R.styleable.PagerSlidingTabStrip_underlineColor, underlineColor); + dividerColor = a.getColor(R.styleable.PagerSlidingTabStrip_dividerColor, dividerColor); + indicatorHeight = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_indicatorHeight, indicatorHeight); + underlineHeight = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_underlineHeight, underlineHeight); + dividerPadding = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_dividerPadding, dividerPadding); + tabPadding = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_tabPaddingLeftRight, tabPadding); + tabBackgroundResId = a.getResourceId(R.styleable.PagerSlidingTabStrip_tabBackground, tabBackgroundResId); + shouldExpand = a.getBoolean(R.styleable.PagerSlidingTabStrip_shouldExpand, shouldExpand); + scrollOffset = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_scrollOffset, scrollOffset); + textAllCaps = a.getBoolean(R.styleable.PagerSlidingTabStrip_textAllCaps, textAllCaps); + + a.recycle(); + + rectPaint = new Paint(); + rectPaint.setAntiAlias(true); + rectPaint.setStyle(Style.FILL); + + dividerPaint = new Paint(); + dividerPaint.setAntiAlias(true); + dividerPaint.setStrokeWidth(dividerWidth); + + defaultTabLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); + expandedTabLayoutParams = new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f); + + if (locale == null) { + locale = getResources().getConfiguration().locale; + } + } + + public void setViewPager(ViewPager pager) { + this.pager = pager; + + if (pager.getAdapter() == null) { + throw new IllegalStateException("ViewPager does not have adapter instance."); + } + + pager.setOnPageChangeListener(pageListener); + + notifyDataSetChanged(); + } + + public void setOnPageChangeListener(OnPageChangeListener listener) { + this.delegatePageListener = listener; + } + + public void notifyDataSetChanged() { + + tabsContainer.removeAllViews(); + + tabCount = pager.getAdapter().getCount(); + + for (int i = 0; i < tabCount; i++) { + + if (pager.getAdapter() instanceof IconTabProvider) { + addIconTab(i, ((IconTabProvider) pager.getAdapter()).getPageIconResId(i)); + } else { + addTextTab(i, pager.getAdapter().getPageTitle(i).toString()); + } + + } + + updateTabStyles(); + + checkedTabWidths = false; + + getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { + + @SuppressWarnings("deprecation") + @SuppressLint("NewApi") + @Override + public void onGlobalLayout() { + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { + getViewTreeObserver().removeGlobalOnLayoutListener(this); + } else { + getViewTreeObserver().removeOnGlobalLayoutListener(this); + } + + currentPosition = pager.getCurrentItem(); + scrollToChild(currentPosition, 0); + } + }); + + } + + private void addTextTab(final int position, String title) { + + TextView tab = new TextView(getContext()); + tab.setText(title); + tab.setFocusable(true); + tab.setGravity(Gravity.CENTER); + tab.setSingleLine(); + + tab.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + pager.setCurrentItem(position); + } + }); + + tabsContainer.addView(tab); + + } + + private void addIconTab(final int position, int resId) { + + ImageButton tab = new ImageButton(getContext()); + tab.setFocusable(true); + tab.setImageResource(resId); + + tab.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + pager.setCurrentItem(position); + } + }); + + tabsContainer.addView(tab); + + } + + private void updateTabStyles() { + + for (int i = 0; i < tabCount; i++) { + + View v = tabsContainer.getChildAt(i); + + v.setLayoutParams(defaultTabLayoutParams); + v.setBackgroundResource(tabBackgroundResId); + if (shouldExpand) { + v.setPadding(0, 0, 0, 0); + } else { + v.setPadding(tabPadding, 0, tabPadding, 0); + } + + if (v instanceof TextView) { + + TextView tab = (TextView) v; + tab.setTextSize(TypedValue.COMPLEX_UNIT_PX, tabTextSize); + tab.setTypeface(tabTypeface, tabTypefaceStyle); + tab.setTextColor(tabTextColor); + + // setAllCaps() is only available from API 14, so the upper case is made manually if we are on a + // pre-ICS-build + if (textAllCaps) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + tab.setAllCaps(true); + } else { + tab.setText(tab.getText().toString().toUpperCase(locale)); + } + } + } + } + + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + if (!shouldExpand || MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.UNSPECIFIED) { + return; + } + + int myWidth = getMeasuredWidth(); + int childWidth = 0; + for (int i = 0; i < tabCount; i++) { + childWidth += tabsContainer.getChildAt(i).getMeasuredWidth(); + } + + if (!checkedTabWidths && childWidth > 0 && myWidth > 0) { + + if (childWidth <= myWidth) { + for (int i = 0; i < tabCount; i++) { + tabsContainer.getChildAt(i).setLayoutParams(expandedTabLayoutParams); + } + } + + checkedTabWidths = true; + } + } + + private void scrollToChild(int position, int offset) { + + if (tabCount == 0) { + return; + } + + int newScrollX = tabsContainer.getChildAt(position).getLeft() + offset; + + if (position > 0 || offset > 0) { + newScrollX -= scrollOffset; + } + + if (newScrollX != lastScrollX) { + lastScrollX = newScrollX; + scrollTo(newScrollX, 0); + } + + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (isInEditMode() || tabCount == 0) { + return; + } + + final int height = getHeight(); + + // draw indicator line + + rectPaint.setColor(indicatorColor); + + // default: line below current tab + View currentTab = tabsContainer.getChildAt(currentPosition); + float lineLeft = currentTab.getLeft(); + float lineRight = currentTab.getRight(); + + // if there is an offset, start interpolating left and right coordinates between current and next tab + if (currentPositionOffset > 0f && currentPosition < tabCount - 1) { + + View nextTab = tabsContainer.getChildAt(currentPosition + 1); + final float nextTabLeft = nextTab.getLeft(); + final float nextTabRight = nextTab.getRight(); + + lineLeft = (currentPositionOffset * nextTabLeft + (1f - currentPositionOffset) * lineLeft); + lineRight = (currentPositionOffset * nextTabRight + (1f - currentPositionOffset) * lineRight); + } + + canvas.drawRect(lineLeft, height - indicatorHeight, lineRight, height, rectPaint); + + // draw underline + + rectPaint.setColor(underlineColor); + canvas.drawRect(0, height - underlineHeight, tabsContainer.getWidth(), height, rectPaint); + + // draw divider + + dividerPaint.setColor(dividerColor); + for (int i = 0; i < tabCount - 1; i++) { + View tab = tabsContainer.getChildAt(i); + canvas.drawLine(tab.getRight(), dividerPadding, tab.getRight(), height - dividerPadding, dividerPaint); + } + } + + private class PageListener implements OnPageChangeListener { + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + + currentPosition = position; + currentPositionOffset = positionOffset; + + scrollToChild(position, (int) (positionOffset * tabsContainer.getChildAt(position).getWidth())); + + invalidate(); + + if (delegatePageListener != null) { + delegatePageListener.onPageScrolled(position, positionOffset, positionOffsetPixels); + } + } + + @Override + public void onPageScrollStateChanged(int state) { + if (state == ViewPager.SCROLL_STATE_IDLE) { + scrollToChild(pager.getCurrentItem(), 0); + } + + if (delegatePageListener != null) { + delegatePageListener.onPageScrollStateChanged(state); + } + } + + @Override + public void onPageSelected(int position) { + if (delegatePageListener != null) { + delegatePageListener.onPageSelected(position); + } + } + + } + + public void setIndicatorColor(int indicatorColor) { + this.indicatorColor = indicatorColor; + invalidate(); + } + + public void setIndicatorColorResource(int resId) { + this.indicatorColor = getResources().getColor(resId); + invalidate(); + } + + public int getIndicatorColor() { + return this.indicatorColor; + } + + public void setIndicatorHeight(int indicatorLineHeightPx) { + this.indicatorHeight = indicatorLineHeightPx; + invalidate(); + } + + public int getIndicatorHeight() { + return indicatorHeight; + } + + public void setUnderlineColor(int underlineColor) { + this.underlineColor = underlineColor; + invalidate(); + } + + public void setUnderlineColorResource(int resId) { + this.underlineColor = getResources().getColor(resId); + invalidate(); + } + + public int getUnderlineColor() { + return underlineColor; + } + + public void setDividerColor(int dividerColor) { + this.dividerColor = dividerColor; + invalidate(); + } + + public void setDividerColorResource(int resId) { + this.dividerColor = getResources().getColor(resId); + invalidate(); + } + + public int getDividerColor() { + return dividerColor; + } + + public void setUnderlineHeight(int underlineHeightPx) { + this.underlineHeight = underlineHeightPx; + invalidate(); + } + + public int getUnderlineHeight() { + return underlineHeight; + } + + public void setDividerPadding(int dividerPaddingPx) { + this.dividerPadding = dividerPaddingPx; + invalidate(); + } + + public int getDividerPadding() { + return dividerPadding; + } + + public void setScrollOffset(int scrollOffsetPx) { + this.scrollOffset = scrollOffsetPx; + invalidate(); + } + + public int getScrollOffset() { + return scrollOffset; + } + + public void setShouldExpand(boolean shouldExpand) { + this.shouldExpand = shouldExpand; + requestLayout(); + } + + public boolean getShouldExpand() { + return shouldExpand; + } + + public boolean isTextAllCaps() { + return textAllCaps; + } + + public void setAllCaps(boolean textAllCaps) { + this.textAllCaps = textAllCaps; + } + + public void setTextSize(int textSizePx) { + this.tabTextSize = textSizePx; + updateTabStyles(); + } + + public int getTextSize() { + return tabTextSize; + } + + public void setTextColor(int textColor) { + this.tabTextColor = textColor; + updateTabStyles(); + } + + public void setTextColorResource(int resId) { + this.tabTextColor = getResources().getColor(resId); + updateTabStyles(); + } + + public int getTextColor() { + return tabTextColor; + } + + public void setTypeface(Typeface typeface, int style) { + this.tabTypeface = typeface; + this.tabTypefaceStyle = style; + updateTabStyles(); + } + + public void setTabBackground(int resId) { + this.tabBackgroundResId = resId; + } + + public int getTabBackground() { + return tabBackgroundResId; + } + + public void setTabPaddingLeftRight(int paddingPx) { + this.tabPadding = paddingPx; + updateTabStyles(); + } + + public int getTabPaddingLeftRight() { + return tabPadding; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + SavedState savedState = (SavedState) state; + super.onRestoreInstanceState(savedState.getSuperState()); + currentPosition = savedState.currentPosition; + requestLayout(); + } + + @Override + public Parcelable onSaveInstanceState() { + Parcelable superState = super.onSaveInstanceState(); + SavedState savedState = new SavedState(superState); + savedState.currentPosition = currentPosition; + return savedState; + } + + static class SavedState extends BaseSavedState { + int currentPosition; + + public SavedState(Parcelable superState) { + super(superState); + } + + private SavedState(Parcel in) { + super(in); + currentPosition = in.readInt(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(currentPosition); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + @Override + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } + +} diff -r a1f3a20c87f8 -r ae7bea323864 src/com/astuetz/viewpager/extensions/package-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/astuetz/viewpager/extensions/package-info.java Wed May 15 11:35:03 2013 +0200 @@ -0,0 +1,10 @@ +/** + * This package contains an implementation of an + * Interactive paging indicator widget, compatible with the `ViewPager` from the + * Android Support Library. + * + * It is developped by Andreas Stuetz + * and can be found here https://github.com/astuetz/PagerSlidingTabStrip/ + * We use the git tag v1.0 aka 0e7f1b9a961692323dbd7300e13dd1d78ad0ae44 + */ +package com.astuetz.viewpager.extensions;