图片设置开关

  1. 准备开关不同状态的两张图片放入drawable中。
  2. xml文件中添加代码:
1
2
3
4
5
6
7
8
9
10
<ToggleButton
android:id="@+id/switch1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@android:color/transparent"
android:button="@drawable/btn_backgrounds"
android:checked="false"
android:textOff=""
android:textOn="" />
1
2
3
4
5
6
android:button="@drawable/btn_backgrounds"是自己定义的android selector文件:
<selector
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:drawable="@drawable/toggle_btn_on" />
<item android:drawable="@drawable/toggle_btn_off" />
</selector>
  1. 之后在java文件中声明ToggleButton控件并且实例化,添加点击事件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 自己出创建的内部类,监听按钮点击事件
*
* @author cyf
*
*/
class MyOnCheckedChangeListener implements OnCheckedChangeListener {

@Override
public void onCheckedChanged(CompoundButton arg0, boolean arg1) {
if (arg1) {
// 开
} else {
// 关
}
}
}
  1. 添加监听:
1
switch1.setOnCheckedChangeListener((OnCheckedChangeListener) new MyOnCheckedChangeListener());

参考:https://www.cnblogs.com/yunfang/p/5485390.html

自定义动画

Github 项目地址:https://github.com/xiaosong520/SwitchButtonDemo.git

步骤:

1.创建SwitchButton类添加到自己的项目中:

1
2
3
4
5
6
7
8
9
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
/**
* @TODO<自定义选择开关按钮>
* @author 小嵩
* @date 2016-8-5 09:57:29
*/
public class SwitchButton extends View{
/** 圆角大小*/
private float radius;
/** 开启颜色*/
private int onColor = Color.parseColor("#4ebb7f");
/** 关闭颜色*/
private int offBorderColor = Color.parseColor("#dadbda");
/** 灰色带颜色*/
private int offColor = Color.parseColor("#ffffff");
/** 手柄颜色*/
private int spotColor = Color.parseColor("#ffffff");
/** 边框颜色*/
private int borderColor = offBorderColor;
/** 画笔*/
private Paint paint ;
/** 开关状态*/
private boolean toggleOn = false;
/** 边框大小*/
private int borderWidth = 2;
/** 垂直中心*/
private float centerY;
/** 按钮的开始和结束位置*/
private float startX, endX;
/** 手柄X位置的最小和最大值*/
private float spotMinX, spotMaxX;
/**手柄大小 */
private int spotSize ;
/** 手柄X位置*/
private float spotX;
/** 关闭时内部灰色带高度*/
private float offLineWidth;
/** */
private RectF rect = new RectF();
/** 默认使用动画*/
private boolean defaultAnimate = true;

private OnToggleChanged listener;

private SwitchButton(Context context) {
super(context);
}
public SwitchButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setup(attrs);
}
public SwitchButton(Context context, AttributeSet attrs) {
super(context, attrs);
setup(attrs);
}

public void setup(AttributeSet attrs) {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Style.FILL);
paint.setStrokeCap(Cap.ROUND);

this.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
toggle(defaultAnimate);
}
});

TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.SwitchButton);
offBorderColor = typedArray.getColor(R.styleable.SwitchButton_offBorderColor, offBorderColor);
onColor = typedArray.getColor(R.styleable.SwitchButton_onColor, onColor);
spotColor = typedArray.getColor(R.styleable.SwitchButton_spotColor, spotColor);
offColor = typedArray.getColor(R.styleable.SwitchButton_offColor, offColor);
borderWidth = typedArray.getDimensionPixelSize(R.styleable.SwitchButton_borderWidth, borderWidth);
defaultAnimate = typedArray.getBoolean(R.styleable.SwitchButton_animate, defaultAnimate);
typedArray.recycle();

borderColor = offBorderColor;
}

public void toggle() {
toggle(true);
}

public void toggle(boolean animate) {
toggleOn = !toggleOn;
takeEffect(animate);

if(listener != null){
listener.onToggle(toggleOn);
}
}

public void toggleOn() {
setToggleOn();
if(listener != null){
listener.onToggle(toggleOn);
}
}

public void toggleOff() {
setToggleOff();
if(listener != null){
listener.onToggle(toggleOn);
}
}

/**
* 设置显示成打开样式,不会触发toggle事件
*/
public void setToggleOn() {
setToggleOn(true);
}

/**
* @param animate
*/
public void setToggleOn(boolean animate){
toggleOn = true;
takeEffect(animate);
}

/**
* 设置显示成关闭样式,不会触发toggle事件
*/
public void setToggleOff() {
setToggleOff(true);
}

public void setToggleOff(boolean animate) {
toggleOn = false;
takeEffect(animate);
}

private void takeEffect(boolean animate) {
if(animate){
slide();
}else{
calculateEffect(toggleOn ? 1 : 0);
}
}


@Override
protected 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);

Resources r = Resources.getSystem();
if(widthMode == MeasureSpec.UNSPECIFIED || widthMode == MeasureSpec.AT_MOST){
widthSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, r.getDisplayMetrics());
widthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
}

if(heightMode == MeasureSpec.UNSPECIFIED || heightSize == MeasureSpec.AT_MOST){
heightSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, r.getDisplayMetrics());
heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY);
}

super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}


@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
super.onLayout(changed, left, top, right, bottom);

final int width = getWidth();
final int height = getHeight();

radius = Math.min(width, height) * 0.5f;
centerY = radius;
startX = radius;
endX = width - radius;
spotMinX = startX + borderWidth;
spotMaxX = endX - borderWidth;
spotSize = height - 4 * borderWidth;
spotX = toggleOn ? spotMaxX : spotMinX;
offLineWidth = 0;
}

private void slide(){
Animation animation = new Animation() {
@Override
protected void applyTransformation(float interpolatedTime,
Transformation t) {
if(toggleOn){
calculateEffect(interpolatedTime);
}else{
calculateEffect(1-interpolatedTime);
}
}
};
animation.setDuration(200);
clearAnimation();
startAnimation(animation);
}

private int clamp(int value, int low, int high) {
return Math.min(Math.max(value, low), high);
}


@Override
public void draw(Canvas canvas) {
//
rect.set(0, 0, getWidth(), getHeight());
paint.setColor(borderColor);
canvas.drawRoundRect(rect, radius, radius, paint);

if(offLineWidth > 0){
final float cy = offLineWidth * 0.5f;
rect.set(spotX - cy, centerY - cy, endX + cy, centerY + cy);
paint.setColor(offColor);
canvas.drawRoundRect(rect, cy, cy, paint);
}

rect.set(spotX - 1 - radius, centerY - radius, spotX + 1.1f + radius, centerY + radius);
paint.setColor(borderColor);
canvas.drawRoundRect(rect, radius, radius, paint);

final float spotR = spotSize * 0.5f;
rect.set(spotX - spotR, centerY - spotR, spotX + spotR, centerY + spotR);
paint.setColor(spotColor);
canvas.drawRoundRect(rect, spotR, spotR, paint);

}

/**
* @param value
*/
private void calculateEffect(final double value) {
final float mapToggleX = (float) mapValueFromRangeToRange(value, 0, 1, spotMinX, spotMaxX);
spotX = mapToggleX;

float mapOffLineWidth = (float) mapValueFromRangeToRange(1 - value, 0, 1, 10, spotSize);

offLineWidth = mapOffLineWidth;

final int fb = Color.blue(onColor);
final int fr = Color.red(onColor);
final int fg = Color.green(onColor);

final int tb = Color.blue(offBorderColor);
final int tr = Color.red(offBorderColor);
final int tg = Color.green(offBorderColor);

int sb = (int) mapValueFromRangeToRange(1 - value, 0, 1, fb, tb);
int sr = (int) mapValueFromRangeToRange(1 - value, 0, 1, fr, tr);
int sg = (int) mapValueFromRangeToRange(1 - value, 0, 1, fg, tg);

sb = clamp(sb, 0, 255);
sr = clamp(sr, 0, 255);
sg = clamp(sg, 0, 255);

borderColor = Color.rgb(sr, sg, sb);

postInvalidate();
}

public interface OnToggleChanged{
/**
* @param on
*/
public void onToggle(boolean on);
}


public void setOnToggleChanged(OnToggleChanged onToggleChanged) {
listener = onToggleChanged;
}

public boolean isAnimate() {
return defaultAnimate;
}
public void setAnimate(boolean animate) {
this.defaultAnimate = animate;
}

/**
* Map a value within a given range to another range.
* @param value the value to map
* @param fromLow the low end of the range the value is within
* @param fromHigh the high end of the range the value is within
* @param toLow the low end of the range to map to
* @param toHigh the high end of the range to map to
* @return the mapped value
*/
public static double mapValueFromRangeToRange(
double value,
double fromLow,
double fromHigh,
double toLow,
double toHigh) {
double fromRangeSize = fromHigh - fromLow;
double toRangeSize = toHigh - toLow;
double valueScale = (value - fromLow) / fromRangeSize;
return toLow + (valueScale * toRangeSize);
}
}

2.在项目res - values目录下 - 找到 attrs.xml 文件,在resource中间添加如下代码:(文件名好像无所谓)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SwitchButton">
<attr name="borderWidth" format="dimension">
</attr>
<attr name="offBorderColor" format="reference|color">
</attr>
<attr name="offColor" format="reference|color">
</attr>
<attr name="onColor" format="reference|color">
</attr>
<attr name="spotColor" format="reference|color">
</attr>
<attr name="animate" format="reference|boolean">
</attr>
</declare-styleable>
</resources>

以及 colors.xml 文件:

1
2
3
4
5
6
<!-- 白色 -->
<color name="common_white">#FFFFFF</color>
<!-- 浅灰色 -->
<color name="light_gray">#DDDDDD</color>
<!-- 浅蓝色 -->
<color name="common_light_blue">#039AE3</color>

3.在XML布局文件中使用控件(路径引用需改成SwitchButton类所在目录):

1
2
3
4
5
6
7
8
9
10
11
<com.yourpath.SwitchButton
android:id="@+id/switchButton"
android:layout_width="45dp"
android:layout_height="25dp"
app:borderWidth="0.8dp"
app:offBorderColor="@color/light_gray"
app:offColor="@color/common_white"
app:onColor="@color/common_light_blue"
app:spotColor="@color/common_white"
app:animate="true"
android:layout_margin="20dp" />

控件的属性介绍:

  • borderWidth 描边宽度
  • offBorderColor 关闭状态下描边的颜色
  • offColor 关闭状态的颜色
  • onColor 打开状态的颜色
  • spotColor 控件中间的圆点的颜色
  • animate 是否打开动画效果

4.在Activity中使用:

4.1绑定控件:
如果使用ButterKinfe库的话:

1
@Bind(R.id.switchButton) SwitchButton switchButton;

findViewById的方式:

1
SwitchButton switchButton = (SwitchButton)findViewById(R.id.switchButton);

4.2使用控件:

1
2
3
4
5
6
7
8
switchButton.setToggleOn(false);//默认打开。如果参数传false,则打开页面初始化时不会有动画效果(改变状态还是会有动画) 
switchButton.setOnToggleChanged(new SwitchButton.OnToggleChanged(){
@Override
public void onToggle(boolean isOn) {
//处理自己的逻辑
showToast( "SwitchButton"+isOn);
}
});

参考:https://blog.csdn.net/qq_22393017/article/details/52198453