当前位置: 首页 > news >正文

Android 自定义按钮(可滑动、点击)

按钮图片素材

https://download.csdn.net/download/Lan_Se_Tian_Ma/88151085
 

px 和 dp 转换工具类(Java)

// px 和 dp 转换工具类
public class DensityUtil {/*** 根据手机的分辨率从 dip 的单位 转成为 px(像素)*/public static int dip2px(Context context, float dpValue) {final float scale = context.getResources().getDisplayMetrics().density;return (int) (dpValue * scale + 0.5f);}/*** 根据手机的分辨率从 px(像素) 的单位 转成为 dp*/public static int px2dip(Context context, float pxValue) {final float scale = context.getResources().getDisplayMetrics().density;return (int) (pxValue / scale + 0.5f);}
}

自定义按钮(Kotlin)

import android.animation.Animator
import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Paint
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.View
import java.util.*class MyButton : View, View.OnClickListener {constructor(context: Context) : super(context)constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet)init {// 加载图片资源btnBackground = BitmapFactory.decodeResource(resources, R.drawable.switch_background)button = BitmapFactory.decodeResource(resources, R.drawable.slide_button)paint = Paint()// 抗锯齿paint.isAntiAlias = true// 点击事件setOnClickListener(this)antiShake = AntiShake(500)}private var btnBackground: Bitmapprivate var button: Bitmapprivate var buttonWidth: Int = 0private var paint: Paintprivate val antiShake : AntiShake// X轴最大移动距离private var maxMoveV: Int = 0// 时时的移动距离private var translateX: Float = 0f// 方向:向左false 向右trueprivate var direction = false// 手指按下时的X轴位置var startX: Float = 0f// 用来记录上一个move坐标,用来判断,左滑还是右滑private var tempV: Float = 0f// 按钮的leftprivate var btnLeft: Float = 0f// 按钮的rightprivate var btnRight: Float = button.width.toFloat()// 当前手指按下的位置private var currentLocation: Float = 0f// 按下的区域,是否处于按钮区域private var isBtn = false// offOrNo:开true 关falsevar offOrNo = false// 是否可以触发点击事件var isOpenClick = false// 平移动画是否结束var animatorComplete = trueoverride fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {val widthSize = MeasureSpec.getSize(widthMeasureSpec)val widthModel = MeasureSpec.getMode(widthMeasureSpec)val heightSize = MeasureSpec.getSize(heightMeasureSpec)val heightModel = MeasureSpec.getMode(heightMeasureSpec)if (widthModel == MeasureSpec.AT_MOST && heightModel == MeasureSpec.AT_MOST) {setMeasuredDimension(btnBackground.width, btnBackground.height)} else if (widthModel == MeasureSpec.AT_MOST) {setMeasuredDimension(btnBackground.width, heightSize)} else if (heightModel == MeasureSpec.AT_MOST) {setMeasuredDimension(widthSize, btnBackground.height)} else {setMeasuredDimension(widthSize, heightSize)}}@SuppressLint("DrawAllocation")override fun onDraw(canvas: Canvas?) {// 改变图片尺寸btnBackground = Bitmap.createScaledBitmap(btnBackground, width, height, true)// 按钮width没有覆盖背景一半区域,手动增加按钮widthbuttonWidth = (width / 2) + DensityUtil.dip2px(context, 12f)button = Bitmap.createScaledBitmap(button, buttonWidth, height, true)canvas?.drawBitmap(btnBackground, 0f, 0f, paint)canvas?.drawBitmap(button, translateX, 0f, paint)// 获取按钮,最大X轴移动值maxMoveV = width - buttonWidth}@SuppressLint("ClickableViewAccessibility")override fun onTouchEvent(event: MotionEvent?): Boolean {// 手指离开时的X轴位置var endX: Float = 0fsuper.onTouchEvent(event)when (event?.action) {MotionEvent.ACTION_DOWN -> {startX = event.xtempV = event.xisBtnContent(event)if (isBtn && animatorComplete) {currentLocation = startX - btnLeft}}MotionEvent.ACTION_UP -> {if (antiShake.isFastClick()) {endX = event.xtempV = event.xisOpenClick = startX == endXif (isBtn && !isOpenClick && animatorComplete) {confirmCoordinate()}}}MotionEvent.ACTION_MOVE -> {if (isBtn && animatorComplete) {// getDirection(event)translateCalculate(event)invalidate()}}}return true}override fun onClick(v: View?) {if (isOpenClick && animatorComplete) {if (offOrNo) {translateAnimator((btnLeft + maxMoveV) - translateX, 0f)offOrNo = false} else {translateAnimator(btnLeft, (btnLeft + maxMoveV))offOrNo = true}}}// 判断是否在按钮区域private fun isBtnContent(event: MotionEvent) {getBtnLeftRight()isBtn = event.x > btnLeft && event.x < btnRight}// 确认停留的坐标(手指离开屏幕后)private fun confirmCoordinate() {// 更新按钮的最新位置getBtnLeftRight()// 获取百分比val round = ((translateX / maxMoveV) * 100).toInt()var animatorStartX = 0fvar animatorEndX = 0f// 超过50%// 这里一定要写成 >= 50,只写 > 50,因为有可能刚好是50if (round > 50) {// 停留在右边animatorStartX = btnLeftanimatorEndX = maxMoveV.toFloat()offOrNo = true} else {// 停留在左边animatorStartX = btnLeftanimatorEndX = 0foffOrNo = false}translateAnimator(animatorStartX, animatorEndX)getBtnLeftRight(true)}// 平移动画private fun translateAnimator(animatorStartX: Float,animatorEndX: Float,duration: Long = 300) {val animator = ValueAnimator.ofFloat(animatorStartX, animatorEndX)animator.duration = durationanimator.addUpdateListener {translateX = it.animatedValue as Float// Log.e("TAG", "$translateX")invalidate()}animator.addListener(object : Animator.AnimatorListener {// 动画开始override fun onAnimationStart(animation: Animator?) {// Log.e("TAG","onAnimationStart")animatorComplete = false}// 动画结束override fun onAnimationEnd(animation: Animator?) {animatorComplete = true// Log.e("TAG","onAnimationEnd")}// 动画取消override fun onAnimationCancel(animation: Animator?) {animatorComplete = true// Log.e("TAG","onAnimationCancel")}// 动画重复override fun onAnimationRepeat(animation: Animator?) {animatorComplete = false}})animator.start()}// 获取滑动方向private fun getDirection(event: MotionEvent) {if (event.x > tempV) {if (translateX == maxMoveV.toFloat()) {// Log.e("TAG", "已右滑至最大值")return}// 向右滑动// Log.e("TAG", "向右滑动:$translateX")if (!direction) {direction = true}tempV = event.x} else if (event.x < tempV) {if (translateX == 0f) {// Log.e("TAG", "已左滑至最大值")return}// 向左滑动// Log.e("TAG", "向左滑动:$translateX")if (direction) {direction = false}tempV = event.x}}// 计算平移距离private fun translateCalculate(event: MotionEvent) {// 平移的距离translateX = event.x - currentLocation// 屏蔽非法值if (translateX >= maxMoveV) {translateX = maxMoveV.toFloat()} else if (translateX <= 0) {translateX = 0f}getBtnLeftRight()}// 获取按钮的位置// complete:动画是否结束// offOrNo:开true 关falseprivate fun getBtnLeftRight(complete: Boolean = false) {if (complete) {if (offOrNo) {translateX = maxMoveV.toFloat()} else {translateX = 0f}}btnRight = translateX + buttonWidthbtnLeft = btnRight - buttonWidth}// 防止快速点击class AntiShake(minTime: Int = 1000) {// 两次点击间隔不能少于1000msprivate var MIN_DELAY_TIME : Intprivate var lastClickTime: Long = 0init {MIN_DELAY_TIME = minTime}fun isFastClick(): Boolean {var flag = trueval currentClickTime = System.currentTimeMillis()if ((currentClickTime - lastClickTime) < MIN_DELAY_TIME) {flag = false}lastClickTime = currentClickTimereturn flag}}}

布局中使用(XML)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"tools:context=".MainActivity"><com.test.festec.customizebutton.MyButtonandroid:layout_width="150dp"android:layout_height="55dp"/></LinearLayout>

相关文章:

Android 自定义按钮(可滑动、点击)

按钮图片素材 https://download.csdn.net/download/Lan_Se_Tian_Ma/88151085 px 和 dp 转换工具类&#xff08;Java&#xff09; // px 和 dp 转换工具类 public class DensityUtil {/*** 根据手机的分辨率从 dip 的单位 转成为 px(像素)*/public static int dip2px(Conte…...

mac录屏怎么打开?很简单,让我来教你!

mac电脑作为一款广受欢迎的电脑系统&#xff0c;提供了多种方式来满足用户录屏的需求。无论您是要录制教学视频、制作演示文稿&#xff0c;还是记录游戏精彩瞬间&#xff0c;mac电脑都能帮助您实现这些目标。本文将为您介绍两种mac录屏的方法。通过本文的指导&#xff0c;您将能…...

Stable Diffusion AI绘画学习指南【插件安装设置】

插件安装的方式 可用列表方式安装&#xff0c;点开Extensions 选项卡&#xff0c;找到如下图&#xff0c;找到Available选项卡&#xff0c;点load from加载可用插件&#xff0c;在可用插件列表中找到要装的插件按install 按扭按装&#xff0c;安装完后(Apply and restart UI)应…...

APP开发中的性能优化:提升用户满意度的关键

APP开发中的性能优化是需要持续进行的&#xff0c;它不仅能够让用户体验到 APP的使用感受&#xff0c;还能在一定程度上提升用户的满意度&#xff0c;从而提升 APP的粘性和转化率。不过在实际开发中&#xff0c;很多 APP开发公司会存在性能优化上的问题&#xff0c;这就需要了解…...

Golang 切片 常用方法

文章目录 移除指定位置的元素查找元素的位置查找最大最小的元素去重随机打乱排序二维排序sort.Sort 排序 下面的方法省略一些校验&#xff0c;如数组越界等&#xff0c;且都采用泛型(要求go版本 > 1.18) 移除指定位置的元素 package mainimport ("fmt" )func Del…...

【Linux后端服务器开发】poll/epoll多路转接IO服务器

目录 一、poll原理 二、poll实现多路转接IO服务器 三、epoll函数接口 四、epoll的工作原理 五、epoll实现多路转接IO服务器 一、poll原理 poll函数接口 #include <poll.h> int poll(struct pollfd *fds, nfds_t nfds, int timeout);// pollfd结构 struct pollfd …...

【设计模式——学习笔记】23种设计模式——命令模式Command(原理讲解+应用场景介绍+案例介绍+Java代码实现)

文章目录 案例引入介绍基础介绍登场角色 案例实现案例一实现 案例二介绍实现拓展 命令模式在JdbcTemplate源码中的应用总结文章说明 案例引入 有一套智能家电&#xff0c;其中有照明灯、风扇、冰箱、洗衣机&#xff0c;这些智能家电来自不同的厂家&#xff0c;我们不想针对每一…...

Rust中的高吞吐量流处理

本篇文章主要介绍了Rust中流处理的概念、方法和优化。作者不仅介绍了流处理的基本概念以及Rust中常用的流处理库&#xff0c;还使用这些库实现了一个流处理程序。 最后&#xff0c;作者介绍了如何通过测量空闲和阻塞时间来优化流处理程序的性能&#xff0c;并将这些内容同步至…...

探索编程世界的宝藏:程序员必掌握的20大算法

文章目录 1 引言2 冒泡排序算法&#xff1a;编程世界的排序魔法 &#x1f9d9;‍♀️&#x1f522;3 选择排序算法&#xff1a;排序世界的精确挑选器 &#x1f3af;&#x1f522;4 插入排序算法&#xff1a;排序世界的巧妙插珠者 ✨&#x1f522;5 快速排序算法&#xff1a;排序…...

Android NFC通信示例

前言 近距离无线通信 (NFC) 是一组近距离无线技术&#xff0c;通常只有在距离不超过 4 厘米时才能启动连接。借助 NFC&#xff0c;您可以在 NFC 标签与 Android 设备之间或者两台 Android 设备之间共享小型负载。 支持 NFC 的 Android 设备同时支持以下三种主要操作模式&…...

2023年08月IDE流行度最新排名

点击查看最新IDE流行度最新排名&#xff08;每月更新&#xff09; 2023年08月IDE流行度最新排名 顶级IDE排名是通过分析在谷歌上搜索IDE下载页面的频率而创建的 一个IDE被搜索的次数越多&#xff0c;这个IDE就被认为越受欢迎。原始数据来自谷歌Trends 如果您相信集体智慧&am…...

使用Beego和MySQL实现帖子和评论的应用,并进行接口测试(附源码和代码深度剖析)

文章目录 小项目介绍源码分析main.gorouter.gomodels/user.gomodels/Post.gomodels/comment.gocontrollers/post.gocontrollers/comment.go 接口测试测试增加帖子测试查看帖子测试增加评论测试查看评论 小项目介绍 经过对需求的分析&#xff0c;我增加了一些额外的东西&#x…...

物联网潜在的巨大价值在于大数据分析

物联网潜在的巨大价值在于大数据分析 从数据里去挖掘市场或者用户的精准需求。 往小的说&#xff0c;后台可以统计用户家里各各插座一年甚至更久的用电情况&#xff0c;这些数据也可以通过app或者小程序展现给用户。 用户可以很直观看到自己一年的用电情况&#xff0c;哪个家…...

SSL原理详解

SSL协议结构&#xff1a; SSL协议分为两层&#xff0c;下层为SSL记录协议&#xff0c;上层为SSL握手协议、SSL密码变化协议和SSL警告协议。 1.下层为SSL记录协议&#xff0c;主要作用是为高层协议提供基本的安全服务 建立在可靠的传输之上&#xff0c;负责对上层的数据进行分块…...

linux下的etc目录代表什么意思

在Linux系统中&#xff0c;/etc目录是一个非常重要的目录&#xff0c;它包含了系统的配置文件和相关的配置信息。下面是一些/etc目录中常见的文件和目录&#xff1a; 1. /etc/passwd&#xff1a;此文件包含了所有用户账户的信息&#xff0c;包括用户名、用户ID、用户所属的组I…...

iOS 两种方式设置状态栏

1、ios9.0以前设置状态栏字体颜色 ///白色 [[UIApplication sharedApplication]setStatusBarStyle:UIStatusBarStyleLightContent]; ///黑色 [[UIApplication sharedApplication]setStatusBarStyle:UIStatusBarStyleDefault]; 会看到如下提示&#xff1a; setStatusBarSty…...

html5:webSocket 基础使用

一、理解 HTML5 WebSocket HTML5 WebSocket是一种新型的网络协议&#xff0c;它能够在客户端和服务器之间建立实时的双向通信通道&#xff0c;使得浏览器和服务器之间的数据传输更加高效、快速和可靠。相比传统的HTTP协议&#xff0c;WebSocket协议使用更少的网络开销&#xf…...

html学习10-----总结(完)

<!DOCTYPE html> <html><head><meta charset"utf-8"/><title>html总结</title></head><body><h1>HTML总结</h1><br/><h2>文本格式化</h2><hr/><p><b>粗体文本<…...

Spring使用P命名空间实现注入数值信息-----Spring框架

<?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xmlns:p"http://www.springframework.org/schema/p"x…...

windows环境下安装RabbitMQ

一、RabbitMq简介1.1消息队列中间件简介消息队列中间件是分布式系统中重要的组件&#xff0c;主要解决应用耦合&#xff0c;异步消息&#xff0c;流量削锋等问题实现高性能&#xff0c;高可用&#xff0c;可伸缩和最终一致性[架构] 使用较多的消息队列有 ActiveMQ(安全)&#x…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现

摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序&#xff0c;以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务&#xff0c;提供稳定高效的数据处理与业务逻辑支持&#xff1b;利用 uniapp 实现跨平台前…...

css3笔记 (1) 自用

outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size&#xff1a;0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格&#xff…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?

在大数据处理领域&#xff0c;Hive 作为 Hadoop 生态中重要的数据仓库工具&#xff0c;其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式&#xff0c;很多开发者常常陷入选择困境。本文将从底…...

Java求职者面试指南:计算机基础与源码原理深度解析

Java求职者面试指南&#xff1a;计算机基础与源码原理深度解析 第一轮提问&#xff1a;基础概念问题 1. 请解释什么是进程和线程的区别&#xff1f; 面试官&#xff1a;进程是程序的一次执行过程&#xff0c;是系统进行资源分配和调度的基本单位&#xff1b;而线程是进程中的…...

vulnyx Blogger writeup

信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面&#xff0c;gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress&#xff0c;说明目标所使用的cms是wordpress&#xff0c;访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...

基于Springboot+Vue的办公管理系统

角色&#xff1a; 管理员、员工 技术&#xff1a; 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能&#xff1a; 该办公管理系统是一个综合性的企业内部管理平台&#xff0c;旨在提升企业运营效率和员工管理水…...

逻辑回归暴力训练预测金融欺诈

简述 「使用逻辑回归暴力预测金融欺诈&#xff0c;并不断增加特征维度持续测试」的做法&#xff0c;体现了一种逐步建模与迭代验证的实验思路&#xff0c;在金融欺诈检测中非常有价值&#xff0c;本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...