您的位置:首页 > 移动开发 > Android开发

Android 自定义带动画效果的开关按钮

2016-08-13 11:35 417 查看
自定义带动画效果的开关按钮

        一个自定义控制开关的控件,简单实用,效果特别棒。项目中常会用到,比那种用两张图片做按钮背景,selector选择的方式,视觉效果好很多。忘了是以前从哪找的了,本着开源的精神整理了一下分享出来。好了,废话不多说,先展示效果图:

 


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


步骤:

1.创建SwitchButton类添加到自己的项目中:
/**
* @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中间添加如下代码:
<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>


3.在XML布局文件中使用控件(路径引用需改成SwitchButton类所在目录):
<com.ui.widget.view.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库的话:

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

findViewById的方式:

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


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



5.没有第五步了,已经大功告成~ 有问题欢迎call我,一起讨论~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: