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%if (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>

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

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

相关文章

【C++】基于多设计模式下的同步异步日志系统

✍作者&#xff1a;阿润021 &#x1f4d6;专栏&#xff1a;C 文章目录 一、项目介绍二、项目实现准备工作1.日志系统技术实现策略2.相关技术知识补充2.1 不定参函数设计2.2 设计模式 三、日志项目框架设计1.模块划分2.各模块关系图 四、详细代码实现1.实用工具类设计2.日志等级…

java -jar指定外部配置文件

场景 spingboot项目部署jar时,需要时常修改配置,为了方便,将配置文件放到jar包外 操作步骤 在jar包同级目录下创建config文件夹(位置没有强制要求,为了方便而已) 在jar包同级目录下创建start.bat文件,并编辑内容 echo off :: 命令窗口标题 title yudibei_performance_tes…

第5章 最佳实践

过去的错误 不要怪罪JavaScript 游览器遇到不合法的html会想尽办法将他展现出来游览器遇到不合法的js将拒绝执行它们并报错写js要保障自己代码的健壮性 质疑一切 写js功能前一定要考虑这个功能的合理性&#xff0c;避免造成不可预见的后果写js功能前一定要考虑用户的游览器…

【Golang】基于录制,自动生成go test接口自动化用例

目录 背景 框架 ginkgo初始化 抓包&运行脚本 目录说明 ∮./business ∮./conf ∮./utils ∮./testcase testcase 用例目录结构规则 示例 实现思路 解析Har数据 定义结构体 解析到json 转换请求数据 转换请求 转换请求参数 写业务请求数据 写gotest测试…

【element-plus】 table表格每行圆角解决方案 element也通用

系列文章目录 【Vue3ViteTselement-plus】使用tsx实现左侧栏菜单无限层级封装 前言 我们在使用element-plus或element 的table时是否有时UI给到的UI效果是如下面这样的&#xff0c;但是我们翻遍了组件库的文档 调整了很多次样式 发现在 左右侧栏固定的时候 普通的方法是完全…

装修小程序,开启装修公司智能化服务的新时代

随着数字化时代的来临&#xff0c;装修小程序成为提升服务质量和效率的关键工具。装修小程序旨在为装修公司提供数字化赋能、提高客户满意度的智慧装修平台。通过装修小程序&#xff0c;装修公司能够与客户进行在线沟通、展示设计方案、提高服务满意度等操作。 装修小程序的好处…

手机pdf转换成word免费版?看看这几个转换方法

手机pdf转换成word免费版&#xff1f;在当今信息化的时代&#xff0c;PDF文档已经成为公文交流、资料分享、学术论文等领域中最常用的文件格式之一。然而&#xff0c;PDF文档的固化特性也使其在文本编辑、内容修改等方面存在不便。因此&#xff0c;将PDF文档转换为Word文档已成…

简单的知识图谱可视化+绘制nx.Graph()时报错TypeError: ‘_AxesStack‘ object is not callable

绘制nx.Graph时报错TypeError: _AxesStack object is not callable 写在最前面知识图谱可视化预期报错可能的原因 原代码原因确认解决后的代码解决&#xff01; 写在最前面 实现一个简单的知识图谱的可视化功能。 使用了NetworkX库来构建知识图谱&#xff0c;并使用matplotlib…

基于Java的房屋租赁系统设计与实现(源码+lun文+数据库)

对于需要租房的人&#xff0c;有的可能没有时间去街上广告处查看房屋信息&#xff1b;对于房屋出租者来说&#xff0c;大量复杂的房产信息、租金等信息很难通过传统的方式进行管理。以方便租客租房&#xff0c;房东求租、求售的需求为向导&#xff0c;开发一套基于web的房屋管理…

PHP代码审计--理论

提供资料&#xff1a; php 基础 : https://www.runoob.com/php/php-tutorial.html php是什么&#xff1f; PHP 是服务器端脚本语言。 首先在学习PHP前需要对HTML 和CSS有一定的认识 PHP 能做什么&#xff1f; PHP 可以生成动态页面内容PHP 可以创建、打开、读取、写入、关…

Unity中UGUI的 OnPopulateMesh函数与VertexHelper类

Graphics类 当一个UGUI的UI元素生成顶点数据时会调用Graphics类中的 OnPopulateMesh(VertexHelper vh) 函数&#xff0c;我们可以在这个函数中修改顶点的数据或者获取顶点的数据。 UGUI中与显示相关的控件&#xff0c;例如Image、Text、RawImage等都继承自MaskableGraphic类&a…

数据结构--单链表

前言 上一章&#xff0c;我们讲了数据结构--动态顺序表&#xff0c;我们会发现有以下问题&#xff1a; 1.当我们要头部或者插入或删除时&#xff0c;都需要进行位置挪动&#xff0c;腾出某一个位置&#xff0c;时间复杂度为0(N)&#xff1b; 2.增容需要申请新空间&#xff0c;…

SpringBoot2.2.0.RELEASE整合Elasticsearch6.8.3

SpringBoot2.2.0.RELEASE整合Elasticsearch6.8.3 SpringBoot是2.2.0.RELEASE&#xff0c;elasticsearch是6.8.3 使用依赖spring-boot-starter-data-elasticsearch 使用ElasticSearchRepository操作 1、导入依赖 <?xml version"1.0" encoding"UTF-8&quo…

VMware Linux Centos 配置网络并设置为静态ip

在root用户下进行以下操作 1. 查看子网ip和网关 &#xff08;1&#xff09;进入虚拟网络编辑器 &#xff08;2&#xff09;进入NAT设置 &#xff08;3&#xff09;记录子网IP和子网掩码 2. 修改网络配置文件 &#xff08;1&#xff09;cd到网络配置文件路径下 [rootlo…

工欲善其事必先利其器,IT工作电脑更要维护好

目录 一&#xff1a;电脑的组成 二&#xff1a;维护措施 三&#xff1a;助力记忆 一&#xff1a;电脑的组成 当谈到电脑主机时&#xff0c;我们通常指的是电脑的中央处理器(CPU)、内存、主板、电源、硬盘、显卡、声卡、网卡等核心部件组成的整体。这些部件共同协作&#xff…

Kafka系列之:记录一次Kafka Topic分区扩容,但是下游flink消费者没有自动消费新的分区的解决方法

Kafka系列之:记录一次Kafka Topic分区扩容,但是下游flink消费者没有自动消费新的分区的解决方法 一、背景二、解决方法三、实现自动发现新的分区一、背景 生产环境Kafka集群压力大,Topic读写压力大,消费的lag比较大,因此通过扩容Topic的分区,增大Topic的读写性能理论上下…

力扣 62. 不同路径

题目来源&#xff1a;https://leetcode.cn/problems/unique-paths/ C题解1&#xff1a;动态规划。声明二维数组。 确定dp数组&#xff08;dp table&#xff09;以及下标的含义。dp[i][j] &#xff1a;表示从&#xff08;0 &#xff0c;0&#xff09;出发&#xff0c;到(i, j) …

Kafka-Broker工作流程

kafka集群在启动时&#xff0c;会将每个broker节点注册到zookeeper中&#xff0c;每个broker节点都有一个controller&#xff0c;哪个controller先在zookeeper中注册&#xff0c;哪个controller就负责监听brokers节点变化&#xff0c;当有分区的leader挂掉时&#xff0c;contro…

一行命令删除tag为<none>的镜像

sudo docker images | grep none | awk {print $3;} | xargs sudo docker rmi

DBeaver开源数据库管理工具发布23.1.3版本

导读DBeaver开源数据库管理软件近日发布了v23.1.3版本,该版本在空间数据查看器、数据传输、数据编辑器等多个模块进行了优化,提升了软件的可用性和兼容性。 具体来看,空间数据查看器新增了地图对象标记和曲线渲染支持,也实现了坐标复制等功能。数据传输模块增强了XLSX文件导入和…