Java tutorial
/* * Copyright (C) 2014 The Android Open Source Project * * 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.am.pagergradienttab.view; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.content.res.TypedArray; import android.database.DataSetObserver; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Paint.FontMetrics; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.support.v4.view.PagerAdapter; import android.text.TextPaint; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewParent; /** * PagerNoSlideTabStrip TabsViewPagerViewPager??Tabs5 * * @author Alex * */ public class PagerGradientTabStrip extends View implements ViewPager.Decor { private ViewPager mPager; private final PageListener mPageListener = new PageListener(); private WeakReference<PagerAdapter> mWatchingAdapter; private List<String> tabs = new ArrayList<String>(); final float density; private float itemWidth; private int itemHeight; private float textTop;// private boolean showTextGradient = true;// ?? private int textColorNormal;// private int textColorSelected;// private boolean showTextScale = true;// private float textSize;// ? private float magnification = 0.2f; private float textSizeOffset = 1; private int intervalWidth; private boolean showIndicator = true; private int indicatorPadding; private int indicatorHeight; private int indicatorColor = Color.BLACK; private float indicatorOffset = 0; private boolean showUnderLine = true; private int underLineHeight; private int underLineColor = Color.BLACK; private boolean showTabColor = false; private int tabColorSelected = Color.DKGRAY; private int tabColorNormal = Color.argb(Color.alpha(0x80000000), Color.red(tabColorSelected), Color.green(tabColorSelected), Color.blue(tabColorSelected)); private TabTagAdapter mTagAdapter; private final Rect mRefreshRect = new Rect(); private final TextPaint mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); private int mLastKnownPosition = 0; private float mPositionOffset = 0; private float mLastKnownPositionOffset = -1; private int currectPager = 0; private int nextPager = 0; private int clickPosition = 0; private static final int[] ATTRS = new int[] { android.R.attr.textSize, android.R.attr.textColor }; /** * ? * * @author AlexMofer * */ public enum TagAlign { LEFTTOP(0), LEFTCENTER(1), LEFTBOTTOM(2), RIGHTTOP(3), RIGHTCENTER(4), RIGHTBOTTOM(5), LEFTTOPTEXT( 6), LEFTCENTERTEXT(7), LEFTBOTTOMTEXT(8), RIGHTTOPTEXT(9), RIGHTCENTERTEXT(10), RIGHTBOTTOMTEXT(11); private TagAlign(int nativeInt) { this.nativeInt = nativeInt; } final int nativeInt; } public PagerGradientTabStrip(Context context) { this(context, null); } public PagerGradientTabStrip(Context context, AttributeSet attrs) { super(context, attrs); density = getResources().getDisplayMetrics().density; final TypedArray a = context.obtainStyledAttributes(attrs, ATTRS); textSize = a.getDimensionPixelSize(0, (int) (16 * density)); textColorSelected = a.getColor(1, Color.BLACK); textColorNormal = Color.argb(Color.alpha(0x80000000), Color.red(textColorSelected), Color.green(textColorSelected), Color.blue(textColorSelected)); a.recycle(); mTextPaint.setAntiAlias(true); intervalWidth = (int) (0 * density); indicatorPadding = (int) (10 * density); indicatorHeight = (int) (3 * density); underLineHeight = (int) (2 * density); } private int getColor(int normalColor, int selectedColor, float offset) { int normalAlpha = Color.alpha(normalColor); int normalRed = Color.red(normalColor); int normalGreen = Color.green(normalColor); int normalBlue = Color.blue(normalColor); int selectedAlpha = Color.alpha(selectedColor); int selectedRed = Color.red(selectedColor); int selectedGreen = Color.green(selectedColor); int selectedBlue = Color.blue(selectedColor); int a = (int) Math.ceil((selectedAlpha - normalAlpha) * offset); int r = (int) Math.ceil((selectedRed - normalRed) * offset); int g = (int) Math.ceil((selectedGreen - normalGreen) * offset); int b = (int) Math.ceil((selectedBlue - normalBlue) * offset); return Color.argb(normalAlpha + a, normalRed + r, normalGreen + g, normalBlue + b); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); final ViewParent parent = getParent(); if (!(parent instanceof ViewPager)) { throw new IllegalStateException("PagerNoSlideTabStrip must be a direct child of a ViewPager."); } final ViewPager pager = (ViewPager) parent; final PagerAdapter adapter = pager.getAdapter(); pager.setInternalPageChangeListener(mPageListener); pager.setOnAdapterChangeListener(mPageListener); mPager = pager; updateAdapter(mWatchingAdapter != null ? mWatchingAdapter.get() : null, adapter); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if (mPager != null) { updateAdapter(mPager.getAdapter(), null); mPager.setInternalPageChangeListener(null); mPager.setOnAdapterChangeListener(null); mPager = null; } } void updateAdapter(PagerAdapter oldAdapter, PagerAdapter newAdapter) { if (oldAdapter != null) { oldAdapter.unregisterDataSetObserver(mPageListener); mWatchingAdapter = null; } tabs.clear(); if (newAdapter != null) { newAdapter.registerDataSetObserver(mPageListener); mWatchingAdapter = new WeakReference<PagerAdapter>(newAdapter); mLastKnownPosition = mPager.getCurrentItem(); currectPager = mLastKnownPosition; nextPager = mLastKnownPosition; for (int i = 0; i < newAdapter.getCount(); i++) { tabs.add(newAdapter.getPageTitle(i).toString()); } } if (mPager != null) { requestLayout(); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int widthMode = MeasureSpec.getMode(widthMeasureSpec); final int heightMode = MeasureSpec.getMode(heightMeasureSpec); final int widthSize = MeasureSpec.getSize(widthMeasureSpec); final int heightSize = MeasureSpec.getSize(heightMeasureSpec); if (widthMode != MeasureSpec.EXACTLY) { // TODO ?? throw new IllegalStateException("Must measure with an exact width"); } itemWidth = (float) (widthSize - getPaddingLeft() - getPaddingRight() - intervalWidth * (tabs.size() <= 0 ? 1 : tabs.size() - 1)) / (tabs.size() <= 0 ? 1 : tabs.size()); mTextPaint.setTextSize(magnification <= 0 ? textSize : textSize + textSize * magnification); // TODO TabitemWidth? FontMetrics fontMetrics = mTextPaint.getFontMetrics(); float textHeight = fontMetrics.descent - fontMetrics.ascent; textTop = textHeight - (-fontMetrics.ascent - fontMetrics.descent + (fontMetrics.bottom - fontMetrics.descent) * density); textHeight += textTop; itemHeight = (int) Math.ceil(textHeight + indicatorHeight + underLineHeight); int minHeight = getMinHeight(); if (minHeight > itemHeight + getPaddingTop() + getPaddingBottom()) { itemHeight = minHeight - getPaddingTop() - getPaddingBottom(); } if (heightMode == MeasureSpec.EXACTLY) { setMeasuredDimension(widthSize, heightSize); mRefreshRect.set(getPaddingLeft(), getPaddingTop(), widthSize - getPaddingLeft() - getPaddingRight(), heightSize - getPaddingTop() - getPaddingBottom()); } else { setMeasuredDimension(widthSize, itemHeight + getPaddingTop() + getPaddingBottom()); mRefreshRect.set(getPaddingLeft(), getPaddingTop(), widthSize - getPaddingLeft() - getPaddingRight(), itemHeight); } } private int getMinHeight() { int minHeight = 0; final Drawable bg = getBackground(); if (bg != null) { minHeight = bg.getIntrinsicHeight(); } return minHeight; } /** * Tab */ @Override public boolean performClick() { performClick(clickPosition); return super.performClick(); } /** * positionTab * * @param position */ public void performClick(int position) { if (mPager.getAdapter() != null && mPager.getAdapter().getCount() > 0) mPager.setCurrentItem(position % mPager.getAdapter().getCount(), true); } @Override public boolean onTouchEvent(MotionEvent ev) { final int action = ev.getAction(); final float x = ev.getX(); switch (action) { case MotionEvent.ACTION_DOWN: break; case MotionEvent.ACTION_MOVE: break; case MotionEvent.ACTION_UP: for (int i = 0; i < tabs.size(); i++) { float l = getPaddingLeft() + intervalWidth * i + itemWidth * i; float r = l + itemWidth; if (x >= l && x <= r) { clickPosition = i; break; } } performClick(); break; } return true; } @Override protected void onDraw(Canvas canvas) { drawTabBG(canvas); drawText(canvas); drawTag(canvas); drawIndicator(canvas); drawUnderLine(canvas); super.onDraw(canvas); } /** * Tab * * @param canvas */ private void drawTabBG(Canvas canvas) { if (showTabColor) { mTextPaint.setColor(tabColorNormal); canvas.save(); canvas.translate(getPaddingLeft(), 0); if (tabs.size() > 0) for (int i = 0; i < tabs.size(); i++) { if (i == nextPager) { mTextPaint.setColor(getColor(tabColorNormal, tabColorSelected, textSizeOffset)); } else if (i == currectPager) { mTextPaint.setColor(getColor(tabColorNormal, tabColorSelected, 1 - textSizeOffset)); } else { mTextPaint.setColor(tabColorNormal); } canvas.drawRect(0, getPaddingTop(), itemWidth, getHeight() - getPaddingBottom(), mTextPaint); canvas.translate(itemWidth + intervalWidth, 0); } else { mTextPaint.setColor(tabColorSelected); canvas.drawRect(0, getPaddingTop(), itemWidth + intervalWidth + getPaddingLeft(), getHeight() - getPaddingBottom(), mTextPaint); } canvas.restore(); } } /** * * * @param canvas */ private void drawText(Canvas canvas) { float x = getPaddingLeft() + itemWidth / 2; int y = getHeight() - getPaddingBottom() - indicatorHeight - underLineHeight; int position = 0; for (String text : tabs) { canvas.save(); mTextPaint.setColor(textColorNormal); mTextPaint.setTextAlign(Align.LEFT); mTextPaint.setTextSize(textSize); if (showTextGradient) { if (position == nextPager) { mTextPaint.setColor(getColor(textColorNormal, textColorSelected, textSizeOffset)); } else if (position == currectPager) { mTextPaint.setColor(getColor(textColorNormal, textColorSelected, 1 - textSizeOffset)); } else { mTextPaint.setColor(textColorNormal); } } if (showTextScale) { if (position == nextPager) { canvas.translate(x - mTextPaint.measureText(text) * (1 + textSizeOffset * magnification) / 2, y - textTop); canvas.scale(1 + textSizeOffset * magnification, 1 + textSizeOffset * magnification); } else if (position == currectPager) { canvas.translate( x - mTextPaint.measureText(text) * (1 + (1 - textSizeOffset) * magnification) / 2, y - textTop); canvas.scale(1 + (1 - textSizeOffset) * magnification, 1 + (1 - textSizeOffset) * magnification); } else { canvas.translate(x - mTextPaint.measureText(text) / 2, y - textTop); canvas.scale(1, 1); } } else { canvas.translate(x - mTextPaint.measureText(text) / 2, y - textTop); } canvas.drawText(text, 0, 0, mTextPaint); x += itemWidth + intervalWidth; position++; canvas.restore(); } } /** * * * @param canvas */ private void drawTag(Canvas canvas) { // TODO if (mTagAdapter != null) { canvas.save(); float canvasOffset = 0; int x = getPaddingLeft(); int y = getPaddingTop(); float textTop = 0; mTextPaint.setColor(Color.WHITE); mTextPaint.setTextAlign(Align.LEFT); for (int position = 0; position < tabs.size(); position++) { canvas.translate(canvasOffset, 0); canvasOffset = itemWidth + intervalWidth; canvas.save(); if (mTagAdapter.isEnable(position)) { int textWidth; int textHeight; mTextPaint.setTextSize(mTagAdapter.getTextSize(position)); String tag = mTagAdapter.getTag(position) == null ? "" : mTagAdapter.getTag(position); textWidth = (int) Math.ceil(mTextPaint.measureText(tag)); FontMetrics fontMetrics = mTextPaint.getFontMetrics(); textHeight = (int) Math.ceil(fontMetrics.descent - fontMetrics.ascent); textTop = textHeight - (-fontMetrics.ascent - fontMetrics.descent + (fontMetrics.bottom - fontMetrics.descent) * density); textHeight += textTop; if ("".equals(tag)) { textHeight = 0; } final Drawable drawable = mTagAdapter.getBackground(position); int drawableWidth = drawable == null ? 0 : drawable.getMinimumWidth(); int drawableHeight = drawable == null ? 0 : drawable.getMinimumHeight(); float offsetX = Math.max(textWidth + mTagAdapter.getPaddingLeft(position) + mTagAdapter.getPaddingRight(position), drawableWidth); float offsetTextX = (offsetX - (textWidth + mTagAdapter.getPaddingLeft(position) + mTagAdapter.getPaddingRight(position))) * 0.5f; float offsetY = Math.max(textHeight + mTagAdapter.getPaddingBottom(position) + mTagAdapter.getPaddingTop(position), drawableHeight); float offsetTextY = (offsetY - (textHeight + mTagAdapter.getPaddingBottom(position) + mTagAdapter.getPaddingTop(position))) * 0.5f; mTextPaint.setTextSize(showTextScale ? textSize * (1 + magnification) : textSize); float myTextWidth = mTextPaint.measureText(tabs.get(position)); TagAlign ta = mTagAdapter.getTagAlign(position); switch (ta) { default: case LEFTTOP: canvas.translate(mTagAdapter.getMarginLeft(position), mTagAdapter.getMarginTop(position)); break; case LEFTCENTER: canvas.translate(mTagAdapter.getMarginLeft(position), (getHeight() - underLineHeight - offsetY) / 2); break; case LEFTBOTTOM: canvas.translate(mTagAdapter.getMarginLeft(position), getHeight() - offsetY - underLineHeight - mTagAdapter.getMarginBottom(position)); break; case RIGHTTOP: canvas.translate(itemWidth - offsetX - mTagAdapter.getMarginRight(position), mTagAdapter.getMarginTop(position)); break; case RIGHTCENTER: canvas.translate(itemWidth - offsetX - mTagAdapter.getMarginRight(position), (getHeight() - underLineHeight - offsetY) / 2); break; case RIGHTBOTTOM: canvas.translate(itemWidth - offsetX - mTagAdapter.getMarginRight(position), getHeight() - offsetY - underLineHeight - mTagAdapter.getMarginBottom(position)); break; case LEFTTOPTEXT: canvas.translate( (itemWidth - myTextWidth) / 2 - offsetX - mTagAdapter.getMarginRight(position), mTagAdapter.getMarginTop(position)); break; case LEFTCENTERTEXT: canvas.translate( (itemWidth - myTextWidth) / 2 - offsetX - mTagAdapter.getMarginRight(position), (getHeight() - underLineHeight - offsetY) / 2); break; case LEFTBOTTOMTEXT: canvas.translate( (itemWidth - myTextWidth) / 2 - offsetX - mTagAdapter.getMarginRight(position), getHeight() - offsetY - underLineHeight - mTagAdapter.getMarginBottom(position)); break; case RIGHTTOPTEXT: canvas.translate((itemWidth + myTextWidth) / 2 + mTagAdapter.getMarginLeft(position), mTagAdapter.getMarginTop(position)); break; case RIGHTCENTERTEXT: canvas.translate((itemWidth + myTextWidth) / 2 + mTagAdapter.getMarginLeft(position), (getHeight() - underLineHeight - offsetY) / 2); break; case RIGHTBOTTOMTEXT: canvas.translate((itemWidth + myTextWidth) / 2 + mTagAdapter.getMarginLeft(position), getHeight() - offsetY - underLineHeight - mTagAdapter.getMarginBottom(position)); break; } mTextPaint.setTextSize(mTagAdapter.getTextSize(position)); if (drawable != null) { drawable.setBounds(x, y, (int) (x + offsetX), (int) (y + offsetY)); drawable.draw(canvas); } canvas.drawText(tag, x + offsetTextX + mTagAdapter.getPaddingLeft(position), y + offsetY - textTop - offsetTextY - mTagAdapter.getPaddingTop(position), mTextPaint); } canvas.restore(); } canvas.restore(); } } /** * * * @param canvas */ private void drawIndicator(Canvas canvas) { canvas.save(); canvas.translate( getPaddingLeft() + currectPager * (itemWidth + intervalWidth) + indicatorPadding + indicatorOffset * (itemWidth + intervalWidth), getHeight() - underLineHeight - getPaddingBottom() - indicatorHeight); if (showIndicator) { mTextPaint.setColor(indicatorColor); if (tabs.size() > 1) canvas.drawRect(0, 0, itemWidth - 2 * indicatorPadding, indicatorHeight, mTextPaint); else if (tabs.size() > 0) canvas.drawRect(itemWidth / 4, 0, itemWidth - itemWidth / 4 - 2 * indicatorPadding, indicatorHeight, mTextPaint); else canvas.drawRect(itemWidth / 4, 0, itemWidth - itemWidth / 4 - 1 * indicatorPadding, indicatorHeight, mTextPaint); } canvas.restore(); } /** * * * @param canvas */ private void drawUnderLine(Canvas canvas) { if (showUnderLine) { mTextPaint.setColor(underLineColor); canvas.drawRect(getPaddingLeft(), getHeight() - underLineHeight - getPaddingBottom(), getWidth() - getPaddingRight(), getHeight() - getPaddingBottom(), mTextPaint); } } /** * ???? * @param position * @param positionOffset * @param force */ private void updateView(int position, float positionOffset, boolean force) { if (mLastKnownPositionOffset == -1) { mLastKnownPositionOffset = positionOffset; } if (!force && positionOffset == mLastKnownPositionOffset) { return; } mPositionOffset = positionOffset; if (mLastKnownPositionOffset == 0 || mLastKnownPositionOffset == 1) if (mPositionOffset > 0.5f) mLastKnownPositionOffset = 1; else mLastKnownPositionOffset = 0; if (position > mLastKnownPosition) { mLastKnownPosition = position - 1; if (mLastKnownPositionOffset > mPositionOffset) { if (mPositionOffset == 0) { mPositionOffset = 1; } else { mLastKnownPosition = position; } gotoRight(mLastKnownPosition, mPositionOffset); } else { gotoLeft(mLastKnownPosition, mPositionOffset); } } else { mLastKnownPosition = position; if (mLastKnownPositionOffset > mPositionOffset) { gotoLeft(mLastKnownPosition, mPositionOffset); } else { mPositionOffset = mPositionOffset == 0 ? 1 : mPositionOffset; gotoRight(mLastKnownPosition, mPositionOffset); } } mLastKnownPosition = position; mLastKnownPositionOffset = positionOffset; } private void gotoLeft(int mLastKnownPosition, float mPositionOffset) { indicatorOffset = mPositionOffset - 1; currectPager = mLastKnownPosition + 1; nextPager = mLastKnownPosition; textSizeOffset = 1 - mPositionOffset; invalidate(); } private void gotoRight(int mLastKnownPosition, float mPositionOffset) { indicatorOffset = mPositionOffset; currectPager = mLastKnownPosition; nextPager = mLastKnownPosition + 1; textSizeOffset = mPositionOffset; invalidate(); } private class PageListener extends DataSetObserver implements ViewPager.OnPageChangeListener, ViewPager.OnAdapterChangeListener { private int mScrollState; @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { updateView(position, positionOffset, false); } @Override public void onPageSelected(int position) { if (mScrollState == ViewPager.SCROLL_STATE_IDLE) { // Only update the text here if we're not dragging or settling. float offset = mLastKnownPositionOffset >= 0 ? mLastKnownPositionOffset : 0; updateView(mPager.getCurrentItem(), offset, false); } } @Override public void onPageScrollStateChanged(int state) { mScrollState = state; } @Override public void onAdapterChanged(PagerAdapter oldAdapter, PagerAdapter newAdapter) { updateAdapter(oldAdapter, newAdapter); } @Override public void onChanged() { final float offset = mLastKnownPositionOffset >= 0 ? mLastKnownPositionOffset : 0; updateView(mPager.getCurrentItem(), offset, true); } } /** * Tabs * * @param intervalWidth */ public void setTabsInterval(int intervalWidth) { this.intervalWidth = intervalWidth; invalidate(); } /** * @return Tabs */ public int getTabsInterval() { return intervalWidth; } /** * * * @param show */ public void showTabIndicator(boolean show) { showIndicator = show; invalidate(); } /** * * * @param color * * @param height * * @param padding * padding */ public void setTabIndicator(int color, int height, int padding) { indicatorColor = color; indicatorHeight = height; indicatorPadding = padding; invalidate(); } /** * * * @param show */ public void showUnderline(boolean show) { showUnderLine = show; invalidate(); } /** * * * @param color * * @param height * */ public void setUnderline(int color, int height) { underLineColor = color; underLineHeight = height; invalidate(); } /** * ?? * * @param show */ public void showTextGradient(boolean show) { showTextGradient = show; invalidate(); } /** * * * @param colorNormal * * @param colorSelected * */ public void setTextGradient(int colorNormal, int colorSelected) { textColorNormal = colorNormal; textColorSelected = colorSelected; invalidate(); } public void showTextScale(boolean show) { showTextScale = show; invalidate(); } /** * ? * * @param size */ public void setTextSize(float size) { textSize = size; invalidate(); } /** * ?? 1.2 * * @param magnification */ public void setMagnification(float magnification) { if (magnification == 1) showTextScale(false); else { this.magnification = magnification - 1; invalidate(); } } /** * ?Tab * * @param show */ public void showTabGradient(boolean show) { showTabColor = show; invalidate(); } /** * Tab * * @param colorNormal * * @param colorSelected * */ public void setTabGradient(int colorNormal, int colorSelected) { tabColorNormal = colorNormal; tabColorSelected = colorSelected; invalidate(); } /** * Adapter * * @param adapter */ public void setTagAdapter(TabTagAdapter adapter) { mTagAdapter = adapter; invalidate(); } }