菜单中的类似iOS中开关的样式

背景是我们有需求,做类似ios中开关的按钮。github上有一些开源项目,比如 SwitchButton, 但是这个项目中提供了很多选项,并且实际使用中会出现一些奇怪的问题。

我调整了下代码,把无关的功能都给删了,保留核心的功能,大概这样。

在这里插入图片描述
在这里插入图片描述

package org.yeshen.widget;// 修改自:https://github.com/zcweng/SwitchButton
// 菜单中的类似iOS中开关的样式import static org.yeshen.widget.YsSwitchButton.ANIMATE.*;import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Checkable;public final class YsSwitchButton extends View implements Checkable {private static final int DEFAULT_WIDTH = dp2pxInt(44);private static final int DEFAULT_HEIGHT = dp2pxInt(25);private static final int DEFAULT_BUTTON_PADDING = dp2pxInt(8);private final int uncheckColor = 0xFFFF0000;private final int checkedColor = 0xFF0000FF;private final int uncheckButtonColor = Color.WHITE;private float viewRadius;private float left;private float top;private float right;private float bottom;private float centerY;private float buttonMinX;private float buttonMaxX;private final Paint buttonPaint;private final Paint paint;private final ViewState viewState;private final ViewState beforeState;private final ViewState afterState;private final RectF rect = new RectF();private ANIMATE animateState = ANIMATE_STATE_NONE;private final ValueAnimator valueAnimator;private final android.animation.ArgbEvaluator argbEvaluator = new android.animation.ArgbEvaluator();private boolean isChecked = false;private boolean isTouchingDown = false;private boolean isUiInit = false;private boolean isEventBroadcast = false;private OnCheckedChangeListener onCheckedChangeListener;private long touchDownTime;private boolean switchByUser;public YsSwitchButton(Context context) {this(context, null);}public YsSwitchButton(Context context, AttributeSet attrs) {this(context, attrs, 0);}public YsSwitchButton(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);paint = new Paint(Paint.ANTI_ALIAS_FLAG);buttonPaint = new Paint(Paint.ANTI_ALIAS_FLAG);buttonPaint.setColor(uncheckButtonColor);viewState = new ViewState();beforeState = new ViewState();afterState = new ViewState();valueAnimator = ValueAnimator.ofFloat(0f, 1f);valueAnimator.setDuration(300);valueAnimator.setRepeatCount(0);ValueAnimator.AnimatorUpdateListener animatorUpdateListener = animation -> {float value = (Float) animation.getAnimatedValue();switch (animateState) {case ANIMATE_STATE_PENDING_SETTLE: {}case ANIMATE_STATE_PENDING_RESET: {}case ANIMATE_STATE_PENDING_DRAG: {if (animateState != ANIMATE_STATE_PENDING_DRAG) {viewState.buttonX = beforeState.buttonX + (afterState.buttonX - beforeState.buttonX) * value;}viewState.checkStateColor = (int) argbEvaluator.evaluate(value, beforeState.checkStateColor, afterState.checkStateColor);break;}case ANIMATE_STATE_SWITCH: {viewState.buttonX = beforeState.buttonX + (afterState.buttonX - beforeState.buttonX) * value;float fraction = (viewState.buttonX - buttonMinX) / (buttonMaxX - buttonMinX);viewState.checkStateColor = (int) argbEvaluator.evaluate(fraction, uncheckColor, checkedColor);break;}default:case ANIMATE_STATE_DRAGING: {}case ANIMATE_STATE_NONE: {break;}}postInvalidate();};valueAnimator.addUpdateListener(animatorUpdateListener);Animator.AnimatorListener animatorListener = new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animation) {}@Overridepublic void onAnimationEnd(Animator animation) {switch (animateState) {case ANIMATE_STATE_PENDING_DRAG: {animateState = ANIMATE_STATE_DRAGING;postInvalidate();break;}case ANIMATE_STATE_PENDING_RESET: {animateState = ANIMATE_STATE_NONE;postInvalidate();break;}case ANIMATE_STATE_PENDING_SETTLE: {animateState = ANIMATE_STATE_NONE;postInvalidate();broadcastEvent(true);break;}case ANIMATE_STATE_SWITCH: {isChecked = !isChecked;animateState = ANIMATE_STATE_NONE;postInvalidate();broadcastEvent(switchByUser);break;}case ANIMATE_STATE_DRAGING:case ANIMATE_STATE_NONE:default: {break;}}}@Overridepublic void onAnimationCancel(Animator animation) {}@Overridepublic void onAnimationRepeat(Animator animation) {}};valueAnimator.addListener(animatorListener);super.setClickable(true);this.setPadding(0, 0, 0, 0);setLayerType(LAYER_TYPE_SOFTWARE, null);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {final int widthMode = MeasureSpec.getMode(widthMeasureSpec);final int heightMode = MeasureSpec.getMode(heightMeasureSpec);if (widthMode == MeasureSpec.UNSPECIFIED || widthMode == MeasureSpec.AT_MOST) {widthMeasureSpec = MeasureSpec.makeMeasureSpec(DEFAULT_WIDTH, MeasureSpec.EXACTLY);}if (heightMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.AT_MOST) {heightMeasureSpec = MeasureSpec.makeMeasureSpec(DEFAULT_HEIGHT, MeasureSpec.EXACTLY);}super.onMeasure(widthMeasureSpec, heightMeasureSpec);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);float viewPadding = 0;float height = h - viewPadding - viewPadding;viewRadius = height * .5f;left = viewPadding;top = viewPadding;right = w - viewPadding;bottom = h - viewPadding;centerY = (top + bottom) * .5f;buttonMinX = left + viewRadius;buttonMaxX = right - viewRadius;if (isChecked()) {setCheckedViewState(viewState);} else {setUncheckViewState(viewState);}isUiInit = true;postInvalidate();}private void setUncheckViewState(ViewState viewState) {viewState.checkStateColor = uncheckColor;viewState.buttonX = buttonMinX;buttonPaint.setColor(uncheckButtonColor);}private void setCheckedViewState(ViewState viewState) {viewState.checkStateColor = checkedColor;viewState.buttonX = buttonMaxX;int checkedButtonColor = Color.WHITE;buttonPaint.setColor(checkedButtonColor);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);// background colorpaint.setColor(uncheckColor);drawRoundRect(canvas, left, top, right, bottom, viewRadius, paint);// select colorpaint.setColor(viewState.checkStateColor);drawRoundRect(canvas, left, top, right, bottom, viewRadius, paint);// buttoncanvas.drawCircle(viewState.buttonX, centerY,viewRadius - DEFAULT_BUTTON_PADDING / 2F, buttonPaint);}@SuppressLint("ObsoleteSdkInt")private void drawRoundRect(Canvas canvas, float left, float top, float right,float bottom, float backgroundRadius, Paint paint) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {canvas.drawRoundRect(left, top, right, bottom, backgroundRadius, backgroundRadius, paint);} else {rect.set(left, top, right, bottom);canvas.drawRoundRect(rect, backgroundRadius, backgroundRadius, paint);}}@Overridepublic void setChecked(boolean checked) {if (checked == isChecked()) {postInvalidate();return;}toggle(true, false);}@Overridepublic boolean isChecked() {return isChecked;}@Overridepublic void toggle() {toggle(true);}public void toggle(boolean animate) {toggle(animate, true);}private void toggle(boolean animate, boolean broadcast) {toggle(animate, broadcast, false);}private void toggle(boolean animate, boolean broadcast, boolean byUser) {if (!isEnabled()) {return;}if (isEventBroadcast) {throw new RuntimeException("should NOT switch the state in method: [onCheckedChanged]!");}if (!isUiInit) {isChecked = !isChecked;if (broadcast) {broadcastEvent(byUser);}return;}if (valueAnimator.isRunning()) {valueAnimator.cancel();}if (!animate) {isChecked = !isChecked;if (isChecked()) {setCheckedViewState(viewState);} else {setUncheckViewState(viewState);}postInvalidate();if (broadcast) {broadcastEvent(byUser);}return;}animateState = ANIMATE_STATE_SWITCH;switchByUser = byUser;beforeState.copy(viewState);if (isChecked()) {setUncheckViewState(afterState);} else {setCheckedViewState(afterState);}valueAnimator.start();}private void broadcastEvent(boolean byUser) {if (onCheckedChangeListener != null) {isEventBroadcast = true;onCheckedChangeListener.onCheckedChanged(this, isChecked(), byUser);}isEventBroadcast = false;}@SuppressLint("ClickableViewAccessibility")@Overridepublic boolean onTouchEvent(MotionEvent event) {if (!isEnabled()) {return false;}int actionMasked = event.getActionMasked();switch (actionMasked) {case MotionEvent.ACTION_DOWN: {isTouchingDown = true;touchDownTime = System.currentTimeMillis();removeCallbacks(postPendingDrag);postDelayed(postPendingDrag, 100);break;}case MotionEvent.ACTION_MOVE: {float eventX = event.getX();if (isPendingDragState()) {float fraction = eventX / getWidth();fraction = Math.max(0f, Math.min(1f, fraction));viewState.buttonX = buttonMinX + (buttonMaxX - buttonMinX) * fraction;} else if (isDragState()) {float fraction = eventX / getWidth();fraction = Math.max(0f, Math.min(1f, fraction));viewState.buttonX = buttonMinX + (buttonMaxX - buttonMinX) * fraction;viewState.checkStateColor = (int) argbEvaluator.evaluate(fraction, uncheckColor, checkedColor);postInvalidate();}break;}case MotionEvent.ACTION_UP: {isTouchingDown = false;removeCallbacks(postPendingDrag);if (System.currentTimeMillis() - touchDownTime <= 300) {toggle(true, true, true);} else if (isDragState()) {float eventX = event.getX();float fraction = eventX / getWidth();fraction = Math.max(0f, Math.min(1f, fraction));boolean newCheck = fraction > .5f;if (newCheck == isChecked()) {pendingCancelDragState();} else {isChecked = newCheck;pendingSettleState();}} else if (isPendingDragState()) {pendingCancelDragState();}break;}case MotionEvent.ACTION_CANCEL: {isTouchingDown = false;removeCallbacks(postPendingDrag);if (isPendingDragState() || isDragState()) {pendingCancelDragState();}break;}}return true;}private final Runnable postPendingDrag = () -> {if (!isInAnimating()) {pendingDragState();}};private boolean isInAnimating() {return animateState != ANIMATE_STATE_NONE;}private boolean isPendingDragState() {return animateState == ANIMATE_STATE_PENDING_DRAG || animateState == ANIMATE_STATE_PENDING_RESET;}private boolean isDragState() {return animateState == ANIMATE_STATE_DRAGING;}private void pendingDragState() {if (isInAnimating()) {return;}if (!isTouchingDown) {return;}if (valueAnimator.isRunning()) {valueAnimator.cancel();}animateState = ANIMATE_STATE_PENDING_DRAG;beforeState.copy(viewState);afterState.copy(viewState);if (isChecked()) {afterState.checkStateColor = checkedColor;afterState.buttonX = buttonMaxX;} else {afterState.checkStateColor = uncheckColor;afterState.buttonX = buttonMinX;}valueAnimator.start();}private void pendingCancelDragState() {if (isDragState() || isPendingDragState()) {if (valueAnimator.isRunning()) {valueAnimator.cancel();}animateState = ANIMATE_STATE_PENDING_RESET;beforeState.copy(viewState);if (isChecked()) {setCheckedViewState(afterState);} else {setUncheckViewState(afterState);}valueAnimator.start();}}private void pendingSettleState() {if (valueAnimator.isRunning()) {valueAnimator.cancel();}animateState = ANIMATE_STATE_PENDING_SETTLE;beforeState.copy(viewState);if (isChecked()) {setCheckedViewState(afterState);} else {setUncheckViewState(afterState);}valueAnimator.start();}@SuppressWarnings("unused")public void setOnCheckedChangeListener(OnCheckedChangeListener l) {onCheckedChangeListener = l;}public interface OnCheckedChangeListener {void onCheckedChanged(YsSwitchButton view, boolean isChecked, boolean byUser);}private static float dp2px(float dp) {Resources r = Resources.getSystem();return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics());}private static int dp2pxInt(float dp) {return (int) dp2px(dp);}private static class ViewState {float buttonX;int checkStateColor;private void copy(ViewState source) {this.buttonX = source.buttonX;this.checkStateColor = source.checkStateColor;}}enum ANIMATE {ANIMATE_STATE_NONE, ANIMATE_STATE_PENDING_DRAG, ANIMATE_STATE_DRAGING, ANIMATE_STATE_PENDING_RESET, ANIMATE_STATE_PENDING_SETTLE, ANIMATE_STATE_SWITCH;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/97742.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue技术学习

vue快速入门 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>vue快速入门</title> </head> <body> <!--老师解读 1. div元素不是必须的&#xff0c;也可以是其它元素&#xff0…

阿里云ECS服务器和轻量应用服务器区别?怎么选择?

阿里云轻量应用服务器和云服务器ECS有什么区别&#xff1f;ECS是专业级云服务器&#xff0c;轻量应用服务器是轻量级服务器&#xff0c;轻量服务器使用门槛更低&#xff0c;适合个人开发者或中小企业新手使用&#xff0c;可视化运维&#xff0c;云服务器ECS适合集群类、高可用、…

密码学学习笔记(二十):DSA签名与X.509证书

数字签名 下图是一个制作以及使用数字签名过程的通用模型。 假设Bob发送一条消息给Alice&#xff0c;尽管消息并不重要&#xff0c;也不需要保密&#xff0c;但他想让Alice知道消息确实是他本人发的。出于这个目的&#xff0c;Bob利用一个安全的散列函数&#xff0c;比如SHA-…

设计模式-过滤器模式(使用案例)

过滤器模式&#xff08;Filter Pattern&#xff09;或标准模式&#xff08;Criteria Pattern&#xff09;是一种设计模式&#xff0c;这种模式允许开发人员使用不同的标准来过滤一组对象&#xff0c;通过逻辑运算以解耦的方式把它们连接起来。这种类型的设计模式属于结构型模式…

Azure使用CLI创建VM

使用CLI创建VM之前&#xff0c;确保资源中的IP资源已经释放掉了&#xff0c;避免创建的过程中没有可以利用的公共IP地址打开 cloudshell ,并输入创建CLI的命令如下&#xff0c;-n指定名称&#xff0c;-g指定资源组&#xff0c;image指定镜像&#xff0c;admin-usernam指定用户名…

如何快速制作解决方案PPT

如何快速制作解决方案PPT 理解客户的需求 在开始制作解决方案PPT之前&#xff0c;需要对客户的需求进行深入了解和分析。这包括客户需要解决的问题、目标、预算和时间限制等。 需求分析 客户需要解决的问题客户的目标预算限制时间限制 确定解决方案 基于客户的需求&#x…

发布一个开源的新闻api(整理后就开源)

目录 说明: 基础说明 其他说明: 通用接口&#xff1a; 登录: 注册: 更改密码(需要token) 更换头像(需要token) 获取用户列表(需要token): 上传文件(5000端口): 获取文件(5000端口)源码文件&#xff0c;db文件均不能获取: 验证token(需要token): 获取系统时间: 文件…

VMware 虚拟机三种网络模式详解

文章目录 前言桥接模式(Bridged)桥接模式特点: 仅主机模式 (Host-only)仅主机模式 (Host-only)特点: NAT网络地址转换模式(NAT)仅主机模式 (Host-only)特点: 前言 很多同学在初次接触虚拟机的时候对 VMware 产品的三种网络模式不是很理解,本文就 VMware 的三种网络模式进行说明…

GBU816-ASEMI新能源专用整流桥GBU816

编辑&#xff1a;ll GBU816-ASEMI新能源专用整流桥GBU816 型号&#xff1a;GBU816 品牌&#xff1a;ASEMI 封装&#xff1a;GBU-4 恢复时间&#xff1a;&#xff1e;50ns 正向电流&#xff1a;8A 反向耐压&#xff1a;1600V 芯片个数&#xff1a;4 引脚数量&#xff1…

判断平面中两射线是否相交的高效方法

1. 简介 最近在工作中遇到判断平面内两射线是否相交的问题。 对于这个问题的解决,常规的方法是将两条射线拓展为直线,计算直线的交点,而后判断交点是否在射线上。 这种方法,在思路上较为直观,也易于理解。然后,该方法在计算量上相对较大。对于少量射线间的交点计算尚可…

一、docker及mysql基本语法

文章目录 一、docker相关命令二、mysql相关命令 一、docker相关命令 &#xff08;1&#xff09;拉取镜像&#xff1a;docker pull <镜像ID/image> &#xff08;2&#xff09;查看当前docker中的镜像&#xff1a;docker images &#xff08;3&#xff09;删除镜像&#x…

如何将图片应用于所有的PPT页面?

问题&#xff1a;如何快速将图片应用到所有PPT页面&#xff1f; 解答&#xff1a;有两种方法可以解决这个问题。第一种用母板。第二种用PPT背景功能。 解决有时候汇报的时候&#xff0c;ppt中背景图片修改不了以及不知道如何查找&#xff0c;今天按照逆向过程进行操作 方法1…

C# 观察者模式

一、概述 观察者模式是一种常用的设计模式&#xff0c;它属于行为型模式。在C#中&#xff0c;观察者模式通过定义一种一对多的依赖关系&#xff0c;使得当一个对象的状态发生变化时&#xff0c;所有依赖于它的对象都会得到通知并自动更新。这种模式可以实现松耦合&#xff0c;…

k8s 自身原理之 Service

好不容易&#xff0c;终于来到 k8s 自身的原理之 关于 Service 的一部分了 前面我们用 2 个简图展示了 pod 之间和 pod 与 node 之间是如何通信息的&#xff0c;且通信的数据包是不会经过 NAT 网络地址转换的 那么 Service 又是如何实现呢&#xff1f; Service 我们知道是用…

常见的 Python 错误及其解决方案

此文整理了一些常见的 Python 错误及其解决方案。 1、SyntaxError: invalid syntax 说明&#xff1a;无效的语法是最常见的错误之一&#xff0c;通常是由于编写代码时违反了 Python 的语法规则。可能的原因&#xff1a; 忘记在 if、while、for 等语句后写冒号&#xff0c;或者…

VS2015打开Qt的pro项目文件 报错

QT报错&#xff1a;Project ERROR: msvc-version.conf loaded but QMAKE_MSC_VER isn‘t set 解决方法&#xff1a; 找到本机安装的QT路径&#xff0c;找到“msvc-version.conf”文件&#xff0c;用记事本打开&#xff0c; 在其中添加版本“QMAKE_MSC_VER 1900”保存即可。 …

基于VUE3+Layui从头搭建通用后台管理系统(前端篇)八:自定义组件封装上

一、本章内容 本章实现一些自定义组件的封装,包括数据字典组件的封装、下拉列表组件封装、复选框单选框组件封装、单选框组件封装、文件上传组件封装、级联选择组件封装、富文本组件封装等。 1. 详细课程地址: 待发布 2. 源码下载地址: 待发布 二、界面预览 ![在这里插入图…

无人机跟随一维高度避障场景--逻辑分析

无人机跟随一维高度避障场景--逻辑分析 1. 源由2. 视频3. 问题3.1 思维发散3.2 问题收敛 4. 图示4.1 水平模式4.2 下坡模式4.3 上坡模式4.4 碰撞分析 5. 总结5.1 一维高度避障场景5.2 业界跟随产品5.3 APM集成跟随示意图一&#xff1a;示意图二&#xff1a;示意图三&#xff1a…

Azure防火墙

文章目录 什么是Azure防火墙如何部署和配置创建虚拟网络创建虚拟机创建防火墙创建路由表&#xff0c;关联子网、路由配置防火墙策略配置应用程序规则配置网络规则配置 DNAT 规则 更改 Srv-Work 网络接口的主要和辅助 DNS 地址测试防火墙 什么是Azure防火墙 Azure防火墙是一种用…

XenDesktop5.6如何连接数据库

Citrix在数据库的连接方式上一直不统一&#xff0c;但是也还是有迹可循的。 经过了好长时间的下载以后&#xff0c;今天终于有时间来测试一下最新版本的XenDesktop 5 SP1&#xff0c;由于结合了其他组件和环境的需要&#xff0c;所以&#xff0c;选择了独立部署数据库&#xf…