Java tutorial
/* * Copyright (C) 2013 Andreas Stuetz <andreas.stuetz@gmail.com> * * 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.coolerfall.uiart; import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Typeface; import android.graphics.drawable.GradientDrawable; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.support.annotation.NonNull; 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; /** * This is forked from com.astuetz.pagerslidingtabstrip. * * @author Vincent Cheung * @since Dec. 09, 2014 */ public class PagerSlidingTabStrip extends HorizontalScrollView implements OnPageChangeListener { /** icon tab provider */ public interface IconTabProvider { int getPageIconResId(int position); } /** text tab with number indicator provider */ public interface NumTabProvider { int getNumber(int position); } private int mTabCount = 0; private int mCurrentPosition = 0; private float mCurrentPositionOffset = 0f; private boolean mFixed = false; private int mScrollOffset = 52; private int mLastScrollX = 0; private int mNumPadding = 8; private int mTabPadding = 25; private int mTabTextSize = 16; private int mNumTextSize = 10; private int mTabTextColor = 0xFF37474F; private int mIndicatorColor = 0xFF009688; private int mUnderlineColor = 0xFFDDDDDD; private int mNumTextColor = Color.WHITE; private int mNumBackground = 0xFFFF8050; private float mCornerRadius = 4f; private int mIndicatorHeight = 4; private int mUnderlineHeight = 0; private Typeface mTabTypeface = null; private int mTabTypefaceStyle = Typeface.NORMAL; private int mTabBackgroundResId = android.R.color.transparent; private Paint mRectPaint; /** default tab layout params */ private LinearLayout.LayoutParams mDefaultTabLayoutParams; /** fixed tab layout params */ private LinearLayout.LayoutParams mFixedTabLayoutParams; /** number layout params in number tab */ private LinearLayout.LayoutParams mNumLayoutParams; /** tabs container to put tabs */ private LinearLayout mTabsContainer; /** view pager */ private ViewPager mViewPager; /** page change listener */ private OnPageChangeListener mOnPageChangeListener; 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); mTabsContainer = new LinearLayout(context); mTabsContainer.setOrientation(LinearLayout.HORIZONTAL); mTabsContainer.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); addView(mTabsContainer); /* default attrs */ DisplayMetrics dm = getResources().getDisplayMetrics(); mIndicatorHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mIndicatorHeight, dm); mUnderlineHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mUnderlineHeight, dm); mTabTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, mTabTextSize, dm); mNumTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, mNumTextSize, dm); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PagerSlidingTabStrip); /* get system attrs */ mTabTextSize = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_android_textSize, mTabTextSize); mTabTextColor = a.getColor(R.styleable.PagerSlidingTabStrip_android_textColor, mTabTextColor); mTabTypefaceStyle = a.getInt(R.styleable.PagerSlidingTabStrip_android_textStyle, mTabTypefaceStyle); /* get custom attrs */ mIndicatorColor = a.getColor(R.styleable.PagerSlidingTabStrip_indicatorColor, mIndicatorColor); mUnderlineColor = a.getColor(R.styleable.PagerSlidingTabStrip_underLineColor, mUnderlineColor); mIndicatorHeight = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_indicatorHeight, mIndicatorHeight); mUnderlineHeight = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_underLineHeight, mUnderlineHeight); mTabPadding = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_tabsPadding, mTabPadding); mFixed = a.getBoolean(R.styleable.PagerSlidingTabStrip_fixed, mFixed); /* if use expand layout, the tab padding should be 0 */ if (mFixed) { mTabPadding = 0; } a.recycle(); /* create a new paint to draw ui */ mRectPaint = new Paint(); mRectPaint.setAntiAlias(true); mRectPaint.setStyle(Style.FILL); mDefaultTabLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); mFixedTabLayoutParams = new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f); mNumLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); mNumLayoutParams.setMargins(mNumPadding, 0, 0, 0); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (isInEditMode() || mTabCount == 0) { return; } final int height = getHeight(); /* default: line below current tab */ View currentTab = mTabsContainer.getChildAt(mCurrentPosition); 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 (mCurrentPositionOffset > 0f && mCurrentPosition < mTabCount - 1) { View nextTab = mTabsContainer.getChildAt(mCurrentPosition + 1); final float nextTabLeft = nextTab.getLeft(); final float nextTabRight = nextTab.getRight(); lineLeft = (mCurrentPositionOffset * nextTabLeft + (1f - mCurrentPositionOffset) * lineLeft); lineRight = (mCurrentPositionOffset * nextTabRight + (1f - mCurrentPositionOffset) * lineRight); } /* draw indicator line */ mRectPaint.setColor(mIndicatorColor); canvas.drawRect(lineLeft, height - mIndicatorHeight, lineRight, height, mRectPaint); /* draw underline */ if (mUnderlineHeight != 0) { mRectPaint.setColor(mUnderlineColor); canvas.drawRect(0, height - mUnderlineHeight, mTabsContainer.getWidth(), height, mRectPaint); } } @Override public void onRestoreInstanceState(Parcelable state) { SavedState savedState = (SavedState) state; super.onRestoreInstanceState(savedState.getSuperState()); mCurrentPosition = savedState.currentPosition; requestLayout(); } @Override public Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); SavedState savedState = new SavedState(superState); savedState.currentPosition = mCurrentPosition; return savedState; } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { mCurrentPosition = position; mCurrentPositionOffset = positionOffset; scrollToChild(position, (int) (positionOffset * mTabsContainer.getChildAt(position).getWidth())); invalidate(); /* if we have page listener, invoke it */ if (mOnPageChangeListener != null) { mOnPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels); } } @Override public void onPageScrollStateChanged(int state) { if (state == ViewPager.SCROLL_STATE_IDLE) { scrollToChild(mViewPager.getCurrentItem(), 0); } /* if we have page listener, invoke it */ if (mOnPageChangeListener != null) { mOnPageChangeListener.onPageScrollStateChanged(state); } } @Override public void onPageSelected(int position) { updateTabStyles(position); /* if we have page listener, invoke it */ if (mOnPageChangeListener != null) { mOnPageChangeListener.onPageSelected(position); } } /** saved state */ 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(@NonNull Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeInt(currentPosition); } public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() { @Override public SavedState createFromParcel(Parcel in) { return new SavedState(in); } @Override public SavedState[] newArray(int size) { return new SavedState[size]; } }; } /** add one text tab */ private void addTextTab(final int position, String title) { TextView tab = new TextView(getContext()); tab.setText(title); tab.setGravity(Gravity.CENTER); tab.setSingleLine(); addTab(position, tab); } /** add one number indicator tab */ private void addNumTab(final int position, String title, int num) { LinearLayout tab = new LinearLayout(getContext()); TextView titleText = new TextView(getContext()); TextView numText = new TextView(getContext()); titleText.setText(title); titleText.setGravity(Gravity.CENTER); titleText.setSingleLine(); numText.setText(Integer.toString(num)); numText.setGravity(Gravity.CENTER); numText.setPadding(mNumPadding, 1, mNumPadding, 1); numText.setSingleLine(); /* if the number is 0, set invisible */ if (num == 0) { numText.setVisibility(View.GONE); } else { numText.setVisibility(View.VISIBLE); } tab.addView(titleText, 0, mDefaultTabLayoutParams); tab.addView(numText, 1, mNumLayoutParams); tab.setGravity(Gravity.CENTER); addTab(position, tab); } /** add one icon tab */ private void addIconTab(final int position, int resId) { ImageButton tab = new ImageButton(getContext()); tab.setImageResource(resId); addTab(position, tab); } /** add one tab into tab container */ private void addTab(final int position, View tab) { tab.setFocusable(true); tab.setPadding(mTabPadding, 3, mTabPadding, 3); tab.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mViewPager.setCurrentItem(position, true); } }); mTabsContainer.addView(tab, position, mFixed ? mFixedTabLayoutParams : mDefaultTabLayoutParams); } /** * update tab styles, the tab at 'position' will be updated to indicator style, * and others will be updated to default style */ private void updateTabStyles(int position) { int textColor; mTabsContainer.setBackgroundResource(mTabBackgroundResId); for (int i = 0; i < mTabCount; i++) { if (i == position) { textColor = mIndicatorColor; } else { textColor = mTabTextColor; } /* only change the style of title text */ View v = mTabsContainer.getChildAt(i); if (v instanceof TextView) { TextView tab = (TextView) v; updateTabTextStyle(tab, textColor); } else if (v instanceof LinearLayout) { View tabTitle = ((LinearLayout) v).getChildAt(0); View tabNum = ((LinearLayout) v).getChildAt(1); /* update title */ if (tabTitle instanceof TextView) { TextView titleText = (TextView) tabTitle; updateTabTextStyle(titleText, textColor); } /* update number */ if (tabNum instanceof TextView) { TextView numText = (TextView) tabNum; updateNumTextStyle(numText); } } } } /** update the style of title text */ private void updateTabTextStyle(TextView tab, int color) { tab.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTabTextSize); tab.setTypeface(mTabTypeface, mTabTypefaceStyle); tab.setTextColor(color); } /** update the style of number text */ private void updateNumTextStyle(TextView tab) { tab.setTextSize(TypedValue.COMPLEX_UNIT_PX, mNumTextSize); tab.setTypeface(mTabTypeface, mTabTypefaceStyle); tab.setTextColor(mNumTextColor); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { setBackgroud(tab); } else { setBackgroundJelly(tab); } } @SuppressWarnings("deprecation") private void setBackgroud(TextView tab) { tab.setBackgroundDrawable(createDrawable()); } @TargetApi(Build.VERSION_CODES.JELLY_BEAN) private void setBackgroundJelly(TextView tab) { tab.setBackground(createDrawable()); } /** get background drawable */ private GradientDrawable createDrawable() { GradientDrawable drawable = new GradientDrawable(); drawable.setCornerRadius(mCornerRadius); drawable.setColor(mNumBackground); return drawable; } /** scroll to child with new position */ private void scrollToChild(int position, int offset) { if (mTabCount == 0) { return; } int newScrollX = mTabsContainer.getChildAt(position).getLeft() + offset; if (position > 0 || offset > 0) { newScrollX -= mScrollOffset; } if (newScrollX != mLastScrollX) { mLastScrollX = newScrollX; scrollTo(newScrollX, 0); } } /** * Set view pager used in strip. * * @param viewPager view pager */ public void setViewPager(ViewPager viewPager) { mViewPager = viewPager; if (mViewPager.getAdapter() == null) { throw new IllegalStateException("ViewPager does not have adapter instance."); } mViewPager.setOnPageChangeListener(this); notifyDataSetChanged(); } /** * Set on page change listener. * * @param l page change listener */ public void setOnPageChangeListener(OnPageChangeListener l) { mOnPageChangeListener = l; } /** * Notify data set changed. */ public void notifyDataSetChanged() { mTabsContainer.removeAllViews(); mTabCount = mViewPager.getAdapter().getCount(); for (int i = 0; i < mTabCount; i++) { if (mViewPager.getAdapter() instanceof IconTabProvider) { addIconTab(i, ((IconTabProvider) mViewPager.getAdapter()).getPageIconResId(i)); } else if (mViewPager.getAdapter() instanceof NumTabProvider) { int num = ((NumTabProvider) mViewPager.getAdapter()).getNumber(i); num = num < 0 ? 0 : num; addNumTab(i, mViewPager.getAdapter().getPageTitle(i).toString(), num); } else { addTextTab(i, mViewPager.getAdapter().getPageTitle(i).toString()); } } updateTabStyles(mViewPager.getCurrentItem()); getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { removeLayoutListener(this); } else { removeLayoutListenerJelly(this); } mCurrentPosition = mViewPager.getCurrentItem(); if (mCurrentPosition > mTabCount) { mCurrentPosition = mTabCount; } scrollToChild(mCurrentPosition, 0); mCurrentPositionOffset = 0; } }); } @TargetApi(Build.VERSION_CODES.JELLY_BEAN) private void removeLayoutListenerJelly(OnGlobalLayoutListener l) { getViewTreeObserver().removeOnGlobalLayoutListener(l); } @SuppressWarnings("deprecation") private void removeLayoutListener(OnGlobalLayoutListener l) { getViewTreeObserver().removeGlobalOnLayoutListener(l); } /** * Set the padding of tab. * * @param padding padding */ public void setTabPadding(int padding) { mTabPadding = padding; } /** * Use fixed layout or not. * * @param fixed true or false */ public void setShouldExpand(boolean fixed) { mFixed = fixed; requestLayout(); } /** * Set backgroud of tab. * * @param resId resource id */ public void setTabBackground(int resId) { setBackgroundResource(resId); invalidate(); } /** * Set the color of indicator. * * @param indicatorColor color of indicator to set */ public void setIndicatorColor(int indicatorColor) { mIndicatorColor = indicatorColor; invalidate(); } /** * Set the color resource of indicator. * * @param resId color resource id of indicator to set */ public void setIndicatorColorResource(int resId) { mIndicatorColor = getResources().getColor(resId); invalidate(); } /** * Set the height of indicator line. * */ public void setIndicatorHeight(int heightPx) { mIndicatorHeight = heightPx; invalidate(); } /** * Set text size in tab. * * @param textSizeSp text size in sp */ public void setTextSize(int textSizeSp) { DisplayMetrics dm = getResources().getDisplayMetrics(); mTabTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, textSizeSp, dm); } /** * Set the color of text in tab. * * @param textColor text color */ public void setTextColor(int textColor) { mTabTextColor = textColor; updateTabStyles(mViewPager.getCurrentItem()); } /** * Set the color of text in tab. * * @param resId text color resource id */ public void setTextColorResource(int resId) { mTabTextColor = getResources().getColor(resId); updateTabStyles(mViewPager.getCurrentItem()); } /** * Set type face of text in tab. * * @param typeface type face * @param style stype */ public void setTypeface(Typeface typeface, int style) { mTabTypeface = typeface; mTabTypefaceStyle = style; updateTabStyles(mViewPager.getCurrentItem()); } /** * Set the height of underline line. * * @param heightPx height in pixel */ public void setUnderlineHeight(int heightPx) { mUnderlineHeight = heightPx; invalidate(); } /** * Set the color of underline. * * @param underlineColor underline color */ public void setUnderlineColor(int underlineColor) { mUnderlineColor = underlineColor; invalidate(); } /** * Set the color resource of underline. * * @param resId resource id */ public void setUnderlineColorResource(int resId) { mUnderlineColor = getResources().getColor(resId); invalidate(); } /** * Set the text color of number. * * @param color text color */ public void setNumberTextColor(int color) { mNumTextColor = color; invalidate(); } /** * Set the color of number background. * * @param color background color */ public void setNumberBackground(int color) { mNumBackground = color; invalidate(); } /** * Set the color resource of number background. * * @param resId background color resource id */ public void setNumberBackgroundResource(int resId) { mNumBackground = getResources().getColor(resId); invalidate(); } /** * Set number showing in number tab. * * @param number number to show */ public void setNumber(int position, int number) { if (position > mTabCount) { throw new IllegalArgumentException("the position is not valid"); } if (!(mViewPager.getAdapter() instanceof NumTabProvider)) { throw new IllegalAccessError( "the tab is not number tab, " + "the adapter should implement NumTabProvider"); } /* update the number text */ View v = mTabsContainer.getChildAt(position); if (v instanceof LinearLayout) { View tabNum = ((LinearLayout) v).getChildAt(1); if (tabNum instanceof TextView) { TextView textNum = (TextView) tabNum; if (number == 0) { textNum.setVisibility(View.GONE); } else { textNum.setVisibility(View.VISIBLE); textNum.setText(Integer.toString(number)); } } } } }