SwitchView

简介: 今天写了一个很简单的开关控件,仿华为手机的,先看效果图...这是华为的001.png这是我的003.gif代码 很简单的package org.

今天写了一个很简单的开关控件,仿华为手机的,先看效果图

...

这是华为的

001.png

这是我的

003.gif

代码 很简单的

<pre>

package org.alex.switchview;
/**
* 作者:Alex
* 时间:2016/10/21 12:59
* 简述:
*/
public class SwitchView extends View implements View.OnClickListener {
private final int defaultLightColor = 0xFF4BD763;
private final int defaultDarkColor = 0xFFE3E3E3;
private final int defaultCircleSlideColor = 0xFFFFFFFF;
private final float defaultCircleScale = 0.90F;
private final int defaultOpenDuration = 300;
private final int defaultCloseDuration = 300;
private Paint paint;
/**
* 左右两端是半圆,中间是矩形的跑道
*/
private Path runwayPath;
/**
* 中间的 小滑块
*/
private Path circleSlidePath;
/**
* 中间的 小滑块
*/
private RectF circleSlideRectF;
private int lightColor;
private int darkColor;
private int circleSlideColor;
private boolean isOpened;
private int width, height;
private int viewBottom, viewTop, viewLeft, viewRight;
private float runwayHeight;
/**
* 中间小滑块 占 滑动开关的 高度比
*/
private float circleSlideScale;
private float circleSlideHeight;
private float circleSlideLeft, circleSlideTop, circleSlideRight, circleSlideBottom;

private int openDuration, closeDuration;
private boolean isCanVisibleDrawing;
/\*\*
 \* 小滑块 和 外层 跑道之间的 间距
 \*/
private float circleSlideOutGap;
/\*\*
 \* 打开开关 动画
 \*/
private ValueAnimator openAnimator;
/\*\*
 \* 关闭开关 动画
 \*/
private ValueAnimator closeAnimator;
/\*\*
 \* 0 表示在最左端
 \* 1 表示在最右端
 \*/
private float progress;
/\*\*
 \* 小滑块 滑动的 距离
 \*/
private float circleSlideRunLength;

private int swStatus;
private OnChangeListener onChangeListener;

public SwitchView(Context context, AttributeSet attrs) {
    super(context, attrs);
    initView(context, attrs);
}

private void initView(Context context, AttributeSet attrs) {
    TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SwitchView);
    lightColor = typedArray.getColor(R.styleable.SwitchView_sv_lightColor, defaultLightColor);
    darkColor = typedArray.getColor(R.styleable.SwitchView_sv_darkColor, defaultDarkColor);
    circleSlideColor = typedArray.getColor(R.styleable.SwitchView_sv_circleSlideColor, defaultCircleSlideColor);
    circleSlideScale = typedArray.getFloat(R.styleable.SwitchView_sv_circleSlideScale, defaultCircleScale);
    openDuration = typedArray.getInt(R.styleable.SwitchView_sv_openDuration, defaultOpenDuration);
    closeDuration = typedArray.getInt(R.styleable.SwitchView_sv_closeDuration, defaultCloseDuration);
    isOpened = typedArray.getBoolean(R.styleable.SwitchView_sv_isOpened, false);
    typedArray.recycle();
    progress = isOpened ? 1F : 0F;
    swStatus = SwStatus.isNone;
    isCanVisibleDrawing = false;
    setLayerType(LAYER_TYPE_SOFTWARE, null);
    paint = new Paint();
    runwayPath = new Path();
    circleSlidePath = new Path();
    circleSlideRectF = new RectF();
    /\*关联动画\*/
    openAnimator = ValueAnimator.ofFloat(0f, 1.0f);
    closeAnimator = ValueAnimator.ofFloat(1.0f, 0.0f);

    closeAnimator.setDuration(closeDuration);
    closeAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
    closeAnimator.addUpdateListener(new MyAnimatorUpdateListener("closeAnimator"));

    openAnimator.setDuration(openDuration);
    openAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
    openAnimator.addUpdateListener(new MyAnimatorUpdateListener("openAnimator"));
    setOnClickListener(this);
}

private final class MyAnimatorUpdateListener implements ValueAnimator.AnimatorUpdateListener {
    private String tag;

    public MyAnimatorUpdateListener(String tag) {
        this.tag = tag;
    }

    /\*\*
     \* <p>Notifies the occurrence of another frame of the animation.</p>
     \*
     \* @param animation The animation which was repeated.
     \*/
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        progress = (float) animation.getAnimatedValue();
        if ("openAnimator".equals(tag)) {
            swStatus = progress > 0.99F ? SwStatus.isNone : SwStatus.is2Open;
        } else if ("closeAnimator".equals(tag)) {
            swStatus = progress < 0.01F ? SwStatus.isNone : SwStatus.is2Close;
        }
        invalidate();
    }
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    if (!isCanVisibleDrawing) {
        return;
    }
    paint.setAntiAlias(true);
    final boolean isOn = isOpened;
    /\*画背景\*/
    paint.setStyle(Paint.Style.FILL);
    paint.setColor(isOn ? lightColor : darkColor);
    canvas.drawPath(runwayPath, paint);
    //canvas.save();

    /\*画 中间的 圆形的 滑块\*/
    //canvas.scale(circleSlideScale, circleSlideScale, circleSlideHeight / 2, circleSlideHeight / 2);
    paint.setStyle(Paint.Style.FILL);
    paint.setColor(circleSlideColor);
    calcCircleSlidePath();
    canvas.drawPath(circleSlidePath, paint);

}

private void calcCircleSlidePath() {
    circleSlidePath.reset();
    if (swStatus == SwStatus.is2Open) {
        slowly4Open();
    } else if (swStatus == SwStatus.is2Close) {
        slowly4Close();
    } else {
        circleSlideRectF.left = circleSlideLeft + circleSlideOutGap + progress \* circleSlideRunLength;
        circleSlideRectF.right = circleSlideRight - circleSlideOutGap + progress \* circleSlideRunLength;
        circleSlidePath.arcTo(circleSlideRectF, 90, 180);
        circleSlideRectF.left = circleSlideLeft + circleSlideOutGap + progress \* circleSlideRunLength;
        circleSlideRectF.right = circleSlideRight - circleSlideOutGap + progress \* circleSlideRunLength;
        circleSlidePath.arcTo(circleSlideRectF, 270, 180);
    }
    circleSlidePath.close();
}

private void slowly4Open() {
    circleSlideRectF.left = circleSlideLeft + circleSlideOutGap + progress \* circleSlideRunLength;
    circleSlideRectF.right = circleSlideRight - circleSlideOutGap + progress \* circleSlideRunLength;
    circleSlidePath.arcTo(circleSlideRectF, 90, 180);
    if (progress < 0.8) {
        circleSlideRectF.right = circleSlideRight - circleSlideOutGap + (progress + 0.2F) \* circleSlideRunLength;
    } else if ((progress >= 0.8) && (progress < 0.99)) {
        circleSlideRectF.right = circleSlideRight - circleSlideOutGap + (progress + (1 - progress)) \* circleSlideRunLength;
    } else {
        circleSlideRectF.left = circleSlideLeft + circleSlideOutGap + progress \* circleSlideRunLength;
        circleSlideRectF.right = circleSlideRight - circleSlideOutGap + progress \* circleSlideRunLength;
    }
    circleSlidePath.arcTo(circleSlideRectF, -90, 180);
}

private void slowly4Close() {
    circleSlideRectF.left = circleSlideLeft + circleSlideOutGap + progress \* circleSlideRunLength;
    circleSlideRectF.right = circleSlideRight - circleSlideOutGap + progress \* circleSlideRunLength;
    circleSlidePath.arcTo(circleSlideRectF, -90, 180);

    if (progress > 0.2) {
        circleSlideRectF.left = circleSlideLeft + circleSlideOutGap + (progress - 0.2F) \* circleSlideRunLength;
    } else if ((progress <= 0.2) && (progress > 0.01)) {
        circleSlideRectF.left = circleSlideLeft + circleSlideOutGap + (progress \* 0.2F) \* circleSlideRunLength;
    } else {
        circleSlideRectF.left = circleSlideLeft + circleSlideOutGap + progress \* circleSlideRunLength;
        circleSlideRectF.right = circleSlideRight - circleSlideOutGap + progress \* circleSlideRunLength;
    }
    circleSlidePath.arcTo(circleSlideRectF, 90, 180);
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    width = w;
    height = h;
    isCanVisibleDrawing = width > getPaddingLeft() + getPaddingRight() && height > getPaddingTop() + getPaddingBottom();
    if (!isCanVisibleDrawing) {
        return;
    }
    viewLeft = getPaddingLeft();
    viewRight = getWidth() - getPaddingRight();
    viewTop = getPaddingTop();
    viewBottom = getHeight() - getPaddingBottom();
    runwayHeight = viewBottom - viewTop;

    circleSlideLeft = viewLeft;
    circleSlideTop = viewTop;
    circleSlideBottom = viewBottom;
    circleSlideHeight = viewBottom - viewTop;
    circleSlideRight = viewLeft + circleSlideHeight;

    runwayPath.reset();
    /\*画跑道需要的矩形\*/
    RectF runwayRectF = new RectF();
    runwayRectF.top = viewTop;
    runwayRectF.bottom = viewBottom;
    runwayRectF.left = viewLeft;
    runwayRectF.right = viewLeft + runwayHeight;
    /\*画 跑道的左半 半圆\*/
    runwayPath.arcTo(runwayRectF, 90, 180);
    runwayRectF.left = viewRight - runwayHeight;
    runwayRectF.right = viewRight;
    /\*画 跑道的右半 半圆\*/
    runwayPath.arcTo(runwayRectF, -90, 180);
    runwayPath.close();

    circleSlideOutGap = height \* (1 - circleSlideScale) \* 0.5F;
    circleSlideRectF.left = circleSlideLeft + circleSlideOutGap;
    circleSlideRectF.right = circleSlideRight - circleSlideOutGap;
    circleSlideRectF.top = circleSlideTop + circleSlideOutGap;
    circleSlideRectF.bottom = circleSlideBottom - circleSlideOutGap;

    circleSlideRunLength = width - circleSlideOutGap \* 2 - (circleSlideRectF.right - circleSlideRectF.left);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int resultWidth;
    if (widthMode == MeasureSpec.EXACTLY) {
        resultWidth = widthSize;
    } else {
        resultWidth = (int) (56 \* getResources().getDisplayMetrics().density + 0.5f) + getPaddingLeft() + getPaddingRight();
        if (widthMode == MeasureSpec.AT_MOST) {
            resultWidth = Math.min(resultWidth, widthSize);
        }
    }

    int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int resultHeight;
    if (heightMode == MeasureSpec.EXACTLY) {
        resultHeight = heightSize;
    } else {
        int selfExpectedResultHeight = (int) (resultWidth \* 0.68F) + getPaddingTop() + getPaddingBottom();
        resultHeight = selfExpectedResultHeight;
        if (heightMode == MeasureSpec.AT_MOST) {
            resultHeight = Math.min(resultHeight, heightSize);
        }
    }
    setMeasuredDimension(resultWidth, resultHeight);
}


/\*\*
 \* Called when a view has been clicked.
 \*
 \* @param v The view that was clicked.
 \*/
@Override
public void onClick(View v) {
    if (closeAnimator.isRunning() || openAnimator.isRunning()) {
        return;
    }
    if (isOpened) {
        swStatus = SwStatus.is2Close;
        closeAnimator.start();
        if (onChangeListener != null) {
            onChangeListener.onChange(this, false);
        }
    } else {
        swStatus = SwStatus.is2Open;
        openAnimator.start();
        if (onChangeListener != null) {
            onChangeListener.onChange(this, true);
        }
    }
    isOpened = !isOpened;
}


public SwitchView onChangeListener(OnChangeListener onChangeListener) {
    this.onChangeListener = onChangeListener;
    return this;
}

public interface OnChangeListener {
    void onChange(SwitchView switchView, boolean isOpen);
}


private final class SwStatus {
    /\*\*
     \* 初始状态
     \*/
    private final static int isNone = 0;
    /\*\*
     \* 初始状态
     \*/
    private final static int is2Open = 1;
    /\*\*
     \* 初始状态
     \*/
    private final static int is2Close = 2;
}

}

</pre>

SwitchView

目录
相关文章
|
安全 Python Windows
python - http请求带Authorization
# 背景 接入公司的一个数据统计平台,该平台的接口是带上了Authorization验证方式来保证验签计算安全   # 方法 其实很简单,就是在header中加入key=Authorization,value是协商好的协议即可; 如,我们这边是base64.
4697 0
|
8月前
|
机器学习/深度学习 数据采集 数据可视化
NumPy 正态分布与 Seaborn 可视化指南
正态分布(高斯分布)是重要的概率模型,具有钟形曲线特征,由均值μ和标准差σ描述。NumPy的`random.normal()`可生成正态分布随机数,Seaborn库方便绘制分布图。正态分布广泛应用于统计学、机器学习、金融和工程等领域。练习包括生成正态分布数据、比较不同标准差影响及模拟考试成绩计算平均分和标准分。
329 0
|
8月前
|
存储 关系型数据库 数据库
超1/3中国500强企业都在用的「汇联易」,为什么选用阿里云RDS?
迎峰而上:汇联易依托阿里云RDS通用云盘,加速业务智能化升级
超1/3中国500强企业都在用的「汇联易」,为什么选用阿里云RDS?
|
弹性计算 缓存 网络协议
测试:阿里云U1实例(通用算力型实例)服务器详细介绍
性能测试:阿里云U1实例(通用算力型实例)服务器详细介绍
313 0
|
SQL 分布式计算 安全
吐血整理的Hadoop最全开发指南【环境准备篇】(上)
吐血整理的Hadoop最全开发指南【环境准备篇】
228 0
吐血整理的Hadoop最全开发指南【环境准备篇】(上)
|
JSON 数据格式 Python
Python3,8款超级好用的实战性小技巧,每一款都只需1行代码。(一)
Python3,8款超级好用的实战性小技巧,每一款都只需1行代码。(一)
110 0
Python3,8款超级好用的实战性小技巧,每一款都只需1行代码。(一)
|
Java 编译器
重载与重写有什么区别?
重载与重写有什么区别?
比hexo更好用的轻量级博客,5分钟部署上线!
比hexo更好用的轻量级博客,5分钟部署上线!
比hexo更好用的轻量级博客,5分钟部署上线!
|
算法 Java 计算机视觉
图像直方图与直方图均衡化
图像直方图与直方图均衡化
166 0
图像直方图与直方图均衡化
|
SQL 存储 安全
都这么卷了,不懂MyBatis插件开发怎么行,教你实现一个MyBatis分页插件
MyBatis可谓是Java开发工程师必须要掌握的持久层框架,它能够让我们更容易的通过Java代码操作数据库,并且它还有很高的扩展性,我们可以自定义插件,去让MyBatis的功能变的更为强大,本篇文章我们就以打印SQL,SQL分页为例,来讲一下如何开发MyBatis的插件。
436 0