Android 4.0 Switch 控件用源代码实现
来源:互联网 发布:运动鞋哪些牌子 知乎 编辑:程序博客网 时间:2024/06/08 08:56
最近项目需要,在4.0 以前的版本上实现一个android 4.0 上的Switch控件,上网找了些例子,都不尽如人意。自己动手,丰衣足食。于是便翻看源码,找到4.0源码中Switch实现的方法。照样子实现了一个,基本上跟4.0的效果一样,使用上没有任何差异。
直接上图:
以下是Switch控件实现的源码:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
public
class
Switch
extends
CompoundButton {...
public
Switch(Context context) {
this
(context,
null
); mContext = context;
}
public
Switch(Context context, AttributeSet attrs) {
this
(context, attrs, R.attr.switchStyle); mContext = context;
}
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;
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
);
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(); refreshDrawableState();
setChecked(isChecked());
}
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
{
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);
}
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);
}
}
public
void
setSwitchTypeface(Typeface tf) {
if
(mTextPaint.getTypeface() != tf) {
mTextPaint.setTypeface(tf); requestLayout();
invalidate();
}
}
public
CharSequence getTextOn() {
return
mTextOn;
}
public
void
setTextOn(CharSequence textOn) {
mTextOn = textOn;
requestLayout();
}
public
CharSequence getTextOff() {
return
mTextOff;
}
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
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
);
}
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();
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)) {
mTouchMode = TOUCH_MODE_DOWN;
mTouchX = x;
mTouchY = y;
}
break
;
}
case
MotionEvent.ACTION_MOVE: {
switch
(mTouchMode) {
case
TOUCH_MODE_IDLE:
// Didn't target the thumb, treat normally.
break
;
case
TOUCH_MODE_DOWN: {
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: {
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();
}
private
void
stopDrag(MotionEvent ev) {
mTouchMode = TOUCH_MODE_IDLE;
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) {
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);
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);
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();
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;
}}
1. 注释掉了 jumpDrawablesToCurrentState() 这个函数,因为这是4.0的Drawable 里面新增的回调接口,这里不需要,就注释掉;
2. 4.0源码中的onPopulateAccessibilityEvent 这个函数是新增的回调函数,低级的SDK是没有的,怎么办。查看源码,发现这个函数只是在dispatchPopulateAccessibilityEvent调用了,将源码的Switch中的onPopulateAccessibilityEvent改成了poulateAccessibilityEvent还是在Switch的dispatchPopulateAccessibilityEvent调用就可以了。
还有就是需要增加Switch控件的属性,做过自定义控件属性的同学大概都知道。不多说,帖代码:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
<declare-styleable name=
"Switch"
>
<!-- 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>
为了方便使用,直接定义了一个switch的style,设置了背景图片,拖拽的图片,显示文字等:
01
02
03
04
05
06
07
08
09
10
11
12
<style name=
"Switch"
>
<item name=
"track"
>
@drawable
/switch_track_holo_dark</item>
<item name=
"thumb"
>
@drawable
/switch_inner_holo_dark</item>
<!-- <item name=
"track"
>
@drawable
/switch_bg_default_mz</item>
<item name=
"thumb"
>
@drawable
/switch_thumb_activated_mz</item> -->
<item name=
"switchTextAppearance"
>
@android
:style/TextAppearance.Small</item>
<item name=
"textOn"
>
@string
/switch_on</item>
<item name=
"textOff"
>
@string
/switch_off</item>
<item name=
"thumbTextPadding"
>12dip</item>
<item name=
"switchMinWidth"
>96dip</item>
<item name=
"switchPadding"
>16dip</item>
</style>
0 0
- Android 4.0 Switch 控件用源代码实现
- android 4.0新控件 Switch 方法解析
- android 4.0新控件 Switch 方法解析
- android 4.0 switch控件的使用
- Android 4.0新控件 switch的属性
- Android Switch控件介绍
- Android Switch控件介绍
- Android自定义Switch控件
- Android控件之Switch
- android 自定义控件 Switch
- Android入门----Switch控件
- 自定义Android Switch控件
- Android UI Switch控件
- 【Android】自定义控件实现可滑动的开关(switch)
- Android控件ToggleButton和Switch
- Android控件之ToggleButton、Switch
- android studio switch控件使用
- Android Switch控件修改样式
- Ng机器学习课程Notes学习及编程实战系列-Part 2 Logistic Regression
- android LinearLayout布局中layout_weight的使用(二)
- (十)串的基本概念与串存储结构
- 孙鑫VC++(深入详解)视频教程
- vs2010 配置qt
- Android 4.0 Switch 控件用源代码实现
- USACO Section 1.5 Checker Challenge
- fgets和gets的区别
- fail-fast机制
- ANT详解
- Candy 分糖果
- github连接被拒绝的问题
- 导致DllMain中死锁的关键隐藏因子
- fail-fast机制