Android自定义Switch控件代码示例

 

Switch是Android的一个开关控件,相当于IPhone的UISwitch效果,但是该控件是4.0以后才有得,故而有些项目需要的时候不得不自己去实现该控件功能,网上主要流行的方法是继承View等控件自己在onDraw()里面绘制控件,但是不是效果不太理想就是体验性太差,另外也有修改官方Switch控件的,综合网上资料,觉得修改官方Switch控件比较靠谱,比较体验性方面性能方面都有保证,API接口大都是一致的,本人选择这种方式。

 

修改Switch的主要思想是:

1. Switch中含有高版本SDK代码,需要去掉或者改写这样的代码

2. Switch对应declare-styleable属性声明应该在Android SDK安装目录中找到对应的源码声明片段,基本上copy过来就行了。所在路径:Androidandroid-sdkplatformsandroid-17datares

 

修改后的MySwitch控件接口基本与原Switch控件一致,并且除了可支持所有SDK外,增加了2项小功能:

1. 支持用Track背景图片的方式代替Texton Textoff等文字方式表现开关状态

2.支持调整控制Switch的高度

 

下面贴出Switch修改的关键代码:

 

 

/** 
 * <p>  
 * modified from android SDK 14(4.0) android.widget.Switch. 
 * <br/> 
 * <strong>new feature: </strong> 
 * <ol> 
 * <li>support SDK 1 or higher. </li> 
 * <li>you can use track drawable instead of text to display the changes of off-on state!</li> 
 * <li>you can control the Switch minimum height. </li> 
 * </ol> 
 * </p> 
 *   
 *  @see {@link Switch} 
 *  @author Wison 
 */  
public class MySwitch extends CompoundButton {  
    // Support track drawable instead of text  
    private Drawable mTrackOnDrawable;  
    private Drawable mTrackOffDrawable;  
      
    // Support minimum height  
    private int mSwitchMinHeight;  
  
    /** 
     * 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 MySwitch(Context context, AttributeSet attrs, int defStyle) {  
        super(context, attrs, defStyle);  
  
        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);  
  
        // off-on 模式: 图片模式或文字模式,图片模式是用Track背景图片表示off-on的状态,文字模式是用文字来表示off-on状态。  
        mTrackOnDrawable = a.getDrawable(R.styleable.Switch_trackOn);  
        mTrackOffDrawable = a.getDrawable(R.styleable.Switch_trackOff);  
        if (checkTrackOffOnDrawable()) {  
            // 如果设定图片模式,则默认显示off状态  
            mTrackDrawable = mTrackOffDrawable;  
        } else {  
            mTrackDrawable = a.getDrawable(R.styleable.Switch_track);  
        }  
          
        mThumbDrawable = a.getDrawable(R.styleable.Switch_thumb);  
        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);  
          
        mSwitchMinHeight = a.getDimensionPixelSize(R.styleable.Switch_switchMinHeight, 0);  
          
        mSwitchPadding = a.getDimensionPixelSize(R.styleable.Switch_switchPadding, 0);  
  
        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());  
    }  
  
    private boolean checkTrackOffOnDrawable() {  
        return mTrackOnDrawable != null && mTrackOffDrawable != null;  
    }  
  
    @SuppressLint("NewApi")  
    @Override  
    public void onMeasure(int widthMeasureSpec, int 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();  
        int switchHeight;  
        if (mSwitchMinHeight <= 0) {  
            switchHeight = mTrackDrawable.getIntrinsicHeight();  
        } else {  
            switchHeight = Math.max(mSwitchMinHeight, mTempRect.top + mTempRect.bottom);  
        }  
          
        mThumbWidth = maxTextWidth + mThumbTextPadding * 2;  
  
        mSwitchWidth = switchWidth;  
        mSwitchHeight = switchHeight;  
  
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
        final int measuredHeight = getMeasuredHeight();  
        if (measuredHeight < switchHeight) {  
            if (Build.VERSION.SDK_INT >= 11) {  
                setMeasuredDimension(getMeasuredWidthAndState(), switchHeight);  
            } else {  
                setMeasuredDimension(getMeasuredWidth(), switchHeight);  
            }  
        }  
    }  
  
    @Override  
    public void setChecked(boolean checked) {  
        if (checkTrackOffOnDrawable()) {  
            mTrackDrawable = checked ? mTrackOnDrawable : mTrackOffDrawable;  
            refreshDrawableState();  
        }  
        super.setChecked(checked);  
        mThumbPosition = checked ? getThumbScrollRange() : 0;  
        invalidate();  
    }  
  
    @Override  
    protected void onDraw(Canvas canvas) {  
        super.onDraw(canvas);  
        // Draw the switch  
        int switchLeft = mSwitchLeft;  
        int switchTop = mSwitchTop;  
        int switchRight = mSwitchRight;  
        int switchBottom = mSwitchBottom;  
          
        if (checkTrackOffOnDrawable()) {  
            mTrackDrawable = getTargetCheckedState() ? mTrackOnDrawable : mTrackOffDrawable;  
            refreshDrawableState();  
        }  
          
        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);  
  
        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;  
  
        if (switchText != null) {  
            canvas.translate((thumbLeft + thumbRight) / 2 - switchText.getWidth() / 2,  
                    (switchInnerTop + switchInnerBottom) / 2 - switchText.getHeight() / 2);  
            switchText.draw(canvas);  
        }  
        canvas.restore();  
    }  
}  

 

 

下面是关键属性声明:

<declare-styleable name="Switch">  
      
    <!-- Drawable to use when the switch is in the checked/"on" state. -->  
    <attr name="trackOn" format="reference" />  
    <!-- Drawable to use when the switch is in the unchecked/"off" state. -->  
    <attr name="trackOff" format="reference" />  
    <!-- Minimum height for the switch component -->  
    <attr name="switchMinHeight" format="dimension" />  
      
    <!-- Drawable to use as the "thumb" that switches back and forth. -->  
    <attr name="thumb" format="reference" />  
    <!-- Drawable to use as the "track" that the switch thumb slides within. -->  
    <attr name="track" format="reference" />  
    <!-- Text to use when the switch is in the checked/"on" state. -->  
    <attr name="textOn" format="string" />  
    <!-- Text to use when the switch is in the unchecked/"off" state. -->  
    <attr name="textOff" format="string" />  
    <!-- Amount of padding on either side of text within the switch thumb. -->  
    <attr name="thumbTextPadding" format="dimension" />  
    <!-- TextAppearance style for text displayed on the switch thumb. -->  
    <attr name="switchTextAppearance" format="reference" />  
    <!-- Minimum width for the switch component -->  
    <attr name="switchMinWidth" format="dimension" />  
    <!-- Minimum space between the switch and caption text -->  
    <attr name="switchPadding" format="dimension" />  
</declare-styleable>  

以下是效果图: