/* * 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.uimodule.R; import com.marshalchen.common.commonUtils.logUtils.Logs; 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); } } }