当前位置: 首页 > 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-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

业务系统对接大模型的基础方案:架构设计与关键步骤

业务系统对接大模型&#xff1a;架构设计与关键步骤 在当今数字化转型的浪潮中&#xff0c;大语言模型&#xff08;LLM&#xff09;已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中&#xff0c;不仅可以优化用户体验&#xff0c;还能为业务决策提供…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能

下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能&#xff0c;包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

基于Uniapp开发HarmonyOS 5.0旅游应用技术实践

一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架&#xff0c;支持"一次开发&#xff0c;多端部署"&#xff0c;可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务&#xff0c;为旅游应用带来&#xf…...

【论文阅读28】-CNN-BiLSTM-Attention-(2024)

本文把滑坡位移序列拆开、筛优质因子&#xff0c;再用 CNN-BiLSTM-Attention 来动态预测每个子序列&#xff0c;最后重构出总位移&#xff0c;预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵&#xff08;S…...

重启Eureka集群中的节点,对已经注册的服务有什么影响

先看答案&#xff0c;如果正确地操作&#xff0c;重启Eureka集群中的节点&#xff0c;对已经注册的服务影响非常小&#xff0c;甚至可以做到无感知。 但如果操作不当&#xff0c;可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

Mysql中select查询语句的执行过程

目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析&#xff08;Parser&#xff09; 2.4、执行sql 1. 预处理&#xff08;Preprocessor&#xff09; 2. 查询优化器&#xff08;Optimizer&#xff09; 3. 执行器…...