当前位置: 首页 > 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…...

MPL3115A2传感器驱动开发与嵌入式高度气压测量实战

1. MPL3115A2 压力/高度/温度传感器深度技术解析 MPL3115A2 是 NXP&#xff08;现为恩智浦半导体&#xff09;推出的一款高精度、低功耗、IC 接口的绝对压力传感器&#xff0c;集成温度测量与气压高度计算引擎。该器件并非简单的模拟信号采集芯片&#xff0c;而是一个具备完整数…...

信号完整性入门避坑:为什么你的PCB板总在‘振铃’?从阻抗不连续说起

信号完整性实战指南&#xff1a;从振铃现象到阻抗匹配的工程思维 实验室里&#xff0c;示波器屏幕上那道本该平滑的方波信号边缘&#xff0c;此刻却像被风吹皱的水面般上下起伏——这种被称为"振铃"的现象&#xff0c;是每位硬件工程师成长路上的必修课。当你的PCB设…...

OBS StreamFX插件:解锁专业级直播特效的免费神器

OBS StreamFX插件&#xff1a;解锁专业级直播特效的免费神器 【免费下载链接】obs-StreamFX StreamFX is a plugin for OBS Studio which adds many new effects, filters, sources, transitions and encoders! Be it 3D Transform, Blur, complex Masking, or even custom sha…...

STM32N6开发板跑YOLOv8人脸检测,从模型转换到烧录的‘避坑’实战记录

STM32N6开发板部署YOLOv8人脸检测的十二个致命陷阱与突围方案 当我在深夜第三次面对开发板毫无反应的LCD屏幕时&#xff0c;咖啡杯旁的示波器探头正闪烁着诡异的蓝光。这不是教科书上的标准流程演示&#xff0c;而是一场真实发生在嵌入式AI部署前线的技术突围战。STM32N6这颗搭…...

BepInEx插件框架:5分钟掌握Unity游戏模组开发与注入技术

BepInEx插件框架&#xff1a;5分钟掌握Unity游戏模组开发与注入技术 【免费下载链接】BepInEx Unity / XNA game patcher and plugin framework 项目地址: https://gitcode.com/GitHub_Trending/be/BepInEx 如果你热爱Unity游戏并希望为它们添加自定义功能&#xff0c;B…...

技术速递|GitHub 初学者指南:GitHub 安全入门

作者&#xff1a;Kedasha Kerr排版&#xff1a;Alan Wang学习如何使用 GitHub Advanced Security 保护你的项目&#xff0c;并确保它们的安全性。欢迎回到《GitHub 初学者指南》第三季&#xff01;到目前为止&#xff0c;今年我们已经介绍了 GitHub Issues 和 Projects&#xf…...

深度解析:macOS微信防撤回插件WeChatIntercept的5个核心技术揭秘

深度解析&#xff1a;macOS微信防撤回插件WeChatIntercept的5个核心技术揭秘 【免费下载链接】WeChatIntercept 微信防撤回插件&#xff0c;一键安装&#xff0c;仅MAC可用&#xff0c;支持v3.7.0微信 项目地址: https://gitcode.com/gh_mirrors/we/WeChatIntercept 作为…...

零基础极速上手:10分钟用AI建站工具生成一个企业官网

很多朋友觉得搭建官网是件很“技术”的事&#xff0c;需要懂代码、会设计。其实&#xff0c;在当下的AI时代&#xff0c;哪怕你完全零基础&#xff0c;也能在10分钟内生成一个结构、看着专业的公司官网。这篇教程&#xff0c;我们就用一套通用的方法&#xff0c;带你走一遍从零…...

高效管理博德之门3模组:BG3 Mod Manager一站式智能解决方案

高效管理博德之门3模组&#xff1a;BG3 Mod Manager一站式智能解决方案 【免费下载链接】BG3ModManager A mod manager for Baldurs Gate 3. This is the only official source! 项目地址: https://gitcode.com/gh_mirrors/bg/BG3ModManager 在《博德之门3》的模组世界中…...

用 Microsoft Agent Framework 构建 SubAgent(Multi-Agent)嵌

本文能帮你解决什么&#xff1f; 1. 搞懂FastAPI异步&#xff08;async/await&#xff09;到底在什么场景下能真正提升性能。 2. 掌握在FastAPI中正确使用多线程处理CPU密集型任务的方法。 3. 避开常见的坑&#xff08;比如阻塞操作、数据库连接池耗尽、GIL限制&#xff09;。 …...