Android Switch控件介绍

来源:互联网 发布:url端口号 编辑:程序博客网 时间:2024/04/26 00:43
IOS有一种UISwitch控件,只有两个状态:on,off。如图所示


在Android4.0中也添加了一个类似的控件:Switch.如图所示

 
其类关系图如下:
java.lang.Object
   ↳ Android.view.View
    ↳ android.widget.TextView
    ↳android.widget.Button
    ↳android.widget.CompoundButton
    ↳android.widget.Switch
父类:compoundButton
Switch是一个可以再两种状态切换的开关控件。用户可以拖动来选择,也可以像选择复选框一样点击切换Switch的状态
在布局文件使用方法同TextView:
<Switch        android:id="@+id/switch_test"        android:layout_width="match_parent"        android:layout_height="match_parent" />
因为该组件继承自CompoundButton,在代码中可以通过实现CompoundButton.OnCheckedChangeListener接口,并实现其内部类的onCheckedChanged来监听状态变化。
public class MainActivity extends Activity {@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Switch switchTest = (Switch) findViewById(R.id.switch_test);switchTest.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {@Overridepublic void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {Toast.makeText(MainActivity.this, isChecked + "",Toast.LENGTH_SHORT).show();}});}}
在代码中也可以改变该组件的外观
setSwitchTextAppearance(Context context, int resid) 使用指定的资源id设置状态标签上的文字大小,类型,颜色等;
setSwitchTypeface(Typeface tf, int style)  使用指定的字体类型库内的指定类型来设置状态标签上的文字;
setSwitchTypeface(Typeface tf) 使用指定字体类型库内的固有类型来设置状态标签上的文字;
setTextOff(CharSequence textOff) 设置“关闭”状态标签文字;
setTextOn(CharSequence textOn) 设置“开启”状体标签文字;
父类内的setButtonDrawable(int resid) 用指定的资源id设置组件背景;
父类内的setButtonDrawable(Drawable d) 用可绘制对象设置组件背景;
android:textStyle 和android:typeface 与setSwitchTypeface(Typeface tf)对应;
如果想在2.2中使用Switch,需要自定义其属性,以下代码摘自网上:
public class Switch extends CompoundButton {    private static final int TOUCH_MODE_IDLE = 0;    private static final int TOUCH_MODE_DOWN = 1;    private static final int TOUCH_MODE_DRAGGING = 2;    // Enum for the "typeface" XML parameter.    private static final int SANS = 1;    private static final int SERIF = 2;    private static final int MONOSPACE = 3;    private Drawable mThumbDrawable;    private Drawable mTrackDrawable;    private int mThumbTextPadding;    private int mSwitchMinWidth;    private int mSwitchPadding;    private CharSequence mTextOn;    private CharSequence mTextOff;    private int mTouchMode;    private int mTouchSlop;    private float mTouchX;    private float mTouchY;    private VelocityTracker mVelocityTracker = VelocityTracker.obtain();    private int mMinFlingVelocity;    private float mThumbPosition;    private int mSwitchWidth;    private int mSwitchHeight;    private int mThumbWidth; // Does not include padding    private int mSwitchLeft;    private int mSwitchTop;    private int mSwitchRight;    private int mSwitchBottom;    private TextPaint mTextPaint;    private ColorStateList mTextColors;    private Layout mOnLayout;    private Layout mOffLayout;    private Context mContext;    @SuppressWarnings("hiding")    private final Rect mTempRect = new Rect();    private static final int[] CHECKED_STATE_SET = {        android.R.attr.state_checked    };    /**     * Construct a new Switch with default styling.     *     * @param context The Context that will determine this widget's theming.     */    public Switch(Context context) {        this(context, null);        mContext = context;    }    /**     * Construct a new Switch with default styling, overriding specific style     * attributes as requested.     *     * @param context The Context that will determine this widget's theming.     * @param attrs Specification of attributes that should deviate from default styling.     */    public Switch(Context context, AttributeSet attrs) {        this(context, attrs, R.attr.switchStyle);        mContext = context;    }    /**     * Construct a new Switch with a default style determined by the given theme attribute,     * overriding specific style attributes as requested.     *     * @param context The Context that will determine this widget's theming.     * @param attrs Specification of attributes that should deviate from the default styling.     * @param defStyle An attribute ID within the active theme containing a reference to the     *                 default style for this widget. e.g. android.R.attr.switchStyle.     */    public Switch(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        mContext = context;        mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);        Resources res = getResources();        mTextPaint.density = res.getDisplayMetrics().density;        //float scaledDensity = res.getDisplayMetrics().scaledDensity;        //mTextPaint.setCompatibilityScaling(res.getCompatibilityInfo().applicationScale);        TypedArray a = context.obtainStyledAttributes(attrs,                R.styleable.Switch, defStyle, 0);        mThumbDrawable = a.getDrawable(R.styleable.Switch_thumb);        mTrackDrawable = a.getDrawable(R.styleable.Switch_track);        mTextOn = a.getText(R.styleable.Switch_textOn);        mTextOff = a.getText(R.styleable.Switch_textOff);        mThumbTextPadding = a.getDimensionPixelSize(                R.styleable.Switch_thumbTextPadding, 0);        mSwitchMinWidth = a.getDimensionPixelSize(                R.styleable.Switch_switchMinWidth, 0);        mSwitchPadding = a.getDimensionPixelSize(                R.styleable.Switch_switchPadding, 0);        Log.d("SvenDebug", "mTextOn:" + mTextOn);        Log.d("SvenDebug", "mTextOff:" + mTextOff);        Log.d("SvenDebug", "mThumbTextPadding:" + mThumbTextPadding);        Log.d("SvenDebug", "mSwitchMinWidth:" + mSwitchMinWidth);        Log.d("SvenDebug", "mSwitchPadding:" + mSwitchPadding);        int appearance = a.getResourceId(                R.styleable.Switch_switchTextAppearance, 0);        if (appearance != 0) {            setSwitchTextAppearance(context, appearance);        }        a.recycle();        ViewConfiguration config = ViewConfiguration.get(context);        mTouchSlop = config.getScaledTouchSlop();        mMinFlingVelocity = config.getScaledMinimumFlingVelocity();        // Refresh display with current params        refreshDrawableState();        setChecked(isChecked());    }    /**     * Sets the switch text color, size, style, hint color, and highlight color     * from the specified TextAppearance resource.     */    public void setSwitchTextAppearance(Context context, int resid) {        mContext = context;        TypedArray appearance =                context.obtainStyledAttributes(resid,                        R.styleable.TextAppearance);        ColorStateList colors;        int ts;        colors = appearance.getColorStateList(R.styleable.                TextAppearance_textColor);        if (colors != null) {            mTextColors = colors;        } else {            // If no color set in TextAppearance, default to the view's textColor            mTextColors = getTextColors();        }        ts = appearance.getDimensionPixelSize(R.styleable.                TextAppearance_textSize, 0);        if (ts != 0) {            if (ts != mTextPaint.getTextSize()) {                mTextPaint.setTextSize(ts);                requestLayout();            }        }        int typefaceIndex, styleIndex;        typefaceIndex = appearance.getInt(R.styleable.                TextAppearance_typeface, -1);        styleIndex = appearance.getInt(R.styleable.                TextAppearance_textStyle, -1);        setSwitchTypefaceByIndex(typefaceIndex, styleIndex);        appearance.recycle();    }    private void setSwitchTypefaceByIndex(int typefaceIndex, int styleIndex) {        Typeface tf = null;        switch (typefaceIndex) {            case SANS:                tf = Typeface.SANS_SERIF;                break;            case SERIF:                tf = Typeface.SERIF;                break;            case MONOSPACE:                tf = Typeface.MONOSPACE;                break;        }        setSwitchTypeface(tf, styleIndex);    }    /**     * Sets the typeface and style in which the text should be displayed on the     * switch, and turns on the fake bold and italic bits in the Paint if the     * Typeface that you provided does not have all the bits in the     * style that you specified.     */    public void setSwitchTypeface(Typeface tf, int style) {        if (style > 0) {            if (tf == null) {                tf = Typeface.defaultFromStyle(style);            } else {                tf = Typeface.create(tf, style);            }            setSwitchTypeface(tf);            // now compute what (if any) algorithmic styling is needed            int typefaceStyle = tf != null ? tf.getStyle() : 0;            int need = style & ~typefaceStyle;            mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);            mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);        } else {            mTextPaint.setFakeBoldText(false);            mTextPaint.setTextSkewX(0);            setSwitchTypeface(tf);        }    }    /**     * Sets the typeface in which the text should be displayed on the switch.     * Note that not all Typeface families actually have bold and italic     * variants, so you may need to use     * {@link #setSwitchTypeface(Typeface, int)} to get the appearance     * that you actually want.     *     * @attr ref android.R.styleable#TextView_typeface     * @attr ref android.R.styleable#TextView_textStyle     */    public void setSwitchTypeface(Typeface tf) {        if (mTextPaint.getTypeface() != tf) {            mTextPaint.setTypeface(tf);            requestLayout();            invalidate();        }    }    /**     * Returns the text displayed when the button is in the checked state.     */    public CharSequence getTextOn() {        return mTextOn;    }    /**     * Sets the text displayed when the button is in the checked state.     */    public void setTextOn(CharSequence textOn) {        mTextOn = textOn;        requestLayout();    }    /**     * Returns the text displayed when the button is not in the checked state.     */    public CharSequence getTextOff() {        return mTextOff;    }    /**     * Sets the text displayed when the button is not in the checked state.     */    public void setTextOff(CharSequence textOff) {        mTextOff = textOff;        requestLayout();    }    @Override    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);        int widthSize = MeasureSpec.getSize(widthMeasureSpec);        int heightSize = MeasureSpec.getSize(heightMeasureSpec);        if (mOnLayout == null) {            mOnLayout = makeLayout(mTextOn);        }        if (mOffLayout == null) {            mOffLayout = makeLayout(mTextOff);        }        mTrackDrawable.getPadding(mTempRect);        final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth());        final int switchWidth = Math.max(mSwitchMinWidth,                maxTextWidth * 2 + mThumbTextPadding * 4 + mTempRect.left + mTempRect.right);        final int switchHeight = mTrackDrawable.getIntrinsicHeight();        mThumbWidth = maxTextWidth + mThumbTextPadding * 2;        switch (widthMode) {            case MeasureSpec.AT_MOST:                widthSize = Math.min(widthSize, switchWidth);                break;            case MeasureSpec.UNSPECIFIED:                widthSize = switchWidth;                break;            case MeasureSpec.EXACTLY:                // Just use what we were given                break;        }        switch (heightMode) {            case MeasureSpec.AT_MOST:                heightSize = Math.min(heightSize, switchHeight);                break;            case MeasureSpec.UNSPECIFIED:                heightSize = switchHeight;                break;            case MeasureSpec.EXACTLY:                // Just use what we were given                break;        }        mSwitchWidth = switchWidth;        mSwitchHeight = switchHeight;        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        final int measuredHeight = getMeasuredHeight();        if (measuredHeight < switchHeight) {            setMeasuredDimension(getMeasuredWidth(), switchHeight);        }    }   /* @Override    public boolean dispatchTouchEvent(MotionEvent event) {        Log.d("SvenDebug", "dispatchTouchEvent:action=" + event.getAction());        return false;    }*/    @Override    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {        Log.d("SvenDebug", "dispatchPopulateAccessibilityEvent");        populateAccessibilityEvent(event);        return false;    }    public void populateAccessibilityEvent(AccessibilityEvent event) {        if (isChecked()) {            CharSequence text = mOnLayout.getText();            if (TextUtils.isEmpty(text)) {                text = mContext.getString(R.string.switch_on);            }            event.getText().add(text);        } else {            CharSequence text = mOffLayout.getText();            if (TextUtils.isEmpty(text)) {                text = mContext.getString(R.string.switch_off);            }            event.getText().add(text);        }    }    private Layout makeLayout(CharSequence text) {        return new StaticLayout(text, mTextPaint,                (int) Math.ceil(Layout.getDesiredWidth(text, mTextPaint)),                Layout.Alignment.ALIGN_NORMAL, 1.f, 0, true);    }    /**     * @return true if (x, y) is within the target area of the switch thumb     */    private boolean hitThumb(float x, float y) {        mThumbDrawable.getPadding(mTempRect);        final int thumbTop = mSwitchTop - mTouchSlop;        final int thumbLeft = mSwitchLeft + (int) (mThumbPosition + 0.5f) - mTouchSlop;        final int thumbRight = thumbLeft + mThumbWidth +                mTempRect.left + mTempRect.right + mTouchSlop;        final int thumbBottom = mSwitchBottom + mTouchSlop;        return x > thumbLeft && x < thumbRight && y > thumbTop && y < thumbBottom;    }    @Override    public boolean onTouchEvent(MotionEvent ev) {        mVelocityTracker.addMovement(ev);        final int action = ev.getActionMasked();        Log.d("SvenDebug", "MotionEvent : " + action);        switch (action) {            case MotionEvent.ACTION_DOWN: {                Log.d("SvenDebug", "MotionEvent.ACTION_DOWN");                final float x = ev.getX();                final float y = ev.getY();                if (isEnabled() && hitThumb(x, y)) {                    Log.d("SvenDebug", "Enable in widget rect");                    mTouchMode = TOUCH_MODE_DOWN;                    mTouchX = x;                    mTouchY = y;                }                break;            }            case MotionEvent.ACTION_MOVE: {                Log.d("SvenDebug", "MotionEvent.ACTION_MOVE");                switch (mTouchMode) {                    case TOUCH_MODE_IDLE:                        // Didn't target the thumb, treat normally.                        break;                    case TOUCH_MODE_DOWN: {                        Log.d("SvenDebug", "TOUCH_MODE_DOWN:mTouchSlop = " + mTouchSlop);                        final float x = ev.getX();                        final float y = ev.getY();                        if (Math.abs(x - mTouchX) > mTouchSlop ||                                Math.abs(y - mTouchY) > mTouchSlop) {                            mTouchMode = TOUCH_MODE_DRAGGING;                            getParent().requestDisallowInterceptTouchEvent(true);                            mTouchX = x;                            mTouchY = y;                            return true;                        }                        break;                    }                    case TOUCH_MODE_DRAGGING: {                        Log.d("SvenDebug", "TOUCH_MODE_DRAGGING");                        final float x = ev.getX();                        final float dx = x - mTouchX;                        float newPos = Math.max(0,                                Math.min(mThumbPosition + dx, getThumbScrollRange()));                        if (newPos != mThumbPosition) {                            mThumbPosition = newPos;                            mTouchX = x;                            invalidate();                        }                        return true;                    }                }                break;            }            case MotionEvent.ACTION_UP:            case MotionEvent.ACTION_CANCEL: {                Log.d("SvenDebug", "MotionEvent.ACTION_UP|ACTION_CANCEL");                if (mTouchMode == TOUCH_MODE_DRAGGING) {                    stopDrag(ev);                    return true;                }                mTouchMode = TOUCH_MODE_IDLE;                mVelocityTracker.clear();                break;            }        }        return super.onTouchEvent(ev);    }    private void cancelSuperTouch(MotionEvent ev) {        MotionEvent cancel = MotionEvent.obtain(ev);        cancel.setAction(MotionEvent.ACTION_CANCEL);        super.onTouchEvent(cancel);        cancel.recycle();    }    /**     * Called from onTouchEvent to end a drag operation.     *     * @param ev Event that triggered the end of drag mode - ACTION_UP or ACTION_CANCEL     */    private void stopDrag(MotionEvent ev) {        mTouchMode = TOUCH_MODE_IDLE;        // Up and not canceled, also checks the switch has not been disabled during the drag        boolean commitChange = ev.getAction() == MotionEvent.ACTION_UP && isEnabled();        cancelSuperTouch(ev);        if (commitChange) {            boolean newState;            mVelocityTracker.computeCurrentVelocity(1000);            float xvel = mVelocityTracker.getXVelocity();            if (Math.abs(xvel) > mMinFlingVelocity) {                newState = xvel > 0;            } else {                newState = getTargetCheckedState();            }            animateThumbToCheckedState(newState);        } else {            animateThumbToCheckedState(isChecked());        }    }    private void animateThumbToCheckedState(boolean newCheckedState) {        // TODO animate!        //float targetPos = newCheckedState ? 0 : getThumbScrollRange();        //mThumbPosition = targetPos;        setChecked(newCheckedState);    }    private boolean getTargetCheckedState() {        return mThumbPosition >= getThumbScrollRange() / 2;    }    @Override    public void setChecked(boolean checked) {        super.setChecked(checked);        mThumbPosition = checked ? getThumbScrollRange() : 0;        invalidate();    }    @Override    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {        super.onLayout(changed, left, top, right, bottom);        mThumbPosition = isChecked() ? getThumbScrollRange() : 0;        int switchRight = getWidth() - getPaddingRight();        int switchLeft = switchRight - mSwitchWidth;        int switchTop = 0;        int switchBottom = 0;        switch (getGravity() & Gravity.VERTICAL_GRAVITY_MASK) {            default:            case Gravity.TOP:                switchTop = getPaddingTop();                switchBottom = switchTop + mSwitchHeight;                break;            case Gravity.CENTER_VERTICAL:                switchTop = (getPaddingTop() + getHeight() - getPaddingBottom()) / 2 -                        mSwitchHeight / 2;                switchBottom = switchTop + mSwitchHeight;                break;            case Gravity.BOTTOM:                switchBottom = getHeight() - getPaddingBottom();                switchTop = switchBottom - mSwitchHeight;                break;        }        mSwitchLeft = switchLeft;        mSwitchTop = switchTop;        mSwitchBottom = switchBottom;        mSwitchRight = switchRight;    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        // Draw the switch        int switchLeft = mSwitchLeft;        int switchTop = mSwitchTop;        int switchRight = mSwitchRight;        int switchBottom = mSwitchBottom;        mTrackDrawable.setBounds(switchLeft, switchTop, switchRight, switchBottom);        mTrackDrawable.draw(canvas);        canvas.save();        mTrackDrawable.getPadding(mTempRect);        int switchInnerLeft = switchLeft + mTempRect.left;        int switchInnerTop = switchTop + mTempRect.top;        int switchInnerRight = switchRight - mTempRect.right;        int switchInnerBottom = switchBottom - mTempRect.bottom;        canvas.clipRect(switchInnerLeft, switchTop, switchInnerRight, switchBottom);        // FIXME:        //Drawable offDrawable = mContext.getResources().getDrawable(R.drawable.switch_thumb_mz);        //Drawable onDrawable = mContext.getResources().getDrawable(R.drawable.switch_thumb_activated_mz);        //mThumbDrawable = getTargetCheckedState() ? onDrawable : offDrawable;        mThumbDrawable.getPadding(mTempRect);        final int thumbPos = (int) (mThumbPosition + 0.5f);        int thumbLeft = switchInnerLeft - mTempRect.left + thumbPos;        int thumbRight = switchInnerLeft + thumbPos + mThumbWidth + mTempRect.right;        mThumbDrawable.setBounds(thumbLeft, switchTop, thumbRight, switchBottom);        mThumbDrawable.draw(canvas);        // mTextColors should not be null, but just in case        if (mTextColors != null) {            mTextPaint.setColor(mTextColors.getColorForState(getDrawableState(),                    mTextColors.getDefaultColor()));        }        mTextPaint.drawableState = getDrawableState();        Layout switchText = getTargetCheckedState() ? mOnLayout : mOffLayout;        canvas.translate((thumbLeft + thumbRight) / 2 - switchText.getWidth() / 2,                (switchInnerTop + switchInnerBottom) / 2 - switchText.getHeight() / 2);        switchText.draw(canvas);        canvas.restore();    }    @Override    public int getCompoundPaddingRight() {        int padding = super.getCompoundPaddingRight() + mSwitchWidth;        if (!TextUtils.isEmpty(getText())) {            padding += mSwitchPadding;        }        return padding;    }    private int getThumbScrollRange() {        if (mTrackDrawable == null) {            return 0;        }        mTrackDrawable.getPadding(mTempRect);        return mSwitchWidth - mThumbWidth - mTempRect.left - mTempRect.right;    }    @Override    protected int[] onCreateDrawableState(int extraSpace) {        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);        if (isChecked()) {            mergeDrawableStates(drawableState, CHECKED_STATE_SET);        }        return drawableState;    }    @Override    protected void drawableStateChanged() {        super.drawableStateChanged();        int[] myDrawableState = getDrawableState();        // Set the state of the Drawable        // Drawable may be null when checked state is set from XML, from super constructor        if (mThumbDrawable != null) mThumbDrawable.setState(myDrawableState);        if (mTrackDrawable != null) mTrackDrawable.setState(myDrawableState);        invalidate();    }    @Override    protected boolean verifyDrawable(Drawable who) {        return super.verifyDrawable(who) || who == mThumbDrawable || who == mTrackDrawable;    }    /*@Override    public void jumpDrawablesToCurrentState() {        super.jumpDrawablesToCurrentState();        mThumbDrawable.jumpToCurrentState();        mTrackDrawable.jumpToCurrentState();    }*/}
项目下载:http://download.csdn.net/detail/welovesunflower/4577152
直接拷贝4.0的源码是有问题的,很多新增的API是4.0之前没有的,需要修改一下。改动最大的部分有两个:
1. 注释掉了jumpDrawablesToCurrentState() 这个函数,因为这是4.0的Drawable 里面新增的回调接口,这里不需要,就注释掉;
2. 4.0源码中的onPopulateAccessibilityEvent 这个函数是新增的回调函数,低级的SDK是没有的,怎么办。查看源码,发现这个函数只是在dispatchPopulateAccessibilityEvent调用了,将源码的Switch中的onPopulateAccessibilityEvent改成了poulateAccessibilityEvent还是在Switch的dispatchPopulateAccessibilityEvent调用就可以了。
另外可以在style文件里自定义Switch属性。
参考:http://www.eoeandroid.com/thread-194563-1-1.html