package com.anthony.segmentcontrol; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.os.Build; import android.util.AttributeSet; import android.util.TypedValue; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import com.edu.anthony.segmentcontrol.R; /** * Created by 7heaven on 15/4/22. */ public class SegmentControl extends View { private String[] mTexts; private Rect[] mCacheBounds; private Rect[] mTextBounds; private RadiusDrawable mBackgroundDrawable; private RadiusDrawable mSelectedDrawable; private int mCurrentIndex; private int mTouchSlop; private boolean inTapRegion; private float mStartX; private float mStartY; private float mCurrentX; private float mCurrentY; private int mHorizonGap; private int mVerticalGap; /** 外边框的width */ private int mBoundWidth = 4; /** 内边框的width */ private int mSeparatorWidth = mBoundWidth / 2; private int mSingleChildWidth; private int mSingleChildHeight; private Paint mPaint; private int mTextSize; private ColorStateList mBackgroundColors; private ColorStateList mTextColors; private int mCornerRadius; private int DEFAULT_SELECTED_COLOR = 0xFF32ADFF; private int DEFAULT_NORMAL_COLOR = 0xFFFFFFFF; private Paint.FontMetrics mCachedFM; public enum Direction { HORIZONTAL(0), VERTICAL(1); int value; Direction(int v) { value = v; } } private Direction mDirection; public SegmentControl(Context context) { this(context, null); } public SegmentControl(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SegmentControl(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SegmentControl); String textArray = ta.getString(R.styleable.SegmentControl_texts); if (textArray != null) { mTexts = textArray.split("\\|"); } mTextSize = ta.getDimensionPixelSize(R.styleable.SegmentControl_android_textSize, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 14, context.getResources().getDisplayMetrics())); mCornerRadius = ta.getDimensionPixelSize(R.styleable.SegmentControl_cornerRadius, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, context.getResources().getDisplayMetrics())); mDirection = Direction.values()[ta.getInt(R.styleable.SegmentControl_block_direction, 0)]; mHorizonGap = ta.getDimensionPixelSize(R.styleable.SegmentControl_horizonGap, 0); mVerticalGap = ta.getDimensionPixelSize(R.styleable.SegmentControl_verticalGap, 0); int gap = ta.getDimensionPixelSize(R.styleable.SegmentControl_gaps, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, context.getResources().getDisplayMetrics())); if(mHorizonGap == 0) { mHorizonGap = gap; } if(mVerticalGap == 0) { mVerticalGap = gap; } mBackgroundDrawable = new RadiusDrawable(mCornerRadius, true); mBackgroundDrawable.setStrokeWidth(2); DEFAULT_NORMAL_COLOR = ta.getColor(R.styleable.SegmentControl_normalColor, DEFAULT_NORMAL_COLOR); DEFAULT_SELECTED_COLOR = ta.getColor(R.styleable.SegmentControl_sc_selectedColor, DEFAULT_SELECTED_COLOR); mBackgroundColors = ta.getColorStateList(R.styleable.SegmentControl_backgroundColors); mTextColors = ta.getColorStateList(R.styleable.SegmentControl_textColors); if (mBackgroundColors == null) { mBackgroundColors = new ColorStateList(new int[][]{{android.R.attr.state_selected}, {-android.R.attr.state_selected}}, new int[]{DEFAULT_SELECTED_COLOR, DEFAULT_NORMAL_COLOR}); } if(mTextColors == null){ mTextColors = new ColorStateList(new int[][]{{android.R.attr.state_selected}, {-android.R.attr.state_selected}}, new int[]{DEFAULT_NORMAL_COLOR, DEFAULT_SELECTED_COLOR}); } mBoundWidth = ta.getDimensionPixelSize(R.styleable.SegmentControl_boundWidth, mBoundWidth); mSeparatorWidth = ta.getDimensionPixelSize(R.styleable.SegmentControl_separatorWidth, mSeparatorWidth); ta.recycle(); mBackgroundDrawable = new RadiusDrawable(mCornerRadius, true); mBackgroundDrawable.setStrokeWidth(mBoundWidth); mBackgroundDrawable.setStrokeColor(getSelectedBGColor()); mBackgroundDrawable.setFillColor(getNormalBGColor()); if (Build.VERSION.SDK_INT < 16) { setBackgroundDrawable(mBackgroundDrawable); } else { setBackground(mBackgroundDrawable); } mSelectedDrawable = new RadiusDrawable(false); mSelectedDrawable.setFillColor(getSelectedBGColor()); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setTextSize(mTextSize); mCachedFM = mPaint.getFontMetrics(); int touchSlop = 0; if (context == null) { touchSlop = ViewConfiguration.getTouchSlop(); } else { final ViewConfiguration config = ViewConfiguration.get(context); touchSlop = config.getScaledTouchSlop(); } mTouchSlop = touchSlop * touchSlop; inTapRegion = false; } public void setText(String... texts) { mTexts = texts; if (mTexts != null) { requestLayout(); } } /** * 设置文字颜色 * * @param color 需要设置的颜色 */ public void setSelectedTextColors(ColorStateList color) { mTextColors = color; invalidate(); } /** * 设置背景颜色 * * @param colors 颜色 */ public void setColors(ColorStateList colors) { mBackgroundColors = colors; if (mBackgroundDrawable != null) { mBackgroundDrawable.setStrokeColor(getSelectedBGColor()); mBackgroundDrawable.setFillColor(getNormalBGColor()); } if (mSelectedDrawable != null) { mSelectedDrawable.setFillColor(getSelectedBGColor()); } invalidate(); } public void setCornerRadius(int cornerRadius) { mCornerRadius = cornerRadius; if (mBackgroundDrawable != null) { mBackgroundDrawable.setRadius(cornerRadius); } invalidate(); } public void setDirection(Direction direction) { Direction tDirection = mDirection; mDirection = direction; if (tDirection != direction) { requestLayout(); invalidate(); } } public void setTextSize(int textSize_sp) { setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize_sp); } public void setTextSize(int unit, int textSize) { mPaint.setTextSize((int) (TypedValue.applyDimension(unit, textSize, getContext().getResources().getDisplayMetrics()))); if (textSize != mTextSize) { mTextSize = textSize; mCachedFM = mPaint.getFontMetrics(); requestLayout(); } } public void setSelectedIndex(int index) { mCurrentIndex = index; invalidate(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int width = 0; int height = 0; if (mTexts != null && mTexts.length > 0) { mSingleChildHeight = 0; mSingleChildWidth = 0; if (mCacheBounds == null || mCacheBounds.length != mTexts.length) { mCacheBounds = new Rect[mTexts.length]; } if (mTextBounds == null || mTextBounds.length != mTexts.length) { mTextBounds = new Rect[mTexts.length]; } for (int i = 0; i < mTexts.length; i++) { String text = mTexts[i]; if(text != null){ if(mTextBounds[i] == null) { mTextBounds[i] = new Rect(); } mPaint.getTextBounds(text, 0, text.length(), mTextBounds[i]); if(mSingleChildWidth < mTextBounds[i].width() + mHorizonGap * 2) { mSingleChildWidth = mTextBounds[i].width() + mHorizonGap * 2; } if(mSingleChildHeight < mTextBounds[i].height() + mVerticalGap * 2) { mSingleChildHeight = mTextBounds[i].height() + mVerticalGap * 2; } } } switch (widthMode) { case MeasureSpec.AT_MOST: if (mDirection == Direction.HORIZONTAL) { if (widthSize <= mSingleChildWidth * mTexts.length) { mSingleChildWidth = widthSize / mTexts.length; width = widthSize; } else { width = mSingleChildWidth * mTexts.length; } } else { width = widthSize <= mSingleChildWidth ? widthSize : mSingleChildWidth; } break; case MeasureSpec.EXACTLY: width = widthSize; break; case MeasureSpec.UNSPECIFIED: default: if (mDirection == Direction.HORIZONTAL) { width = mSingleChildWidth * mTexts.length; } else { width = mSingleChildWidth; } break; } switch (heightMode) { case MeasureSpec.AT_MOST: if (mDirection == Direction.VERTICAL) { if (heightSize <= mSingleChildHeight * mTexts.length) { mSingleChildHeight = heightSize / mTexts.length; height = heightSize; } else { height = mSingleChildHeight * mTexts.length; } } else { height = heightSize <= mSingleChildHeight ? heightSize : mSingleChildHeight; } break; case MeasureSpec.EXACTLY: height = heightSize; break; case MeasureSpec.UNSPECIFIED: default: if (mDirection == Direction.VERTICAL) { height = mSingleChildHeight * mTexts.length; } else { height = mSingleChildHeight; } break; } switch (mDirection) { case HORIZONTAL: if (mSingleChildWidth != width / mTexts.length) { mSingleChildWidth = width / mTexts.length; } mSingleChildHeight = height; break; case VERTICAL: if(mSingleChildHeight != height / mTexts.length) { mSingleChildHeight = height / mTexts.length; } mSingleChildWidth = width; break; default: break; } for (int i = 0; i < mTexts.length; i++) { if (mCacheBounds[i] == null) { mCacheBounds[i] = new Rect(); } if (mDirection == Direction.HORIZONTAL) { mCacheBounds[i].left = i * mSingleChildWidth; mCacheBounds[i].top = 0; } else { mCacheBounds[i].left = 0; mCacheBounds[i].top = i * mSingleChildHeight; } mCacheBounds[i].right = mCacheBounds[i].left + mSingleChildWidth; mCacheBounds[i].bottom = mCacheBounds[i].top + mSingleChildHeight; } } else { width = widthMode == MeasureSpec.UNSPECIFIED ? 0 : widthSize; height = heightMode == MeasureSpec.UNSPECIFIED ? 0 : heightSize; } setMeasuredDimension(width, height); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: inTapRegion = true; mStartX = event.getX(); mStartY = event.getY(); break; case MotionEvent.ACTION_MOVE: mCurrentX = event.getX(); mCurrentY = event.getY(); int dx = (int) (mCurrentX - mStartX); int dy = (int) (mCurrentY - mStartY); int distance = dx * dx + dy * dy; if (distance > mTouchSlop) { inTapRegion = false; } break; case MotionEvent.ACTION_UP: if (inTapRegion) { int index = 0; if (mDirection == Direction.HORIZONTAL) { index = (int) (mStartX / mSingleChildWidth); } else { index = (int) (mStartY / mSingleChildHeight); } if(mOnSegmentControlClickListener != null) { mOnSegmentControlClickListener.onSegmentControlClick(index); } mCurrentIndex = index; invalidate(); } break; default: break; } return true; } private int getSelectedTextColor(){ return mTextColors.getColorForState(new int[]{android.R.attr.state_selected}, DEFAULT_NORMAL_COLOR); } private int getNormalTextColor(){ return mTextColors.getColorForState(new int[]{-android.R.attr.state_selected}, DEFAULT_SELECTED_COLOR); } private int getSelectedBGColor(){ return mBackgroundColors.getColorForState(new int[]{android.R.attr.state_selected}, DEFAULT_SELECTED_COLOR); } private int getNormalBGColor(){ return mBackgroundColors.getColorForState(new int[]{-android.R.attr.state_selected}, DEFAULT_NORMAL_COLOR); } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); if (mTexts != null && mTexts.length > 0) { for (int i = 0; i < mTexts.length; i++) { //draw separate lines if (i < mTexts.length - 1) { mPaint.setColor(getSelectedBGColor()); mPaint.setStrokeWidth(mSeparatorWidth); if (mDirection == Direction.HORIZONTAL) { canvas.drawLine(mCacheBounds[i].right, 0, mCacheBounds[i].right, getHeight(), mPaint); } else { canvas.drawLine(mCacheBounds[i].left, mSingleChildHeight * (i + 1), mCacheBounds[i].right, mSingleChildHeight * (i + 1), mPaint); } } //draw selected drawable if (i == mCurrentIndex && mSelectedDrawable != null) { int topLeftRadius = 0; int topRightRadius = 0; int bottomLeftRadius = 0; int bottomRightRadius = 0; if (mDirection == Direction.HORIZONTAL) { if (i == 0) { topLeftRadius = mCornerRadius; bottomLeftRadius = mCornerRadius; } else if (i == mTexts.length - 1) { topRightRadius = mCornerRadius; bottomRightRadius = mCornerRadius; } } else { if (i == 0) { topLeftRadius = mCornerRadius; topRightRadius = mCornerRadius; } else if (i == mTexts.length - 1) { bottomLeftRadius = mCornerRadius; bottomRightRadius = mCornerRadius; } } mSelectedDrawable.setRadius(topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); mSelectedDrawable.setBounds(mCacheBounds[i]); mSelectedDrawable.draw(canvas); mPaint.setColor(getSelectedTextColor()); } else { mPaint.setColor(getNormalTextColor()); } //draw texts float baseline = mCacheBounds[i].top + ((mSingleChildHeight - mCachedFM.ascent + mCachedFM.descent) / 2) - mCachedFM.descent; canvas.drawText(mTexts[i], mCacheBounds[i].left + (mSingleChildWidth - mTextBounds[i].width()) / 2, baseline, mPaint); } } } // ========================================================= // OnSegmentControlClickListener // ========================================================= private OnSegmentControlClickListener mOnSegmentControlClickListener; public void setOnSegmentControlClickListener(OnSegmentControlClickListener listener) { mOnSegmentControlClickListener = listener; } public OnSegmentControlClickListener getOnSegmentControlClicklistener() { return mOnSegmentControlClickListener; } public interface OnSegmentControlClickListener { void onSegmentControlClick(int index); } }