SegmentControl的实现与使用

来源:互联网 发布:mac文档在哪里 编辑:程序博客网 时间:2024/05/29 12:38

发现问题

Android中并没有分段选择器这一控件,如果我们想通过点击而不是滑动的方式来控制碎片的切换,我们可以自己实现SegmentControl。

SegmentControl的实现

res/values/attras

<?xml version="1.0" encoding="utf-8"?><resources>    <declare-styleable name="SegmentControl">        <attr name="cornerRadius" format="dimension|reference" />        <attr name="colors" format="color|reference" />        <attr name="texts" format="string|reference" />        <attr name="android:textSize" />        <attr name="direction" format="enum">            <enum name="horizon" value="0" />            <enum name="vertical" value="1" />        </attr>        <attr name="gaps" format="dimension|reference" />        <attr name="horizonGap" format="dimension|reference" />        <attr name="verticalGap" format="dimension|reference" />    </declare-styleable></resources>

SegmentControl.java

import com.app.cloud.R;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;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;    private int mCenterX;    private int mCenterY;    private int mChildrenWidth;    private int mChildrenHeight;    private int mSingleChildWidth;    private int mSingleChildHeight;    private Paint mPaint;    public enum Direction {        HORIZON(0), VERTICAL(1);        int mV;        private Direction(int v) {            mV = v;        }    }    private Direction mDirection;    private int mTextSize;    private ColorStateList mColors;    private int mCornerRadius;    public interface OnSegmentControlClickListener {        public void onSegmentControlClick(int index);    }    private OnSegmentControlClickListener mOnSegmentControlClickListener;    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()));        mColors = ta.getColorStateList(R.styleable.SegmentControl_colors);        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_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;        ta.recycle();        mBackgroundDrawable = new RadiusDrawable(mCornerRadius, true, 0);        mBackgroundDrawable.setStrokeWidth(2);        if (mColors == null) {            mColors = new ColorStateList(new int[][] { {} }, new int[] { 0xFF0099CC });        }        mBackgroundDrawable.setStrokeColor(mColors.getDefaultColor());        if (Build.VERSION.SDK_INT < 16) {            setBackgroundDrawable(mBackgroundDrawable);        } else {            setBackground(mBackgroundDrawable);        }        mSelectedDrawable = new RadiusDrawable(mCornerRadius, false, mColors.getDefaultColor());        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);        mPaint.setTextSize(mTextSize);        mPaint.setColor(mColors.getDefaultColor());        // here's the tricky thing, when you doing a click detect on a        // capacitive touch screen,        // sometimes the touch points of touchDown and touchUp are        // different(it's call slop) even when you didn't actually move your        // finger,        // so we set a distance limit for the distance of this two touch points        // to create a better user experience;        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 setOnSegmentControlClickListener(OnSegmentControlClickListener onSegmentControlClickListener) {        mOnSegmentControlClickListener = onSegmentControlClickListener;    }    public OnSegmentControlClickListener getOnSegmentControlClicklistener() {        return mOnSegmentControlClickListener;    }    public void setText(String... texts) {        mTexts = texts;        if (mTexts != null) {            requestLayout();        }    }    public void setColors(ColorStateList colors) {        mColors = colors;        if (mBackgroundDrawable != null) {            mBackgroundDrawable.setStrokeColor(colors.getDefaultColor());        }        if (mSelectedDrawable != null) {            mSelectedDrawable.setColor(colors.getDefaultColor());        }        mPaint.setColor(colors.getDefaultColor());        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) {        setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize);    }    public void setTextSize(int unit, int textSize) {        mPaint.setTextSize(                (int) (TypedValue.applyDimension(unit, textSize, getContext().getResources().getDisplayMetrics())));        if (textSize != mTextSize) {            mTextSize = textSize;            requestLayout();        }    }    @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) {            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;                }            }            for (int i = 0; i < mTexts.length; i++) {                if (mCacheBounds[i] == null)                    mCacheBounds[i] = new Rect();                if (mDirection == Direction.HORIZON) {                    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;            }            switch (widthMode) {            case MeasureSpec.AT_MOST:                if (mDirection == Direction.HORIZON) {                    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:                if (mDirection == Direction.HORIZON) {                    width = mSingleChildWidth * mTexts.length;                } else {                    width = widthSize <= mSingleChildWidth ? widthSize : 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:                if (mDirection == Direction.VERTICAL) {                    height = mSingleChildHeight * mTexts.length;                } else {                    height = heightSize <= mSingleChildHeight ? heightSize : mSingleChildHeight;                }                break;            }            mChildrenWidth = mDirection == Direction.HORIZON ? mSingleChildWidth * mTexts.length : mSingleChildWidth;            mChildrenHeight = mDirection == Direction.VERTICAL ? mSingleChildHeight * mTexts.length                    : mSingleChildHeight;        } else {            width = widthMode == MeasureSpec.UNSPECIFIED ? 0 : widthSize;            height = heightMode == MeasureSpec.UNSPECIFIED ? 0 : heightSize;        }        mCenterX = width / 2;        mCenterY = height / 2;        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.HORIZON) {                    index = (int) (mStartX / mSingleChildWidth);                } else {                    index = (int) (mStartY / mSingleChildHeight);                }                if (mOnSegmentControlClickListener != null)                    mOnSegmentControlClickListener.onSegmentControlClick(index);                mCurrentIndex = index;                invalidate();            }            break;        }        return true;    }    public void setSelectedIndex(int index) {        mCurrentIndex = index;        invalidate();    }    @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(mColors.getDefaultColor());                    if (mDirection == Direction.HORIZON) {                        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.HORIZON) {                        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.setRadiuses(topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);                    mSelectedDrawable.setBounds(mCacheBounds[i]);                    mSelectedDrawable.draw(canvas);                    mPaint.setColor(0xFFFFFFFF);                } else {                    mPaint.setColor(mColors.getDefaultColor());                }                // draw texts                canvas.drawText(mTexts[i], mCacheBounds[i].left + (mSingleChildWidth - mTextBounds[i].width()) / 2,                        mCacheBounds[i].top + ((mSingleChildHeight + mTextBounds[i].height()) / 2), mPaint);            }        }    }}

SegmentControl的使用

<com.app.cloud.Ui.SegmentControl        xmlns:segmentcontrol="http://schemas.android.com/apk/res-auto"        android:id="@+id/segment_control"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="center"        android:layout_margin="8dp"        android:textSize="15sp"        segmentcontrol:colors="#1E90FF"        segmentcontrol:cornerRadius="5dip"        segmentcontrol:horizonGap="35dip"        segmentcontrol:texts="全国|同城|个人"        segmentcontrol:verticalGap="10dip" />
SegmentControl segment;segment = (SegmentControl) view.findViewById(R.id.segment_control);segment.setOnSegmentControlClickListener(new OnSegmentControlClickListener() {    @Override    public void onSegmentControlClick(int index) {        //TODO index点击下标事件    }});

效果

分段选择