/*
* Copyright (C) 2011 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.marshalchen.common.uimodule.modifysys;
import android.content.Context;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.support.v4.view.PagerAdapter;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils.TruncateAt;
import android.text.style.ImageSpan;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.TextView;
import com.marshalchen.common.commonUtils.logUtils.Logs;
import com.marshalchen.ultimateandroiduicomponent.R;
import java.lang.ref.WeakReference;
/**
* PagerTitleStrip is a non-interactive indicator of the current, next,
* and previous pages of a {@link android.support.v4.view.ViewPager}. It is intended to be used as a
* child view of a ViewPager widget in your XML layout.
* Add it as a child of a ViewPager in your layout file and set its
* android:layout_gravity to TOP or BOTTOM to pin it to the top or bottom
* of the ViewPager. The title from each page is supplied by the method
* {@link android.support.v4.view.PagerAdapter#getPageTitle(int)} in the adapter supplied to
* the ViewPager.
* <p/>
* <p>For an interactive indicator, see {@link android.support.v4.view.PagerTabStrip}.</p>
*/
public class PagerTitleStrip extends ViewGroup implements ViewPager.Decor {
private static final String TAG = "PagerTitleStrip";
ViewPager mPager;
TextView mPrevText;
TextView mCurrText;
TextView mNextText;
private int mLastKnownCurrentPage = -1;
private float mLastKnownPositionOffset = -1;
private int mScaledTextSpacing;
private int mGravity;
private boolean mUpdatingText;
private boolean mUpdatingPositions;
private final PageListener mPageListener = new PageListener();
private WeakReference<PagerAdapter> mWatchingAdapter;
private static final int[] ATTRS = new int[]{
android.R.attr.textAppearance,
android.R.attr.textSize,
android.R.attr.textColor,
android.R.attr.gravity
};
private static final int[] TEXT_ATTRS = new int[]{
0x0101038c // android.R.attr.textAllCaps
};
private static final float SIDE_ALPHA = 0.6f;
private static final int TEXT_SPACING = 16; // dip
private int mNonPrimaryAlpha;
int mTextColor;
interface PagerTitleStripImpl {
void setSingleLineAllCaps(TextView text);
}
static class PagerTitleStripImplBase implements PagerTitleStripImpl {
public void setSingleLineAllCaps(TextView text) {
text.setSingleLine();
}
}
static class PagerTitleStripImplIcs implements PagerTitleStripImpl {
public void setSingleLineAllCaps(TextView text) {
PagerTitleStripIcs.setSingleLineAllCaps(text);
}
}
private static final PagerTitleStripImpl IMPL;
static {
if (android.os.Build.VERSION.SDK_INT >= 14) {
IMPL = new PagerTitleStripImplIcs();
} else {
IMPL = new PagerTitleStripImplBase();
}
}
private static void setSingleLineAllCaps(TextView text) {
IMPL.setSingleLineAllCaps(text);
}
public PagerTitleStrip(Context context) {
this(context, null);
}
public PagerTitleStrip(Context context, AttributeSet attrs) {
super(context, attrs);
addView(mPrevText = new TextView(context));
addView(mCurrText = new TextView(context));
addView(mNextText = new TextView(context));
final TypedArray a = context.obtainStyledAttributes(attrs, ATTRS);
final int textAppearance = a.getResourceId(0, 0);
if (textAppearance != 0) {
mPrevText.setTextAppearance(context, textAppearance);
mCurrText.setTextAppearance(context, textAppearance);
mNextText.setTextAppearance(context, textAppearance);
}
final int textSize = a.getDimensionPixelSize(1, 0);
if (textSize != 0) {
setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
}
if (a.hasValue(2)) {
final int textColor = a.getColor(2, 0);
mPrevText.setTextColor(textColor);
mCurrText.setTextColor(textColor);
mNextText.setTextColor(textColor);
}
mGravity = a.getInteger(3, Gravity.CENTER);
a.recycle();
mTextColor = mCurrText.getTextColors().getDefaultColor();
setNonPrimaryAlpha(SIDE_ALPHA);
mPrevText.setEllipsize(TruncateAt.END);
mCurrText.setEllipsize(TruncateAt.END);
mNextText.setEllipsize(TruncateAt.END);
boolean allCaps = false;
if (textAppearance != 0) {
final TypedArray ta = context.obtainStyledAttributes(textAppearance, TEXT_ATTRS);
allCaps = ta.getBoolean(0, false);
ta.recycle();
}
if (allCaps) {
setSingleLineAllCaps(mPrevText);
setSingleLineAllCaps(mCurrText);
setSingleLineAllCaps(mNextText);
} else {
mPrevText.setSingleLine();
mCurrText.setSingleLine();
mNextText.setSingleLine();
}
final float density = context.getResources().getDisplayMetrics().density;
mScaledTextSpacing = (int) (TEXT_SPACING * density);
}
/**
* Set the required spacing between title segments.
*
* @param spacingPixels Spacing between each title displayed in pixels
*/
public void setTextSpacing(int spacingPixels) {
mScaledTextSpacing = spacingPixels;
requestLayout();
}
/**
* @return The required spacing between title segments in pixels
*/
public int getTextSpacing() {
return mScaledTextSpacing;
}
/**
* Set the alpha value used for non-primary page titles.
*
* @param alpha Opacity value in the range 0-1f
*/
public void setNonPrimaryAlpha(float alpha) {
mNonPrimaryAlpha = (int) (alpha * 255) & 0xFF;
final int transparentColor = (mNonPrimaryAlpha << 24) | (mTextColor & 0xFFFFFF);
mPrevText.setTextColor(transparentColor);
mNextText.setTextColor(transparentColor);
}
/**
* Set the color value used as the base color for all displayed page titles.
* Alpha will be ignored for non-primary page titles. See {@link #setNonPrimaryAlpha(float)}.
*
* @param color Color hex code in 0xAARRGGBB format
*/
public void setTextColor(int color) {
mTextColor = color;
mCurrText.setTextColor(color);
final int transparentColor = (mNonPrimaryAlpha << 24) | (mTextColor & 0xFFFFFF);
mPrevText.setTextColor(transparentColor);
mNextText.setTextColor(transparentColor);
}
public void setTextColor(final int color, final int transColor) {
mTextColor = color;
mCurrText.setTextColor(color);
final int transparentColor = (mNonPrimaryAlpha << 24) | (mTextColor & 0xFFFFFF);
mPrevText.setTextColor(transColor);
mNextText.setTextColor(transColor);
// new Thread(){
// /**
// * Calls the <code>run()</code> method of the Runnable object the receiver
// * holds. If no Runnable is set, does nothing.
// *
// * @see Thread#start
// */
// @Override
// public void run() {
// super.run();
// try {
// sleep(300);
// Bundle bundle=new Bundle();
// bundle.putInt("color",color);
// bundle.putInt("transColor",transColor);
// HandlerUtils.sendMessageHandler(textColorHandler,0,bundle);
// } catch (InterruptedException e) {
// e.printStackTrace();
// Logs.e(e, "");
// }
// }
// }.start();;
}
// Handler textColorHandler=new Handler(){
// /**
// * Subclasses must implement this to receive messages.
// *
// * @param msg
// */
// @Override
// public void handleMessage(Message msg) {
// super.handleMessage(msg);
// Bundle bundle=msg.getData();
// mCurrText.setTextColor(bundle.getInt("color"));
// final int transparentColor = (mNonPrimaryAlpha << 24) | (mTextColor & 0xFFFFFF);
// mPrevText.setTextColor(bundle.getInt("transColor"));
// mNextText.setTextColor(bundle.getInt("transColor"));
// }
// };
/**
* Set the default text size to a given unit and value.
* See {@link android.util.TypedValue} for the possible dimension units.
* <p/>
* <p>Example: to set the text size to 14px, use
* setTextSize(TypedValue.COMPLEX_UNIT_PX, 14);</p>
*
* @param unit The desired dimension unit
* @param size The desired size in the given units
*/
public void setTextSize(int unit, float size) {
mPrevText.setTextSize(unit, size);
mCurrText.setTextSize(unit, size);
mNextText.setTextSize(unit, size);
}
/**
* Set the {@link android.view.Gravity} used to position text within the title strip.
* Only the vertical gravity component is used.
*
* @param gravity {@link android.view.Gravity} constant for positioning title text
*/
public void setGravity(int gravity) {
mGravity = gravity;
requestLayout();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
final ViewParent parent = getParent();
if (!(parent instanceof ViewPager)) {
throw new IllegalStateException(
"PagerTitleStrip 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 updateText(final int currentItem, PagerAdapter adapter) {
final int itemCount = adapter != null ? adapter.getCount() : 0;
mUpdatingText = true;
CharSequence text = null;
if (currentItem >= 1 && adapter != null) {
text = adapter.getPageTitle(currentItem - 1);
}
Logs.d("text---" + text + " " + currentItem);
//if (text!=null){
mPrevText.setText(text);
if (currentItem == 1) {
if (text != null)
mPrevText.setText(changeSpanString(text.toString(), 0, 2));
}
// }
mCurrText.setText(adapter != null && currentItem < itemCount ?
changeSpanString(adapter.getPageTitle(currentItem).toString(), currentItem, 1) : null);
// if (adapter != null && currentItem < itemCount){
// final String tempString=adapter.getPageTitle(currentItem).toString();
// final int tempInt=currentItem;
// new Thread() {
// /**
// * Calls the <code>run()</code> method of the Runnable object the receiver
// * holds. If no Runnable is set, does nothing.
// *
// * @see Thread#start
// */
// @Override
// public void run() {
// super.run();
// try {
// sleep(1000);
// Bundle bundle=new Bundle();
// bundle.putString("string",tempString);
// bundle.putInt("int",tempInt);
// HandlerUtils.sendMessageHandler(textHandler,1,bundle);
// } catch (InterruptedException e) {
// e.printStackTrace();
// Logs.e(e, "");
// }
// }
// }.start();
// }else{
// HandlerUtils.sendMessageHandler(textHandler,2);
// }
text = null;
if (currentItem + 1 < itemCount && adapter != null) {
text = adapter.getPageTitle(currentItem + 1);
}
Logs.d("text---" + text + " " + currentItem);
//if (text!=null){
mNextText.setText(text);
if (currentItem == 0) {
if (text != null)
mNextText.setText(changeSpanString(text.toString(), 1, 2));
}
// }
// Measure everything
final int width = getWidth() - getPaddingLeft() - getPaddingRight();
final int childHeight = getHeight() - getPaddingTop() - getPaddingBottom();
final int childWidthSpec = MeasureSpec.makeMeasureSpec((int) (width * 0.8f),
MeasureSpec.AT_MOST);
final int childHeightSpec = MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.AT_MOST);
mPrevText.measure(childWidthSpec, childHeightSpec);
mCurrText.measure(childWidthSpec, childHeightSpec);
mNextText.measure(childWidthSpec, childHeightSpec);
mLastKnownCurrentPage = currentItem;
if (!mUpdatingPositions) {
updateTextPositions(currentItem, mLastKnownPositionOffset, false);
}
mUpdatingText = false;
}
// Handler textHandler=new Handler(){
// /**
// * Subclasses must implement this to receive messages.
// *
// * @param msg
// */
// @Override
// public void handleMessage(Message msg) {
// super.handleMessage(msg);
// if (msg.what==1){
// Bundle bundle=msg.getData();
// mCurrText.setText(
// changeSpanString(bundle.getString("string"), bundle.getInt("int"), 1) );
// }else{
// mCurrText.setText(null);
// }
// }
// };
SpannableString changeSpanString(String temp, int position, int color) {
try {
temp = temp.replaceAll("-", " - ");
SpannableString ss = new SpannableString(temp);
Matrix matrix = new Matrix();
matrix.postRotate(180);
Bitmap bitmaporg = BitmapFactory.decodeResource(getResources(),
R.drawable.ico_arrow2x);
if (color == 2) {
bitmaporg = BitmapFactory.decodeResource(getResources(),
R.drawable.ico_arrow_grey2x);
}
Bitmap resizedBitmap = Bitmap.createBitmap(bitmaporg, 0, 0,
bitmaporg.getWidth(), bitmaporg.getHeight(), matrix, true);
BitmapDrawable bmd = new BitmapDrawable(getResources(), resizedBitmap);
BitmapDrawable bm = new BitmapDrawable(getResources(), bitmaporg);
Drawable drawable = position == 0 ? bm :
bmd;
drawable.setBounds(0, 0,
drawable.getIntrinsicWidth() * 12 / 10,
drawable.getIntrinsicHeight() * 12 / 10);
ImageSpan imageSpan = new ImageSpan(drawable, ImageSpan.ALIGN_BASELINE);
ss.setSpan(imageSpan, temp.indexOf("-"), temp.indexOf("-") + 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
return ss;
} catch (Exception e) {
e.printStackTrace();
Logs.e(e, "");
return null;
}
}
@Override
public void requestLayout() {
if (!mUpdatingText) {
super.requestLayout();
}
}
void updateAdapter(PagerAdapter oldAdapter, PagerAdapter newAdapter) {
if (oldAdapter != null) {
oldAdapter.unregisterDataSetObserver(mPageListener);
mWatchingAdapter = null;
}
if (newAdapter != null) {
newAdapter.registerDataSetObserver(mPageListener);
mWatchingAdapter = new WeakReference<PagerAdapter>(newAdapter);
}
if (mPager != null) {
mLastKnownCurrentPage = -1;
mLastKnownPositionOffset = -1;
updateText(mPager.getCurrentItem(), newAdapter);
requestLayout();
}
}
void updateTextPositions(int position, float positionOffset, boolean force) {
if (position != mLastKnownCurrentPage) {
updateText(position, mPager.getAdapter());
} else if (!force && positionOffset == mLastKnownPositionOffset) {
return;
}
mUpdatingPositions = true;
final int prevWidth = mPrevText.getMeasuredWidth();
final int currWidth = mCurrText.getMeasuredWidth();
final int nextWidth = mNextText.getMeasuredWidth();
final int halfCurrWidth = currWidth / 2;
final int stripWidth = getWidth();
final int stripHeight = getHeight();
final int paddingLeft = getPaddingLeft();
final int paddingRight = getPaddingRight();
final int paddingTop = getPaddingTop();
final int paddingBottom = getPaddingBottom();
final int textPaddedLeft = paddingLeft + halfCurrWidth;
final int textPaddedRight = paddingRight + halfCurrWidth;
final int contentWidth = stripWidth - textPaddedLeft - textPaddedRight;
float currOffset = positionOffset + 0.5f;
if (currOffset > 1.f) {
currOffset -= 1.f;
}
final int currCenter = stripWidth - textPaddedRight - (int) (contentWidth * currOffset);
final int currLeft = currCenter - currWidth / 2;
final int currRight = currLeft + currWidth;
final int prevBaseline = mPrevText.getBaseline();
final int currBaseline = mCurrText.getBaseline();
final int nextBaseline = mNextText.getBaseline();
final int maxBaseline = Math.max(Math.max(prevBaseline, currBaseline), nextBaseline);
final int prevTopOffset = maxBaseline - prevBaseline;
final int currTopOffset = maxBaseline - currBaseline;
final int nextTopOffset = maxBaseline - nextBaseline;
final int alignedPrevHeight = prevTopOffset + mPrevText.getMeasuredHeight();
final int alignedCurrHeight = currTopOffset + mCurrText.getMeasuredHeight();
final int alignedNextHeight = nextTopOffset + mNextText.getMeasuredHeight();
final int maxTextHeight = Math.max(Math.max(alignedPrevHeight, alignedCurrHeight),
alignedNextHeight);
final int vgrav = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
int prevTop;
int currTop;
int nextTop;
switch (vgrav) {
default:
case Gravity.TOP:
prevTop = paddingTop + prevTopOffset;
currTop = paddingTop + currTopOffset;
nextTop = paddingTop + nextTopOffset;
break;
case Gravity.CENTER_VERTICAL:
final int paddedHeight = stripHeight - paddingTop - paddingBottom;
final int centeredTop = (paddedHeight - maxTextHeight) / 2;
prevTop = centeredTop + prevTopOffset;
currTop = centeredTop + currTopOffset;
nextTop = centeredTop + nextTopOffset;
break;
case Gravity.BOTTOM:
final int bottomGravTop = stripHeight - paddingBottom - maxTextHeight;
prevTop = bottomGravTop + prevTopOffset;
currTop = bottomGravTop + currTopOffset;
nextTop = bottomGravTop + nextTopOffset;
break;
}
mCurrText.layout(currLeft, currTop, currRight,
currTop + mCurrText.getMeasuredHeight());
final int prevLeft = Math.min(paddingLeft, currLeft - mScaledTextSpacing - prevWidth);
mPrevText.layout(prevLeft, prevTop, prevLeft + prevWidth,
prevTop + mPrevText.getMeasuredHeight());
final int nextLeft = Math.max(stripWidth - paddingRight - nextWidth,
currRight + mScaledTextSpacing);
mNextText.layout(nextLeft, nextTop, nextLeft + nextWidth,
nextTop + mNextText.getMeasuredHeight());
mLastKnownPositionOffset = positionOffset;
mUpdatingPositions = false;
}
@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) {
throw new IllegalStateException("Must measure with an exact width");
}
int childHeight = heightSize;
int minHeight = getMinHeight();
int padding = 0;
padding = getPaddingTop() + getPaddingBottom();
childHeight -= padding;
final int childWidthSpec = MeasureSpec.makeMeasureSpec((int) (widthSize * 0.8f),
MeasureSpec.AT_MOST);
final int childHeightSpec = MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.AT_MOST);
mPrevText.measure(childWidthSpec, childHeightSpec);
mCurrText.measure(childWidthSpec, childHeightSpec);
mNextText.measure(childWidthSpec, childHeightSpec);
if (heightMode == MeasureSpec.EXACTLY) {
setMeasuredDimension(widthSize, heightSize * 7 / 5);
} else {
int textHeight = mCurrText.getMeasuredHeight();
setMeasuredDimension(widthSize, Math.max(minHeight, textHeight + padding) * 7 / 5);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (mPager != null) {
final float offset = mLastKnownPositionOffset >= 0 ? mLastKnownPositionOffset : 0;
updateTextPositions(mLastKnownCurrentPage, offset, true);
}
}
int getMinHeight() {
int minHeight = 0;
final Drawable bg = getBackground();
if (bg != null) {
minHeight = bg.getIntrinsicHeight();
}
return minHeight;
}
Handler pageHandler = new Handler() {
/**
* Subclasses must implement this to receive messages.
*
* @param msg
*/
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
updateTextPositions(msg.getData().getInt("position"), msg.getData().getFloat("positionOffset"), false);
}
};
private class PageListener extends DataSetObserver implements ViewPager.OnPageChangeListener,
ViewPager.OnAdapterChangeListener {
private int mScrollState;
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (positionOffset > 0.5f) {
// Consider ourselves to be on the next page when we're 50% of the way there.
position++;
}
updateTextPositions(position, positionOffset, true);
}
@Override
public void onPageSelected(int position) {
if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
// Only update the text here if we're not dragging or settling.
Logs.d("select------");
updateText(mPager.getCurrentItem(), mPager.getAdapter());
final float offset = mLastKnownPositionOffset >= 0 ? mLastKnownPositionOffset : 0;
updateTextPositions(mPager.getCurrentItem(), offset, true);
// Message message = new Message();
// message.what = 0;
// Bundle bundle=new Bundle();
// bundle.putInt("position",mPager.getCurrentItem());
// bundle.putFloat("positionOffset", offset);
// pageHandler.sendMessageDelayed(message,1000);
}
}
@Override
public void onPageScrollStateChanged(int state) {
mScrollState = state;
}
@Override
public void onAdapterChanged(PagerAdapter oldAdapter, PagerAdapter newAdapter) {
updateAdapter(oldAdapter, newAdapter);
}
@Override
public void onChanged() {
updateText(mPager.getCurrentItem(), mPager.getAdapter());
Logs.d("select------");
final float offset = mLastKnownPositionOffset >= 0 ? mLastKnownPositionOffset : 0;
updateTextPositions(mPager.getCurrentItem(), offset, true);
}
}
}