package so.orion.slidebar; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.graphics.drawable.StateListDrawable; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.animation.LinearInterpolator; /** * 项目名称:GBSlideBar * 类描述: * 创建人:Edanel * 创建时间:16/1/14 下午4:48 * 修改人:Edanel * 修改时间:16/1/14 下午4:48 * 修改备注: */ public class GBSlideBar extends View { private static final String TAG = "edanelx"; private RectF mBackgroundPaddingRect; private Drawable mBackgroundDrawable; private boolean mFirstDraw = true; private GBSlideBarAdapter mAdapter; private int[][] mAnchor; private boolean mModIsHorizontal = true; private int mCurrentX, mCurrentY, mPivotX, mPivotY; private boolean mSlide = false; private static final int[] STATE_NORMAL = new int[]{}; private static final int[] STATE_SELECTED = new int[]{android.R.attr.state_selected}; private static final int[] STATE_PRESS = new int[]{android.R.attr.state_pressed}; private int[] mState = STATE_SELECTED; private int mCurrentItem; private int mAnchorWidth, mAnchorHeight; private int mPlaceHolderWidth, mPlaceHolderHeight; private int mTextMargin; private int mType; private Paint mPaint; private int mTextSize; private int mTextColor; private int mLastX; private int mSlideX, mSlideY; private int mAbsoluteY; private int mSelectedX; private boolean mIsStartAnimation = false, mIsEndAnimation = false; private ValueAnimator mStartAnim, mEndAnim; private boolean mIsFirstSelect = true, mCanSelect = true; private GBSlideBarListener gbSlideBarListener; public GBSlideBar(Context context) { super(context); init(null, 0); } public GBSlideBar(Context context, AttributeSet attrs) { super(context, attrs); init(attrs, 0); } public GBSlideBar(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(attrs, defStyleAttr); } private void init(AttributeSet attributeSet, int defStyleAttr) { mBackgroundPaddingRect = new RectF(); TypedArray a = getContext().obtainStyledAttributes(attributeSet, R.styleable.GBSlideBar, defStyleAttr, 0); mBackgroundPaddingRect.left = a.getDimension(R.styleable.GBSlideBar_gbs_paddingLeft, 0.0f); mBackgroundPaddingRect.top = a.getDimension(R.styleable.GBSlideBar_gbs_paddingTop, 0.0f); mBackgroundPaddingRect.right = a.getDimension(R.styleable.GBSlideBar_gbs_paddingRight, 0.0f); mBackgroundPaddingRect.bottom = a.getDimension(R.styleable.GBSlideBar_gbs_paddingBottom, 0.0f); mAnchorWidth = (int) a.getDimension(R.styleable.GBSlideBar_gbs_anchor_width, 50.0f); mAnchorHeight = (int) a.getDimension(R.styleable.GBSlideBar_gbs_anchor_height, 50.0f); mPlaceHolderWidth = (int) a.getDimension(R.styleable.GBSlideBar_gbs_placeholder_width, 20.0f); mPlaceHolderHeight = (int) a.getDimension(R.styleable.GBSlideBar_gbs_placeholder_height, 20.0f); mType = a.getInt(R.styleable.GBSlideBar_gbs_type, 1); mBackgroundDrawable = a.getDrawable(R.styleable.GBSlideBar_gbs_background); mTextSize = a.getDimensionPixelSize(R.styleable.GBSlideBar_gbs_textSize, 28); mTextColor = a.getColor(R.styleable.GBSlideBar_gbs_textColor, Color.BLACK); mTextMargin = (int) a.getDimension(R.styleable.GBSlideBar_gbs_text_margin, 0.0f); a.recycle(); } private void drawBackground() { Rect rect = new Rect((int) mBackgroundPaddingRect.left + mAnchorWidth, (int) mBackgroundPaddingRect.top, (int) (getWidth() - mBackgroundPaddingRect.right - mAnchorWidth), (int) (getHeight() - mBackgroundPaddingRect.bottom)); mBackgroundDrawable.setBounds(rect); mAbsoluteY = (int) (mBackgroundPaddingRect.top - mBackgroundPaddingRect.bottom); Log.d(TAG, "mAbsoluteY:" + mBackgroundPaddingRect.top + " : " + mBackgroundPaddingRect.bottom + " : " + (mBackgroundPaddingRect.top - mBackgroundPaddingRect.bottom)); mCurrentX = mPivotX = getWidth() / 2; mCurrentY = mPivotY = getHeight() / 2; int widthBase = rect.width() / getCount(); int widthHalf = widthBase / 2; int heightBase = rect.height() / getCount(); int heightHalf = heightBase / 2; mAnchor = new int[getCount()][2]; for (int i = 0, j = 1; i < getCount(); i++, j++) { // if (mType == 1) { if (i == 0) { mAnchor[i][0] = mModIsHorizontal ? rect.left : mPivotX; } else if (i == getCount() - 1) { mAnchor[i][0] = mModIsHorizontal ? rect.right : mPivotX; } else { mAnchor[i][0] = mModIsHorizontal ? widthBase * j - widthHalf + rect.left : mPivotX; } mAnchor[i][1] = !mModIsHorizontal ? heightBase * j - heightHalf + rect.top : mPivotY + mAbsoluteY / 2; // } } mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setTextSize(mTextSize); mPaint.setColor(mTextColor); mPaint.setTextAlign(Paint.Align.CENTER); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mFirstDraw) drawBackground(); if (mBackgroundDrawable != null) mBackgroundDrawable.draw(canvas); if (isInEditMode()) return; Drawable itemDefault, itemSlide; StateListDrawable stateListDrawable; if (!mSlide) { int distance, minIndex = 0, minDistance = Integer.MAX_VALUE; for (int i = 0; i < getCount(); i++) { distance = Math.abs(mModIsHorizontal ? mAnchor[i][0] - mCurrentX : mAnchor[i][1] - mCurrentY); if (minDistance > distance) { minIndex = i; minDistance = distance; } } setCurrentItem(minIndex); stateListDrawable = mAdapter.getItem(minIndex); } else { mSlide = false; mCurrentX = mAnchor[mCurrentItem][0]; mCurrentY = mAnchor[mCurrentItem][1]; if (mFirstDraw) { mSlideX = mLastX = mCurrentX; } stateListDrawable = mAdapter.getItem(mCurrentItem); mIsFirstSelect = true; } stateListDrawable.setState(mState); itemDefault = stateListDrawable.getCurrent(); for (int i = 0; i < getCount(); i++) { if (i == mCurrentItem) { // continue; // mPaint.setColor(mAdapter.getTextColor(mCurrentItem)); canvas.drawText(mAdapter.getText(i), mAnchor[i][0], mAnchor[i][1] + mAnchorHeight * 3 / 2 + mTextMargin, mPaint); }else { mPaint.setColor(mTextColor); canvas.drawText(mAdapter.getText(i), mAnchor[i][0], mAnchor[i][1] + mAnchorHeight * 3 / 2 + mTextMargin, mPaint); } stateListDrawable = mAdapter.getItem(i); stateListDrawable.setState(STATE_NORMAL); itemSlide = stateListDrawable.getCurrent(); itemSlide.setBounds( mAnchor[i][0] - mPlaceHolderWidth, mAnchor[i][1] - mPlaceHolderHeight, mAnchor[i][0] + mPlaceHolderWidth, mAnchor[i][1] + mPlaceHolderHeight ); itemSlide.draw(canvas); } itemDefault.setBounds( mSlideX - mAnchorWidth, mPivotY + mAbsoluteY / 2 - mAnchorHeight, mSlideX + mAnchorWidth, mPivotY + mAbsoluteY / 2 + mAnchorHeight ); itemDefault.draw(canvas); setFirstDraw(false); } private void endSlide() { if (mIsEndAnimation == false && mSlide) { mIsEndAnimation = true; mEndAnim = ValueAnimator.ofFloat(0.0f, 1.0f); mEndAnim.setDuration(200); mEndAnim.setInterpolator(new LinearInterpolator()); mEndAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mSlideX = (int) ((mCurrentX - mLastX) * animation.getAnimatedFraction() + mLastX); mSlideY = (int) (mCurrentY * animation.getAnimatedFraction()); invalidate(); } }); mEndAnim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mIsStartAnimation = false; mLastX = mCurrentX; mIsEndAnimation = false; mCanSelect = true; invalidate(); } }); mEndAnim.start(); } else { mLastX = mCurrentX; mSlideX = mCurrentX; invalidate(); } } private void startSlide() { if (mIsStartAnimation == false && !mSlide && mCanSelect) { mIsStartAnimation = true; mStartAnim = ValueAnimator.ofFloat(0.0f, 1.0f); mStartAnim.setDuration(200); mStartAnim.setInterpolator(new LinearInterpolator()); mStartAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mSlideX = (int) ((mCurrentX - mLastX) * animation.getAnimatedFraction() + mLastX); mSlideY = (int) (mCurrentY * animation.getAnimatedFraction()); invalidate(); } }); mStartAnim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mLastX = mCurrentX; mIsStartAnimation = false; mCanSelect = true; invalidate(); } }); mStartAnim.start(); } } @Override public boolean onTouchEvent(MotionEvent event) { if (mCanSelect) { int action = event.getAction(); //获取当前坐标 mCurrentX = mModIsHorizontal ? getNormalizedX(event) : mPivotX; mCurrentY = !mModIsHorizontal ? (int) event.getY() : mPivotY; mSlide = action == MotionEvent.ACTION_UP; if (!mSlide && mIsFirstSelect) { startSlide(); mIsFirstSelect = false; } else if (mIsStartAnimation == false && mIsEndAnimation == false) { endSlide(); } mState = action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL ? STATE_SELECTED : STATE_PRESS; switch (action) { case MotionEvent.ACTION_MOVE: // if (BuildConfig.DEBUG) Log.d(TAG, "Move " + event.getX()); return true; case MotionEvent.ACTION_DOWN: Log.d(TAG, "Down " + event.getX()); return true; case MotionEvent.ACTION_UP: Log.d(TAG, "Up " + event.getX()); mCanSelect = false; invalidate(); return true; } } return super.onTouchEvent(event); } @Override public boolean dispatchTouchEvent(MotionEvent event) { getParent().requestDisallowInterceptTouchEvent(true); return super.dispatchTouchEvent(event); } private int getNormalizedX(MotionEvent event) { return Math.min(Math.max((int) event.getX(), mAnchorWidth), getWidth() - mAnchorWidth); } private void setFirstDraw(boolean firstDraw) { mFirstDraw = firstDraw; } private int getCount() { return isInEditMode() ? 3 : mAdapter.getCount(); } private void setCurrentItem(int currentItem) { if (mCurrentItem != currentItem && gbSlideBarListener != null) { gbSlideBarListener.onPositionSelected(currentItem); } mCurrentItem = currentItem; } public void setAdapter(GBSlideBarAdapter adapter) { mAdapter = adapter; } public void setPosition(int position) { position = position < 0 ? 0 : position; position = position > mAdapter.getCount() ? mAdapter.getCount() - 1 : position; mCurrentItem = position; mSlide = true; invalidate(); } public void setOnGbSlideBarListener(GBSlideBarListener listener) { this.gbSlideBarListener = listener; } }