Android Switch控件(在android2.2 api8及以上使用)

时间:2021-10-03 17:22:16
IOS有一种UISwitch控件,只有两个状态:on,off。如图所示
Android Switch控件(在android2.2 api8及以上使用)
在Android4.0中也添加了一个类似的控件:Switch.如图所示
Android Switch控件(在android2.2 api8及以上使用) Android Switch控件(在android2.2 api8及以上使用) 其类关系图如下:
java.lang.Object
   ↳ Android.view.View
    ↳android.widget.TextView
    ↳android.widget.Button
    ↳android.widget.CompoundButton
    ↳android.widget.Switch
父类:compoundButton
Switch是一个可以再两种状态切换的开关控件。用户可以拖动来选择,也可以像选择复选框一样点击切换Switch的状态 在布局文件使用方法同TextView: [java] view plaincopy
  1. <Switch  
  2.         android:id="@+id/switch_test"  
  3.         android:layout_width="match_parent"  
  4.         android:layout_height="match_parent" />  
因为该组件继承自CompoundButton,在代码中可以通过实现CompoundButton.OnCheckedChangeListener接口,并实现其内部类的onCheckedChanged来监听状态变化。

[java] view plaincopy
  1. public class MainActivity extends Activity {  
  2.   
  3.     @Override  
  4.     public void onCreate(Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.         setContentView(R.layout.activity_main);  
  7.         Switch switchTest = (Switch) findViewById(R.id.switch_test);  
  8.         switchTest.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {  
  9.                     @Override  
  10.                     public void onCheckedChanged(CompoundButton buttonView,  
  11.                             boolean isChecked) {  
  12.                         Toast.makeText(MainActivity.this, isChecked + "",  
  13.                                 Toast.LENGTH_SHORT).show();  
  14.                     }  
  15.                 });  
  16.     }  
  17. }  
在代码中也可以改变该组件的外观
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,需要自定义其属性,以下代码摘自网上:
[java] view plaincopy
  1. public class Switch extends CompoundButton {  
  2.     private static final int TOUCH_MODE_IDLE = 0;  
  3.     private static final int TOUCH_MODE_DOWN = 1;  
  4.     private static final int TOUCH_MODE_DRAGGING = 2;  
  5.   
  6.     // Enum for the "typeface" XML parameter.  
  7.     private static final int SANS = 1;  
  8.     private static final int SERIF = 2;  
  9.     private static final int MONOSPACE = 3;  
  10.   
  11.     private Drawable mThumbDrawable;  
  12.     private Drawable mTrackDrawable;  
  13.     private int mThumbTextPadding;  
  14.     private int mSwitchMinWidth;  
  15.     private int mSwitchPadding;  
  16.     private CharSequence mTextOn;  
  17.     private CharSequence mTextOff;  
  18.   
  19.     private int mTouchMode;  
  20.     private int mTouchSlop;  
  21.     private float mTouchX;  
  22.     private float mTouchY;  
  23.     private VelocityTracker mVelocityTracker = VelocityTracker.obtain();  
  24.     private int mMinFlingVelocity;  
  25.   
  26.     private float mThumbPosition;  
  27.     private int mSwitchWidth;  
  28.     private int mSwitchHeight;  
  29.     private int mThumbWidth; // Does not include padding  
  30.   
  31.     private int mSwitchLeft;  
  32.     private int mSwitchTop;  
  33.     private int mSwitchRight;  
  34.     private int mSwitchBottom;  
  35.   
  36.     private TextPaint mTextPaint;  
  37.     private ColorStateList mTextColors;  
  38.     private Layout mOnLayout;  
  39.     private Layout mOffLayout;  
  40.   
  41.     private Context mContext;  
  42.   
  43.     @SuppressWarnings("hiding")  
  44.     private final Rect mTempRect = new Rect();  
  45.   
  46.     private static final int[] CHECKED_STATE_SET = {  
  47.         android.R.attr.state_checked  
  48.     };  
  49.   
  50.     /** 
  51.      * Construct a new Switch with default styling. 
  52.      * 
  53.      * @param context The Context that will determine this widget's theming. 
  54.      */  
  55.     public Switch(Context context) {  
  56.         this(context, null);  
  57.   
  58.         mContext = context;  
  59.     }  
  60.   
  61.     /** 
  62.      * Construct a new Switch with default styling, overriding specific style 
  63.      * attributes as requested. 
  64.      * 
  65.      * @param context The Context that will determine this widget's theming. 
  66.      * @param attrs Specification of attributes that should deviate from default styling. 
  67.      */  
  68.     public Switch(Context context, AttributeSet attrs) {  
  69.         this(context, attrs, R.attr.switchStyle);  
  70.   
  71.         mContext = context;  
  72.     }  
  73.   
  74.     /** 
  75.      * Construct a new Switch with a default style determined by the given theme attribute, 
  76.      * overriding specific style attributes as requested. 
  77.      * 
  78.      * @param context The Context that will determine this widget's theming. 
  79.      * @param attrs Specification of attributes that should deviate from the default styling. 
  80.      * @param defStyle An attribute ID within the active theme containing a reference to the 
  81.      *                 default style for this widget. e.g. android.R.attr.switchStyle. 
  82.      */  
  83.     public Switch(Context context, AttributeSet attrs, int defStyle) {  
  84.         super(context, attrs, defStyle);  
  85.   
  86.         mContext = context;  
  87.   
  88.         mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);  
  89.         Resources res = getResources();  
  90.         mTextPaint.density = res.getDisplayMetrics().density;  
  91.         //float scaledDensity = res.getDisplayMetrics().scaledDensity;  
  92.         //mTextPaint.setCompatibilityScaling(res.getCompatibilityInfo().applicationScale);  
  93.   
  94.         TypedArray a = context.obtainStyledAttributes(attrs,  
  95.                 R.styleable.Switch, defStyle, 0);  
  96.   
  97.         mThumbDrawable = a.getDrawable(R.styleable.Switch_thumb);  
  98.         mTrackDrawable = a.getDrawable(R.styleable.Switch_track);  
  99.         mTextOn = a.getText(R.styleable.Switch_textOn);  
  100.         mTextOff = a.getText(R.styleable.Switch_textOff);  
  101.         mThumbTextPadding = a.getDimensionPixelSize(  
  102.                 R.styleable.Switch_thumbTextPadding, 0);  
  103.         mSwitchMinWidth = a.getDimensionPixelSize(  
  104.                 R.styleable.Switch_switchMinWidth, 0);  
  105.         mSwitchPadding = a.getDimensionPixelSize(  
  106.                 R.styleable.Switch_switchPadding, 0);  
  107.   
  108.         Log.d("SvenDebug""mTextOn:" + mTextOn);  
  109.         Log.d("SvenDebug""mTextOff:" + mTextOff);  
  110.         Log.d("SvenDebug""mThumbTextPadding:" + mThumbTextPadding);  
  111.         Log.d("SvenDebug""mSwitchMinWidth:" + mSwitchMinWidth);  
  112.         Log.d("SvenDebug""mSwitchPadding:" + mSwitchPadding);  
  113.   
  114.         int appearance = a.getResourceId(  
  115.                 R.styleable.Switch_switchTextAppearance, 0);  
  116.         if (appearance != 0) {  
  117.             setSwitchTextAppearance(context, appearance);  
  118.         }  
  119.         a.recycle();  
  120.   
  121.         ViewConfiguration config = ViewConfiguration.get(context);  
  122.         mTouchSlop = config.getScaledTouchSlop();  
  123.         mMinFlingVelocity = config.getScaledMinimumFlingVelocity();  
  124.   
  125.         // Refresh display with current params  
  126.         refreshDrawableState();  
  127.         setChecked(isChecked());  
  128.     }  
  129.   
  130.     /** 
  131.      * Sets the switch text color, size, style, hint color, and highlight color 
  132.      * from the specified TextAppearance resource. 
  133.      */  
  134.     public void setSwitchTextAppearance(Context context, int resid) {  
  135.         mContext = context;  
  136.   
  137.         TypedArray appearance =  
  138.                 context.obtainStyledAttributes(resid,  
  139.                         R.styleable.TextAppearance);  
  140.   
  141.         ColorStateList colors;  
  142.         int ts;  
  143.   
  144.         colors = appearance.getColorStateList(R.styleable.  
  145.                 TextAppearance_textColor);  
  146.         if (colors != null) {  
  147.             mTextColors = colors;  
  148.         } else {  
  149.             // If no color set in TextAppearance, default to the view's textColor  
  150.             mTextColors = getTextColors();  
  151.         }  
  152.   
  153.         ts = appearance.getDimensionPixelSize(R.styleable.  
  154.                 TextAppearance_textSize, 0);  
  155.         if (ts != 0) {  
  156.             if (ts != mTextPaint.getTextSize()) {  
  157.                 mTextPaint.setTextSize(ts);  
  158.                 requestLayout();  
  159.             }  
  160.         }  
  161.   
  162.         int typefaceIndex, styleIndex;  
  163.   
  164.         typefaceIndex = appearance.getInt(R.styleable.  
  165.                 TextAppearance_typeface, -1);  
  166.         styleIndex = appearance.getInt(R.styleable.  
  167.                 TextAppearance_textStyle, -1);  
  168.   
  169.         setSwitchTypefaceByIndex(typefaceIndex, styleIndex);  
  170.   
  171.         appearance.recycle();  
  172.     }  
  173.   
  174.     private void setSwitchTypefaceByIndex(int typefaceIndex, int styleIndex) {  
  175.         Typeface tf = null;  
  176.         switch (typefaceIndex) {  
  177.             case SANS:  
  178.                 tf = Typeface.SANS_SERIF;  
  179.                 break;  
  180.   
  181.             case SERIF:  
  182.                 tf = Typeface.SERIF;  
  183.                 break;  
  184.   
  185.             case MONOSPACE:  
  186.                 tf = Typeface.MONOSPACE;  
  187.                 break;  
  188.         }  
  189.   
  190.         setSwitchTypeface(tf, styleIndex);  
  191.     }  
  192.   
  193.     /** 
  194.      * Sets the typeface and style in which the text should be displayed on the 
  195.      * switch, and turns on the fake bold and italic bits in the Paint if the 
  196.      * Typeface that you provided does not have all the bits in the 
  197.      * style that you specified. 
  198.      */  
  199.     public void setSwitchTypeface(Typeface tf, int style) {  
  200.         if (style > 0) {  
  201.             if (tf == null) {  
  202.                 tf = Typeface.defaultFromStyle(style);  
  203.             } else {  
  204.                 tf = Typeface.create(tf, style);  
  205.             }  
  206.   
  207.             setSwitchTypeface(tf);  
  208.             // now compute what (if any) algorithmic styling is needed  
  209.             int typefaceStyle = tf != null ? tf.getStyle() : 0;  
  210.             int need = style & ~typefaceStyle;  
  211.             mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);  
  212.             mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);  
  213.         } else {  
  214.             mTextPaint.setFakeBoldText(false);  
  215.             mTextPaint.setTextSkewX(0);  
  216.             setSwitchTypeface(tf);  
  217.         }  
  218.     }  
  219.   
  220.     /** 
  221.      * Sets the typeface in which the text should be displayed on the switch. 
  222.      * Note that not all Typeface families actually have bold and italic 
  223.      * variants, so you may need to use 
  224.      * {@link #setSwitchTypeface(Typeface, int)} to get the appearance 
  225.      * that you actually want. 
  226.      * 
  227.      * @attr ref android.R.styleable#TextView_typeface 
  228.      * @attr ref android.R.styleable#TextView_textStyle 
  229.      */  
  230.     public void setSwitchTypeface(Typeface tf) {  
  231.         if (mTextPaint.getTypeface() != tf) {  
  232.             mTextPaint.setTypeface(tf);  
  233.   
  234.             requestLayout();  
  235.             invalidate();  
  236.         }  
  237.     }  
  238.   
  239.     /** 
  240.      * Returns the text displayed when the button is in the checked state. 
  241.      */  
  242.     public CharSequence getTextOn() {  
  243.         return mTextOn;  
  244.     }  
  245.   
  246.     /** 
  247.      * Sets the text displayed when the button is in the checked state. 
  248.      */  
  249.     public void setTextOn(CharSequence textOn) {  
  250.         mTextOn = textOn;  
  251.         requestLayout();  
  252.     }  
  253.   
  254.     /** 
  255.      * Returns the text displayed when the button is not in the checked state. 
  256.      */  
  257.     public CharSequence getTextOff() {  
  258.         return mTextOff;  
  259.     }  
  260.   
  261.     /** 
  262.      * Sets the text displayed when the button is not in the checked state. 
  263.      */  
  264.     public void setTextOff(CharSequence textOff) {  
  265.         mTextOff = textOff;  
  266.         requestLayout();  
  267.     }  
  268.   
  269.     @Override  
  270.     public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  271.         final int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
  272.         final int heightMode = MeasureSpec.getMode(heightMeasureSpec);  
  273.         int widthSize = MeasureSpec.getSize(widthMeasureSpec);  
  274.         int heightSize = MeasureSpec.getSize(heightMeasureSpec);  
  275.   
  276.   
  277.         if (mOnLayout == null) {  
  278.             mOnLayout = makeLayout(mTextOn);  
  279.         }  
  280.         if (mOffLayout == null) {  
  281.             mOffLayout = makeLayout(mTextOff);  
  282.         }  
  283.   
  284.         mTrackDrawable.getPadding(mTempRect);  
  285.         final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth());  
  286.         final int switchWidth = Math.max(mSwitchMinWidth,  
  287.                 maxTextWidth * 2 + mThumbTextPadding * 4 + mTempRect.left + mTempRect.right);  
  288.         final int switchHeight = mTrackDrawable.getIntrinsicHeight();  
  289.   
  290.         mThumbWidth = maxTextWidth + mThumbTextPadding * 2;  
  291.   
  292.         switch (widthMode) {  
  293.             case MeasureSpec.AT_MOST:  
  294.                 widthSize = Math.min(widthSize, switchWidth);  
  295.                 break;  
  296.   
  297.             case MeasureSpec.UNSPECIFIED:  
  298.                 widthSize = switchWidth;  
  299.                 break;  
  300.   
  301.             case MeasureSpec.EXACTLY:  
  302.                 // Just use what we were given  
  303.                 break;  
  304.         }  
  305.   
  306.         switch (heightMode) {  
  307.             case MeasureSpec.AT_MOST:  
  308.                 heightSize = Math.min(heightSize, switchHeight);  
  309.                 break;  
  310.   
  311.             case MeasureSpec.UNSPECIFIED:  
  312.                 heightSize = switchHeight;  
  313.                 break;  
  314.   
  315.             case MeasureSpec.EXACTLY:  
  316.                 // Just use what we were given  
  317.                 break;  
  318.         }  
  319.   
  320.         mSwitchWidth = switchWidth;  
  321.         mSwitchHeight = switchHeight;  
  322.   
  323.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  324.         final int measuredHeight = getMeasuredHeight();  
  325.         if (measuredHeight < switchHeight) {  
  326.             setMeasuredDimension(getMeasuredWidth(), switchHeight);  
  327.         }  
  328.     }  
  329.   
  330.    /* @Override 
  331.     public boolean dispatchTouchEvent(MotionEvent event) { 
  332.         Log.d("SvenDebug", "dispatchTouchEvent:action=" + event.getAction()); 
  333.         return false; 
  334.     }*/  
  335.   
  336.     @Override  
  337.     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {  
  338.         Log.d("SvenDebug""dispatchPopulateAccessibilityEvent");  
  339.         populateAccessibilityEvent(event);  
  340.   
  341.         return false;  
  342.     }  
  343.   
  344.     public void populateAccessibilityEvent(AccessibilityEvent event) {  
  345.         if (isChecked()) {  
  346.             CharSequence text = mOnLayout.getText();  
  347.             if (TextUtils.isEmpty(text)) {  
  348.                 text = mContext.getString(R.string.switch_on);  
  349.             }  
  350.             event.getText().add(text);  
  351.         } else {  
  352.             CharSequence text = mOffLayout.getText();  
  353.             if (TextUtils.isEmpty(text)) {  
  354.                 text = mContext.getString(R.string.switch_off);  
  355.             }  
  356.             event.getText().add(text);  
  357.         }  
  358.     }  
  359.   
  360.     private Layout makeLayout(CharSequence text) {  
  361.         return new StaticLayout(text, mTextPaint,  
  362.                 (int) Math.ceil(Layout.getDesiredWidth(text, mTextPaint)),  
  363.                 Layout.Alignment.ALIGN_NORMAL, 1.f, 0true);  
  364.     }  
  365.   
  366.     /** 
  367.      * @return true if (x, y) is within the target area of the switch thumb 
  368.      */  
  369.     private boolean hitThumb(float x, float y) {  
  370.         mThumbDrawable.getPadding(mTempRect);  
  371.         final int thumbTop = mSwitchTop - mTouchSlop;  
  372.         final int thumbLeft = mSwitchLeft + (int) (mThumbPosition + 0.5f) - mTouchSlop;  
  373.         final int thumbRight = thumbLeft + mThumbWidth +  
  374.                 mTempRect.left + mTempRect.right + mTouchSlop;  
  375.         final int thumbBottom = mSwitchBottom + mTouchSlop;  
  376.         return x > thumbLeft && x < thumbRight && y > thumbTop && y < thumbBottom;  
  377.     }  
  378.   
  379.     @Override  
  380.     public boolean onTouchEvent(MotionEvent ev) {  
  381.         mVelocityTracker.addMovement(ev);  
  382.         final int action = ev.getActionMasked();  
  383.         Log.d("SvenDebug""MotionEvent : " + action);  
  384.         switch (action) {  
  385.             case MotionEvent.ACTION_DOWN: {  
  386.                 Log.d("SvenDebug""MotionEvent.ACTION_DOWN");  
  387.                 final float x = ev.getX();  
  388.                 final float y = ev.getY();  
  389.                 if (isEnabled() && hitThumb(x, y)) {  
  390.                     Log.d("SvenDebug""Enable in widget rect");  
  391.                     mTouchMode = TOUCH_MODE_DOWN;  
  392.                     mTouchX = x;  
  393.                     mTouchY = y;  
  394.                 }  
  395.                 break;  
  396.             }  
  397.   
  398.             case MotionEvent.ACTION_MOVE: {  
  399.                 Log.d("SvenDebug""MotionEvent.ACTION_MOVE");  
  400.                 switch (mTouchMode) {  
  401.                     case TOUCH_MODE_IDLE:  
  402.                         // Didn't target the thumb, treat normally.  
  403.                         break;  
  404.   
  405.                     case TOUCH_MODE_DOWN: {  
  406.                         Log.d("SvenDebug""TOUCH_MODE_DOWN:mTouchSlop = " + mTouchSlop);  
  407.                         final float x = ev.getX();  
  408.                         final float y = ev.getY();  
  409.                         if (Math.abs(x - mTouchX) > mTouchSlop ||  
  410.                                 Math.abs(y - mTouchY) > mTouchSlop) {  
  411.                             mTouchMode = TOUCH_MODE_DRAGGING;  
  412.                             getParent().requestDisallowInterceptTouchEvent(true);  
  413.                             mTouchX = x;  
  414.                             mTouchY = y;  
  415.                             return true;  
  416.                         }  
  417.                         break;  
  418.                     }  
  419.   
  420.                     case TOUCH_MODE_DRAGGING: {  
  421.                         Log.d("SvenDebug""TOUCH_MODE_DRAGGING");  
  422.                         final float x = ev.getX();  
  423.                         final float dx = x - mTouchX;  
  424.                         float newPos = Math.max(0,  
  425.                                 Math.min(mThumbPosition + dx, getThumbScrollRange()));  
  426.                         if (newPos != mThumbPosition) {  
  427.                             mThumbPosition = newPos;  
  428.                             mTouchX = x;  
  429.                             invalidate();  
  430.                         }  
  431.                         return true;  
  432.                     }  
  433.                 }  
  434.                 break;  
  435.             }  
  436.   
  437.             case MotionEvent.ACTION_UP:  
  438.             case MotionEvent.ACTION_CANCEL: {  
  439.                 Log.d("SvenDebug""MotionEvent.ACTION_UP|ACTION_CANCEL");  
  440.                 if (mTouchMode == TOUCH_MODE_DRAGGING) {  
  441.                     stopDrag(ev);  
  442.                     return true;  
  443.                 }  
  444.                 mTouchMode = TOUCH_MODE_IDLE;  
  445.                 mVelocityTracker.clear();  
  446.                 break;  
  447.             }  
  448.         }  
  449.   
  450.         return super.onTouchEvent(ev);  
  451.     }  
  452.   
  453.     private void cancelSuperTouch(MotionEvent ev) {  
  454.         MotionEvent cancel = MotionEvent.obtain(ev);  
  455.         cancel.setAction(MotionEvent.ACTION_CANCEL);  
  456.         super.onTouchEvent(cancel);  
  457.         cancel.recycle();  
  458.     }  
  459.   
  460.     /** 
  461.      * Called from onTouchEvent to end a drag operation. 
  462.      * 
  463.      * @param ev Event that triggered the end of drag mode - ACTION_UP or ACTION_CANCEL 
  464.      */  
  465.     private void stopDrag(MotionEvent ev) {  
  466.         mTouchMode = TOUCH_MODE_IDLE;  
  467.         // Up and not canceled, also checks the switch has not been disabled during the drag  
  468.         boolean commitChange = ev.getAction() == MotionEvent.ACTION_UP && isEnabled();  
  469.   
  470.         cancelSuperTouch(ev);  
  471.   
  472.         if (commitChange) {  
  473.             boolean newState;  
  474.             mVelocityTracker.computeCurrentVelocity(1000);  
  475.             float xvel = mVelocityTracker.getXVelocity();  
  476.             if (Math.abs(xvel) > mMinFlingVelocity) {  
  477.                 newState = xvel > 0;  
  478.             } else {  
  479.                 newState = getTargetCheckedState();  
  480.             }  
  481.             animateThumbToCheckedState(newState);  
  482.         } else {  
  483.             animateThumbToCheckedState(isChecked());  
  484.         }  
  485.     }  
  486.   
  487.     private void animateThumbToCheckedState(boolean newCheckedState) {  
  488.         // TODO animate!  
  489.         //float targetPos = newCheckedState ? 0 : getThumbScrollRange();  
  490.         //mThumbPosition = targetPos;  
  491.         setChecked(newCheckedState);  
  492.     }  
  493.   
  494.     private boolean getTargetCheckedState() {  
  495.         return mThumbPosition >= getThumbScrollRange() / 2;  
  496.     }  
  497.   
  498.     @Override  
  499.     public void setChecked(boolean checked) {  
  500.         super.setChecked(checked);  
  501.         mThumbPosition = checked ? getThumbScrollRange() : 0;  
  502.         invalidate();  
  503.     }  
  504.   
  505.     @Override  
  506.     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {  
  507.         super.onLayout(changed, left, top, right, bottom);  
  508.   
  509.         mThumbPosition = isChecked() ? getThumbScrollRange() : 0;  
  510.   
  511.         int switchRight = getWidth() - getPaddingRight();  
  512.         int switchLeft = switchRight - mSwitchWidth;  
  513.         int switchTop = 0;  
  514.         int switchBottom = 0;  
  515.         switch (getGravity() & Gravity.VERTICAL_GRAVITY_MASK) {  
  516.             default:  
  517.             case Gravity.TOP:  
  518.                 switchTop = getPaddingTop();  
  519.                 switchBottom = switchTop + mSwitchHeight;  
  520.                 break;  
  521.   
  522.             case Gravity.CENTER_VERTICAL:  
  523.                 switchTop = (getPaddingTop() + getHeight() - getPaddingBottom()) / 2 -  
  524.                         mSwitchHeight / 2;  
  525.                 switchBottom = switchTop + mSwitchHeight;  
  526.                 break;  
  527.   
  528.             case Gravity.BOTTOM:  
  529.                 switchBottom = getHeight() - getPaddingBottom();  
  530.                 switchTop = switchBottom - mSwitchHeight;  
  531.                 break;  
  532.         }  
  533.   
  534.         mSwitchLeft = switchLeft;  
  535.         mSwitchTop = switchTop;  
  536.         mSwitchBottom = switchBottom;  
  537.         mSwitchRight = switchRight;  
  538.     }  
  539.   
  540.     @Override  
  541.     protected void onDraw(Canvas canvas) {  
  542.         super.onDraw(canvas);  
  543.   
  544.         // Draw the switch  
  545.         int switchLeft = mSwitchLeft;  
  546.         int switchTop = mSwitchTop;  
  547.         int switchRight = mSwitchRight;  
  548.         int switchBottom = mSwitchBottom;  
  549.   
  550.         mTrackDrawable.setBounds(switchLeft, switchTop, switchRight, switchBottom);  
  551.         mTrackDrawable.draw(canvas);  
  552.   
  553.         canvas.save();  
  554.   
  555.         mTrackDrawable.getPadding(mTempRect);  
  556.         int switchInnerLeft = switchLeft + mTempRect.left;  
  557.         int switchInnerTop = switchTop + mTempRect.top;  
  558.         int switchInnerRight = switchRight - mTempRect.right;  
  559.         int switchInnerBottom = switchBottom - mTempRect.bottom;  
  560.         canvas.clipRect(switchInnerLeft, switchTop, switchInnerRight, switchBottom);  
  561.   
  562.         // FIXME:  
  563.         //Drawable offDrawable = mContext.getResources().getDrawable(R.drawable.switch_thumb_mz);  
  564.         //Drawable onDrawable = mContext.getResources().getDrawable(R.drawable.switch_thumb_activated_mz);  
  565.         //mThumbDrawable = getTargetCheckedState() ? onDrawable : offDrawable;  
  566.   
  567.         mThumbDrawable.getPadding(mTempRect);  
  568.         final int thumbPos = (int) (mThumbPosition + 0.5f);  
  569.         int thumbLeft = switchInnerLeft - mTempRect.left + thumbPos;  
  570.         int thumbRight = switchInnerLeft + thumbPos + mThumbWidth + mTempRect.right;  
  571.   
  572.         mThumbDrawable.setBounds(thumbLeft, switchTop, thumbRight, switchBottom);  
  573.         mThumbDrawable.draw(canvas);  
  574.   
  575.         // mTextColors should not be null, but just in case  
  576.         if (mTextColors != null) {  
  577.             mTextPaint.setColor(mTextColors.getColorForState(getDrawableState(),  
  578.                     mTextColors.getDefaultColor()));  
  579.         }  
  580.         mTextPaint.drawableState = getDrawableState();  
  581.   
  582.         Layout switchText = getTargetCheckedState() ? mOnLayout : mOffLayout;  
  583.   
  584.         canvas.translate((thumbLeft + thumbRight) / 2 - switchText.getWidth() / 2,  
  585.                 (switchInnerTop + switchInnerBottom) / 2 - switchText.getHeight() / 2);  
  586.         switchText.draw(canvas);  
  587.   
  588.         canvas.restore();  
  589.     }  
  590.   
  591.     @Override  
  592.     public int getCompoundPaddingRight() {  
  593.         int padding = super.getCompoundPaddingRight() + mSwitchWidth;  
  594.         if (!TextUtils.isEmpty(getText())) {  
  595.             padding += mSwitchPadding;  
  596.         }  
  597.         return padding;  
  598.     }  
  599.   
  600.     private int getThumbScrollRange() {  
  601.         if (mTrackDrawable == null) {  
  602.             return 0;  
  603.         }  
  604.         mTrackDrawable.getPadding(mTempRect);  
  605.         return mSwitchWidth - mThumbWidth - mTempRect.left - mTempRect.right;  
  606.     }  
  607.   
  608.     @Override  
  609.     protected int[] onCreateDrawableState(int extraSpace) {  
  610.         final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);  
  611.         if (isChecked()) {  
  612.             mergeDrawableStates(drawableState, CHECKED_STATE_SET);  
  613.         }  
  614.         return drawableState;  
  615.     }  
  616.   
  617.     @Override  
  618.     protected void drawableStateChanged() {  
  619.         super.drawableStateChanged();  
  620.   
  621.         int[] myDrawableState = getDrawableState();  
  622.   
  623.         // Set the state of the Drawable  
  624.         // Drawable may be null when checked state is set from XML, from super constructor  
  625.         if (mThumbDrawable != null) mThumbDrawable.setState(myDrawableState);  
  626.         if (mTrackDrawable != null) mTrackDrawable.setState(myDrawableState);  
  627.   
  628.         invalidate();  
  629.     }  
  630.   
  631.     @Override  
  632.     protected boolean verifyDrawable(Drawable who) {  
  633.         return super.verifyDrawable(who) || who == mThumbDrawable || who == mTrackDrawable;  
  634.     }  
  635.   
  636.     /*@Override 
  637.     public void jumpDrawablesToCurrentState() { 
  638.         super.jumpDrawablesToCurrentState(); 
  639.         mThumbDrawable.jumpToCurrentState(); 
  640.         mTrackDrawable.jumpToCurrentState(); 
  641.     }*/  
  642. }  
项目下载: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
[java] view plaincopy
  1. public class MainActivity extends Activity {  
  2.   
  3.     @Override  
  4.     public void onCreate(Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.         setContentView(R.layout.activity_main);  
  7.         Switch switchTest = (Switch) findViewById(R.id.switch_test);  
  8.         switchTest.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {  
  9.                     @Override  
  10.                     public void onCheckedChanged(CompoundButton buttonView,  
  11.                             boolean isChecked) {  
  12.                         Toast.makeText(MainActivity.this, isChecked + "",  
  13.                                 Toast.LENGTH_SHORT).show();  
  14.                     }  
  15.                 });  
  16.     }  
  17. }  
在代码中也可以改变该组件的外观
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,需要自定义其属性,以下代码摘自网上:
[java] view plaincopy
  1. public class Switch extends CompoundButton {  
  2.     private static final int TOUCH_MODE_IDLE = 0;  
  3.     private static final int TOUCH_MODE_DOWN = 1;  
  4.     private static final int TOUCH_MODE_DRAGGING = 2;  
  5.   
  6.     // Enum for the "typeface" XML parameter.  
  7.     private static final int SANS = 1;  
  8.     private static final int SERIF = 2;  
  9.     private static final int MONOSPACE = 3;  
  10.   
  11.     private Drawable mThumbDrawable;  
  12.     private Drawable mTrackDrawable;  
  13.     private int mThumbTextPadding;  
  14.     private int mSwitchMinWidth;  
  15.     private int mSwitchPadding;  
  16.     private CharSequence mTextOn;  
  17.     private CharSequence mTextOff;  
  18.   
  19.     private int mTouchMode;  
  20.     private int mTouchSlop;  
  21.     private float mTouchX;  
  22.     private float mTouchY;  
  23.     private VelocityTracker mVelocityTracker = VelocityTracker.obtain();  
  24.     private int mMinFlingVelocity;  
  25.   
  26.     private float mThumbPosition;  
  27.     private int mSwitchWidth;  
  28.     private int mSwitchHeight;  
  29.     private int mThumbWidth; // Does not include padding  
  30.   
  31.     private int mSwitchLeft;  
  32.     private int mSwitchTop;  
  33.     private int mSwitchRight;  
  34.     private int mSwitchBottom;  
  35.   
  36.     private TextPaint mTextPaint;  
  37.     private ColorStateList mTextColors;  
  38.     private Layout mOnLayout;  
  39.     private Layout mOffLayout;  
  40.   
  41.     private Context mContext;  
  42.   
  43.     @SuppressWarnings("hiding")  
  44.     private final Rect mTempRect = new Rect();  
  45.   
  46.     private static final int[] CHECKED_STATE_SET = {  
  47.         android.R.attr.state_checked  
  48.     };  
  49.   
  50.     /** 
  51.      * Construct a new Switch with default styling. 
  52.      * 
  53.      * @param context The Context that will determine this widget's theming. 
  54.      */  
  55.     public Switch(Context context) {  
  56.         this(context, null);  
  57.   
  58.         mContext = context;  
  59.     }  
  60.   
  61.     /** 
  62.      * Construct a new Switch with default styling, overriding specific style 
  63.      * attributes as requested. 
  64.      * 
  65.      * @param context The Context that will determine this widget's theming. 
  66.      * @param attrs Specification of attributes that should deviate from default styling. 
  67.      */  
  68.     public Switch(Context context, AttributeSet attrs) {  
  69.         this(context, attrs, R.attr.switchStyle);  
  70.   
  71.         mContext = context;  
  72.     }  
  73.   
  74.     /** 
  75.      * Construct a new Switch with a default style determined by the given theme attribute, 
  76.      * overriding specific style attributes as requested. 
  77.      * 
  78.      * @param context The Context that will determine this widget's theming. 
  79.      * @param attrs Specification of attributes that should deviate from the default styling. 
  80.      * @param defStyle An attribute ID within the active theme containing a reference to the 
  81.      *                 default style for this widget. e.g. android.R.attr.switchStyle. 
  82.      */  
  83.     public Switch(Context context, AttributeSet attrs, int defStyle) {  
  84.         super(context, attrs, defStyle);  
  85.   
  86.         mContext = context;  
  87.   
  88.         mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);  
  89.         Resources res = getResources();  
  90.         mTextPaint.density = res.getDisplayMetrics().density;  
  91.         //float scaledDensity = res.getDisplayMetrics().scaledDensity;  
  92.         //mTextPaint.setCompatibilityScaling(res.getCompatibilityInfo().applicationScale);  
  93.   
  94.         TypedArray a = context.obtainStyledAttributes(attrs,  
  95.                 R.styleable.Switch, defStyle, 0);  
  96.   
  97.         mThumbDrawable = a.getDrawable(R.styleable.Switch_thumb);  
  98.         mTrackDrawable = a.getDrawable(R.styleable.Switch_track);  
  99.         mTextOn = a.getText(R.styleable.Switch_textOn);  
  100.         mTextOff = a.getText(R.styleable.Switch_textOff);  
  101.         mThumbTextPadding = a.getDimensionPixelSize(  
  102.                 R.styleable.Switch_thumbTextPadding, 0);  
  103.         mSwitchMinWidth = a.getDimensionPixelSize(  
  104.                 R.styleable.Switch_switchMinWidth, 0);  
  105.         mSwitchPadding = a.getDimensionPixelSize(  
  106.                 R.styleable.Switch_switchPadding, 0);  
  107.   
  108.         Log.d("SvenDebug""mTextOn:" + mTextOn);  
  109.         Log.d("SvenDebug""mTextOff:" + mTextOff);  
  110.         Log.d("SvenDebug""mThumbTextPadding:" + mThumbTextPadding);  
  111.         Log.d("SvenDebug""mSwitchMinWidth:" + mSwitchMinWidth);  
  112.         Log.d("SvenDebug""mSwitchPadding:" + mSwitchPadding);  
  113.   
  114.         int appearance = a.getResourceId(  
  115.                 R.styleable.Switch_switchTextAppearance, 0);  
  116.         if (appearance != 0) {  
  117.             setSwitchTextAppearance(context, appearance);  
  118.         }  
  119.         a.recycle();  
  120.   
  121.         ViewConfiguration config = ViewConfiguration.get(context);  
  122.         mTouchSlop = config.getScaledTouchSlop();  
  123.         mMinFlingVelocity = config.getScaledMinimumFlingVelocity();  
  124.   
  125.         // Refresh display with current params  
  126.         refreshDrawableState();  
  127.         setChecked(isChecked());  
  128.     }  
  129.   
  130.     /** 
  131.      * Sets the switch text color, size, style, hint color, and highlight color 
  132.      * from the specified TextAppearance resource. 
  133.      */  
  134.     public void setSwitchTextAppearance(Context context, int resid) {  
  135.         mContext = context;  
  136.   
  137.         TypedArray appearance =  
  138.                 context.obtainStyledAttributes(resid,  
  139.                         R.styleable.TextAppearance);  
  140.   
  141.         ColorStateList colors;  
  142.         int ts;  
  143.   
  144.         colors = appearance.getColorStateList(R.styleable.  
  145.                 TextAppearance_textColor);  
  146.         if (colors != null) {  
  147.             mTextColors = colors;  
  148.         } else {  
  149.             // If no color set in TextAppearance, default to the view's textColor  
  150.             mTextColors = getTextColors();  
  151.         }  
  152.   
  153.         ts = appearance.getDimensionPixelSize(R.styleable.  
  154.                 TextAppearance_textSize, 0);  
  155.         if (ts != 0) {  
  156.             if (ts != mTextPaint.getTextSize()) {  
  157.                 mTextPaint.setTextSize(ts);  
  158.                 requestLayout();  
  159.             }  
  160.         }  
  161.   
  162.         int typefaceIndex, styleIndex;  
  163.   
  164.         typefaceIndex = appearance.getInt(R.styleable.  
  165.                 TextAppearance_typeface, -1);  
  166.         styleIndex = appearance.getInt(R.styleable.  
  167.                 TextAppearance_textStyle, -1);  
  168.   
  169.         setSwitchTypefaceByIndex(typefaceIndex, styleIndex);  
  170.   
  171.         appearance.recycle();  
  172.     }  
  173.   
  174.     private void setSwitchTypefaceByIndex(int typefaceIndex, int styleIndex) {  
  175.         Typeface tf = null;  
  176.         switch (typefaceIndex) {  
  177.             case SANS:  
  178.                 tf = Typeface.SANS_SERIF;  
  179.                 break;  
  180.   
  181.             case SERIF:  
  182.                 tf = Typeface.SERIF;  
  183.                 break;  
  184.   
  185.             case MONOSPACE:  
  186.                 tf = Typeface.MONOSPACE;  
  187.                 break;  
  188.         }  
  189.   
  190.         setSwitchTypeface(tf, styleIndex);  
  191.     }  
  192.   
  193.     /** 
  194.      * Sets the typeface and style in which the text should be displayed on the 
  195.      * switch, and turns on the fake bold and italic bits in the Paint if the 
  196.      * Typeface that you provided does not have all the bits in the 
  197.      * style that you specified. 
  198.      */  
  199.     public void setSwitchTypeface(Typeface tf, int style) {  
  200.         if (style > 0) {  
  201.             if (tf == null) {  
  202.                 tf = Typeface.defaultFromStyle(style);  
  203.             } else {  
  204.                 tf = Typeface.create(tf, style);  
  205.             }  
  206.   
  207.             setSwitchTypeface(tf);  
  208.             // now compute what (if any) algorithmic styling is needed  
  209.             int typefaceStyle = tf != null ? tf.getStyle() : 0;  
  210.             int need = style & ~typefaceStyle;  
  211.             mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);  
  212.             mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);  
  213.         } else {  
  214.             mTextPaint.setFakeBoldText(false);  
  215.             mTextPaint.setTextSkewX(0);  
  216.             setSwitchTypeface(tf);  
  217.         }  
  218.     }  
  219.   
  220.     /** 
  221.      * Sets the typeface in which the text should be displayed on the switch. 
  222.      * Note that not all Typeface families actually have bold and italic 
  223.      * variants, so you may need to use 
  224.      * {@link #setSwitchTypeface(Typeface, int)} to get the appearance 
  225.      * that you actually want. 
  226.      * 
  227.      * @attr ref android.R.styleable#TextView_typeface 
  228.      * @attr ref android.R.styleable#TextView_textStyle 
  229.      */  
  230.     public void setSwitchTypeface(Typeface tf) {  
  231.         if (mTextPaint.getTypeface() != tf) {  
  232.             mTextPaint.setTypeface(tf);  
  233.   
  234.             requestLayout();  
  235.             invalidate();  
  236.         }  
  237.     }  
  238.   
  239.     /** 
  240.      * Returns the text displayed when the button is in the checked state. 
  241.      */  
  242.     public CharSequence getTextOn() {  
  243.         return mTextOn;  
  244.     }  
  245.   
  246.     /** 
  247.      * Sets the text displayed when the button is in the checked state. 
  248.      */  
  249.     public void setTextOn(CharSequence textOn) {  
  250.         mTextOn = textOn;  
  251.         requestLayout();  
  252.     }  
  253.   
  254.     /** 
  255.      * Returns the text displayed when the button is not in the checked state. 
  256.      */  
  257.     public CharSequence getTextOff() {  
  258.         return mTextOff;  
  259.     }  
  260.   
  261.     /** 
  262.      * Sets the text displayed when the button is not in the checked state. 
  263.      */  
  264.     public void setTextOff(CharSequence textOff) {  
  265.         mTextOff = textOff;  
  266.         requestLayout();  
  267.     }  
  268.   
  269.     @Override  
  270.     public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  271.         final int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
  272.         final int heightMode = MeasureSpec.getMode(heightMeasureSpec);  
  273.         int widthSize = MeasureSpec.getSize(widthMeasureSpec);  
  274.         int heightSize = MeasureSpec.getSize(heightMeasureSpec);  
  275.   
  276.   
  277.         if (mOnLayout == null) {  
  278.             mOnLayout = makeLayout(mTextOn);  
  279.         }  
  280.         if (mOffLayout == null) {  
  281.             mOffLayout = makeLayout(mTextOff);  
  282.         }  
  283.   
  284.         mTrackDrawable.getPadding(mTempRect);  
  285.         final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth());  
  286.         final int switchWidth = Math.max(mSwitchMinWidth,  
  287.                 maxTextWidth * 2 + mThumbTextPadding * 4 + mTempRect.left + mTempRect.right);  
  288.         final int switchHeight = mTrackDrawable.getIntrinsicHeight();  
  289.   
  290.         mThumbWidth = maxTextWidth + mThumbTextPadding * 2;  
  291.   
  292.         switch (widthMode) {  
  293.             case MeasureSpec.AT_MOST:  
  294.                 widthSize = Math.min(widthSize, switchWidth);  
  295.                 break;  
  296.   
  297.             case MeasureSpec.UNSPECIFIED:  
  298.                 widthSize = switchWidth;  
  299.                 break;  
  300.   
  301.             case MeasureSpec.EXACTLY:  
  302.                 // Just use what we were given  
  303.                 break;  
  304.         }  
  305.   
  306.         switch (heightMode) {  
  307.             case MeasureSpec.AT_MOST:  
  308.                 heightSize = Math.min(heightSize, switchHeight);  
  309.                 break;  
  310.   
  311.             case MeasureSpec.UNSPECIFIED:  
  312.                 heightSize = switchHeight;  
  313.                 break;  
  314.   
  315.             case MeasureSpec.EXACTLY:  
  316.                 // Just use what we were given  
  317.                 break;  
  318.         }  
  319.   
  320.         mSwitchWidth = switchWidth;  
  321.         mSwitchHeight = switchHeight;  
  322.   
  323.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  324.         final int measuredHeight = getMeasuredHeight();  
  325.         if (measuredHeight < switchHeight) {  
  326.             setMeasuredDimension(getMeasuredWidth(), switchHeight);  
  327.         }  
  328.     }  
  329.   
  330.    /* @Override 
  331.     public boolean dispatchTouchEvent(MotionEvent event) { 
  332.         Log.d("SvenDebug", "dispatchTouchEvent:action=" + event.getAction()); 
  333.         return false; 
  334.     }*/  
  335.   
  336.     @Override  
  337.     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {  
  338.         Log.d("SvenDebug""dispatchPopulateAccessibilityEvent");  
  339.         populateAccessibilityEvent(event);  
  340.   
  341.         return false;  
  342.     }  
  343.   
  344.     public void populateAccessibilityEvent(AccessibilityEvent event) {  
  345.         if (isChecked()) {  
  346.             CharSequence text = mOnLayout.getText();  
  347.             if (TextUtils.isEmpty(text)) {  
  348.                 text = mContext.getString(R.string.switch_on);  
  349.             }  
  350.             event.getText().add(text);  
  351.         } else {  
  352.             CharSequence text = mOffLayout.getText();  
  353.             if (TextUtils.isEmpty(text)) {  
  354.                 text = mContext.getString(R.string.switch_off);  
  355.             }  
  356.             event.getText().add(text);  
  357.         }  
  358.     }  
  359.   
  360.     private Layout makeLayout(CharSequence text) {  
  361.         return new StaticLayout(text, mTextPaint,  
  362.                 (int) Math.ceil(Layout.getDesiredWidth(text, mTextPaint)),  
  363.                 Layout.Alignment.ALIGN_NORMAL, 1.f, 0true);  
  364.     }  
  365.   
  366.     /** 
  367.      * @return true if (x, y) is within the target area of the switch thumb 
  368.      */  
  369.     private boolean hitThumb(float x, float y) {  
  370.         mThumbDrawable.getPadding(mTempRect);  
  371.         final int thumbTop = mSwitchTop - mTouchSlop;  
  372.         final int thumbLeft = mSwitchLeft + (int) (mThumbPosition + 0.5f) - mTouchSlop;  
  373.         final int thumbRight = thumbLeft + mThumbWidth +  
  374.                 mTempRect.left + mTempRect.right + mTouchSlop;  
  375.         final int thumbBottom = mSwitchBottom + mTouchSlop;  
  376.         return x > thumbLeft && x < thumbRight && y > thumbTop && y < thumbBottom;  
  377.     }  
  378.   
  379.     @Override  
  380.     public boolean onTouchEvent(MotionEvent ev) {  
  381.         mVelocityTracker.addMovement(ev);  
  382.         final int action = ev.getActionMasked();  
  383.         Log.d("SvenDebug""MotionEvent : " + action);  
  384.         switch (action) {  
  385.             case MotionEvent.ACTION_DOWN: {  
  386.                 Log.d("SvenDebug""MotionEvent.ACTION_DOWN");  
  387.                 final float x = ev.getX();  
  388.                 final float y = ev.getY();  
  389.                 if (isEnabled() && hitThumb(x, y)) {  
  390.                     Log.d("SvenDebug""Enable in widget rect");  
  391.                     mTouchMode = TOUCH_MODE_DOWN;  
  392.                     mTouchX = x;  
  393.                     mTouchY = y;  
  394.                 }  
  395.                 break;  
  396.             }  
  397.   
  398.             case MotionEvent.ACTION_MOVE: {  
  399.                 Log.d("SvenDebug""MotionEvent.ACTION_MOVE");  
  400.                 switch (mTouchMode) {  
  401.                     case TOUCH_MODE_IDLE:  
  402.                         // Didn't target the thumb, treat normally.  
  403.                         break;  
  404.   
  405.                     case TOUCH_MODE_DOWN: {  
  406.                         Log.d("SvenDebug""TOUCH_MODE_DOWN:mTouchSlop = " + mTouchSlop);  
  407.                         final float x = ev.getX();  
  408.                         final float y = ev.getY();  
  409.                         if (Math.abs(x - mTouchX) > mTouchSlop ||  
  410.                                 Math.abs(y - mTouchY) > mTouchSlop) {  
  411.                             mTouchMode = TOUCH_MODE_DRAGGING;  
  412.                             getParent().requestDisallowInterceptTouchEvent(true);  
  413.                             mTouchX = x;  
  414.                             mTouchY = y;  
  415.                             return true;  
  416.                         }  
  417.                         break;  
  418.                     }  
  419.   
  420.                     case TOUCH_MODE_DRAGGING: {  
  421.                         Log.d("SvenDebug""TOUCH_MODE_DRAGGING");  
  422.                         final float x = ev.getX();  
  423.                         final float dx = x - mTouchX;  
  424.                         float newPos = Math.max(0,  
  425.                                 Math.min(mThumbPosition + dx, getThumbScrollRange()));  
  426.                         if (newPos != mThumbPosition) {  
  427.                             mThumbPosition = newPos;  
  428.                             mTouchX = x;  
  429.                             invalidate();  
  430.                         }  
  431.                         return true;  
  432.                     }  
  433.                 }  
  434.                 break;  
  435.             }  
  436.   
  437.             case MotionEvent.ACTION_UP:  
  438.             case MotionEvent.ACTION_CANCEL: {  
  439.                 Log.d("SvenDebug""MotionEvent.ACTION_UP|ACTION_CANCEL");  
  440.                 if (mTouchMode == TOUCH_MODE_DRAGGING) {  
  441.                     stopDrag(ev);  
  442.                     return true;  
  443.                 }  
  444.                 mTouchMode = TOUCH_MODE_IDLE;  
  445.                 mVelocityTracker.clear();  
  446.                 break;  
  447.             }  
  448.         }  
  449.   
  450.         return super.onTouchEvent(ev);  
  451.     }  
  452.   
  453.     private void cancelSuperTouch(MotionEvent ev) {  
  454.         MotionEvent cancel = MotionEvent.obtain(ev);  
  455.         cancel.setAction(MotionEvent.ACTION_CANCEL);  
  456.         super.onTouchEvent(cancel);  
  457.         cancel.recycle();  
  458.     }  
  459.   
  460.     /** 
  461.      * Called from onTouchEvent to end a drag operation. 
  462.      * 
  463.      * @param ev Event that triggered the end of drag mode - ACTION_UP or ACTION_CANCEL 
  464.      */  
  465.     private void stopDrag(MotionEvent ev) {  
  466.         mTouchMode = TOUCH_MODE_IDLE;  
  467.         // Up and not canceled, also checks the switch has not been disabled during the drag  
  468.         boolean commitChange = ev.getAction() == MotionEvent.ACTION_UP && isEnabled();  
  469.   
  470.         cancelSuperTouch(ev);  
  471.   
  472.         if (commitChange) {  
  473.             boolean newState;  
  474.             mVelocityTracker.computeCurrentVelocity(1000);  
  475.             float xvel = mVelocityTracker.getXVelocity();  
  476.             if (Math.abs(xvel) > mMinFlingVelocity) {  
  477.                 newState = xvel > 0;  
  478.             } else {  
  479.                 newState = getTargetCheckedState();  
  480.             }  
  481.             animateThumbToCheckedState(newState);  
  482.         } else {  
  483.             animateThumbToCheckedState(isChecked());  
  484.         }  
  485.     }  
  486.   
  487.     private void animateThumbToCheckedState(boolean newCheckedState) {  
  488.         // TODO animate!  
  489.         //float targetPos = newCheckedState ? 0 : getThumbScrollRange();  
  490.         //mThumbPosition = targetPos;  
  491.         setChecked(newCheckedState);  
  492.     }  
  493.   
  494.     private boolean getTargetCheckedState() {  
  495.         return mThumbPosition >= getThumbScrollRange() / 2;  
  496.     }  
  497.   
  498.     @Override  
  499.     public void setChecked(boolean checked) {  
  500.         super.setChecked(checked);  
  501.         mThumbPosition = checked ? getThumbScrollRange() : 0;  
  502.         invalidate();  
  503.     }  
  504.   
  505.     @Override  
  506.     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {  
  507.         super.onLayout(changed, left, top, right, bottom);  
  508.   
  509.         mThumbPosition = isChecked() ? getThumbScrollRange() : 0;  
  510.   
  511.         int switchRight = getWidth() - getPaddingRight();  
  512.         int switchLeft = switchRight - mSwitchWidth;  
  513.         int switchTop = 0;  
  514.         int switchBottom = 0;  
  515.         switch (getGravity() & Gravity.VERTICAL_GRAVITY_MASK) {  
  516.             default:  
  517.             case Gravity.TOP:  
  518.                 switchTop = getPaddingTop();  
  519.                 switchBottom = switchTop + mSwitchHeight;  
  520.                 break;  
  521.   
  522.             case Gravity.CENTER_VERTICAL:  
  523.                 switchTop = (getPaddingTop() + getHeight() - getPaddingBottom()) / 2 -  
  524.                         mSwitchHeight / 2;  
  525.                 switchBottom = switchTop + mSwitchHeight;  
  526.                 break;  
  527.   
  528.             case Gravity.BOTTOM:  
  529.                 switchBottom = getHeight() - getPaddingBottom();  
  530.                 switchTop = switchBottom - mSwitchHeight;  
  531.                 break;  
  532.         }  
  533.   
  534.         mSwitchLeft = switchLeft;  
  535.         mSwitchTop = switchTop;  
  536.         mSwitchBottom = switchBottom;  
  537.         mSwitchRight = switchRight;  
  538.     }  
  539.   
  540.     @Override  
  541.     protected void onDraw(Canvas canvas) {  
  542.         super.onDraw(canvas);  
  543.   
  544.         // Draw the switch  
  545.         int switchLeft = mSwitchLeft;  
  546.         int switchTop = mSwitchTop;  
  547.         int switchRight = mSwitchRight;  
  548.         int switchBottom = mSwitchBottom;  
  549.   
  550.         mTrackDrawable.setBounds(switchLeft, switchTop, switchRight, switchBottom);  
  551.         mTrackDrawable.draw(canvas);  
  552.   
  553.         canvas.save();  
  554.   
  555.         mTrackDrawable.getPadding(mTempRect);  
  556.         int switchInnerLeft = switchLeft + mTempRect.left;  
  557.         int switchInnerTop = switchTop + mTempRect.top;  
  558.         int switchInnerRight = switchRight - mTempRect.right;  
  559.         int switchInnerBottom = switchBottom - mTempRect.bottom;  
  560.         canvas.clipRect(switchInnerLeft, switchTop, switchInnerRight, switchBottom);  
  561.   
  562.         // FIXME:  
  563.         //Drawable offDrawable = mContext.getResources().getDrawable(R.drawable.switch_thumb_mz);  
  564.         //Drawable onDrawable = mContext.getResources().getDrawable(R.drawable.switch_thumb_activated_mz);  
  565.         //mThumbDrawable = getTargetCheckedState() ? onDrawable : offDrawable;  
  566.   
  567.         mThumbDrawable.getPadding(mTempRect);  
  568.         final int thumbPos = (int) (mThumbPosition + 0.5f);  
  569.         int thumbLeft = switchInnerLeft - mTempRect.left + thumbPos;  
  570.         int thumbRight = switchInnerLeft + thumbPos + mThumbWidth + mTempRect.right;  
  571.   
  572.         mThumbDrawable.setBounds(thumbLeft, switchTop, thumbRight, switchBottom);  
  573.         mThumbDrawable.draw(canvas);  
  574.   
  575.         // mTextColors should not be null, but just in case  
  576.         if (mTextColors != null) {  
  577.             mTextPaint.setColor(mTextColors.getColorForState(getDrawableState(),  
  578.                     mTextColors.getDefaultColor()));  
  579.         }  
  580.         mTextPaint.drawableState = getDrawableState();  
  581.   
  582.         Layout switchText = getTargetCheckedState() ? mOnLayout : mOffLayout;  
  583.   
  584.         canvas.translate((thumbLeft + thumbRight) / 2 - switchText.getWidth() / 2,  
  585.                 (switchInnerTop + switchInnerBottom) / 2 - switchText.getHeight() / 2);  
  586.         switchText.draw(canvas);  
  587.   
  588.         canvas.restore();  
  589.     }  
  590.   
  591.     @Override  
  592.     public int getCompoundPaddingRight() {  
  593.         int padding = super.getCompoundPaddingRight() + mSwitchWidth;  
  594.         if (!TextUtils.isEmpty(getText())) {  
  595.             padding += mSwitchPadding;  
  596.         }  
  597.         return padding;  
  598.     }  
  599.   
  600.     private int getThumbScrollRange() {  
  601.         if (mTrackDrawable == null) {  
  602.             return 0;  
  603.         }  
  604.         mTrackDrawable.getPadding(mTempRect);  
  605.         return mSwitchWidth - mThumbWidth - mTempRect.left - mTempRect.right;  
  606.     }  
  607.   
  608.     @Override  
  609.     protected int[] onCreateDrawableState(int extraSpace) {  
  610.         final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);  
  611.         if (isChecked()) {  
  612.             mergeDrawableStates(drawableState, CHECKED_STATE_SET);  
  613.         }  
  614.         return drawableState;  
  615.     }  
  616.   
  617.     @Override  
  618.     protected void drawableStateChanged() {  
  619.         super.drawableStateChanged();  
  620.   
  621.         int[] myDrawableState = getDrawableState();  
  622.   
  623.         // Set the state of the Drawable  
  624.         // Drawable may be null when checked state is set from XML, from super constructor  
  625.         if (mThumbDrawable != null) mThumbDrawable.setState(myDrawableState);  
  626.         if (mTrackDrawable != null) mTrackDrawable.setState(myDrawableState);  
  627.   
  628.         invalidate();  
  629.     }  
  630.   
  631.     @Override  
  632.     protected boolean verifyDrawable(Drawable who) {  
  633.         return super.verifyDrawable(who) || who == mThumbDrawable || who == mTrackDrawable;  
  634.     }  
  635.   
  636.     /*@Override 
  637.     public void jumpDrawablesToCurrentState() { 
  638.         super.jumpDrawablesToCurrentState(); 
  639.         mThumbDrawable.jumpToCurrentState(); 
  640.         mTrackDrawable.jumpToCurrentState(); 
  641.     }*/  
  642. }  
项目下载: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