Android原生实现控件点击弹起效果方案(API28及以上)
之前在实现控件阴影时有提到过,阴影效果的实现采用的是Android原生的View的属性,拔高Z轴。Z轴会让View产生阴影的效果。
Z=elevation+ translationZ
拔高Z轴可以通过控制elevation和translationZ。
我们之前是通过elevation来单纯的控制Z轴;而translateZ,除了控制Z轴,还可以用来控制动画效果,比如我们点击按钮时希望它有一个弹起的效果,就是借助这个属性来实现。
StateAnimatorView接口
创建一个通用接口:
public interface StateAnimatorView {void setStateAnimator(StateAnimator stateAnimator);StateAnimator getStateAnimator();
}
实现接口
ShapeButton 实现 StateAnimatorView接口:
// -------------------------------// stateAnimator// -------------------------------private StateAnimator stateAnimator = new StateAnimator(this);@Overridepublic void setStateAnimator(StateAnimator stateAnimator) {this.stateAnimator = stateAnimator;}@Overridepublic StateAnimator getStateAnimator() {return stateAnimator;}
StateAnimator
StateAnimator是一个管理View状态和相关Animator的类:
public class StateAnimator {private final ArrayList<Tuple> mTuples = new ArrayList<>();private Tuple lastMatch = null;private Animator runningAnimation = null;private WeakReference<View> viewRef;public StateAnimator(View target) {setTarget(target);}private Animator.AnimatorListener mAnimationListener = new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animation) {}@Overridepublic void onAnimationEnd(Animator animation) {if (runningAnimation == animation) {runningAnimation = null;}}@Overridepublic void onAnimationCancel(Animator animation) {}@Overridepublic void onAnimationRepeat(Animator animation) {}};/*** Associates the given Animation with the provided drawable state specs so that it will be run* when the View's drawable state matches the specs.** @param specs drawable state specs to match against* @param animation The Animation to run when the specs match*/public void addState(int[] specs, Animator animation, Animator.AnimatorListener listener) {Tuple tuple = new Tuple(specs, animation, listener);animation.addListener(mAnimationListener);mTuples.add(tuple);}/*** Returns the current {@link Animation} which is started because of a state change.** @return The currently running Animation or null if no Animation is running*/Animator getRunningAnimation() {return runningAnimation;}View getTarget() {return viewRef == null ? null : viewRef.get();}void setTarget(View view) {final View current = getTarget();if (current == view) {return;}if (current != null) {clearTarget();}if (view != null) {viewRef = new WeakReference<>(view);}}private void clearTarget() {viewRef = null;lastMatch = null;runningAnimation = null;}/*** Called by View*/public void setState(int[] state) {Tuple match = null;final int count = mTuples.size();for (int i = 0; i < count; i++) {final Tuple tuple = mTuples.get(i);if (StateSet.stateSetMatches(tuple.mSpecs, state)) {match = tuple;break;}}if (match == lastMatch) {return;}if (lastMatch != null) {cancel();}lastMatch = match;View view = (View) viewRef.get();if (match != null && view != null && view.getVisibility() == View.VISIBLE) {start(match);}}private void start(Tuple match) {match.getListener().onAnimationStart(match.animation);runningAnimation = match.animation;runningAnimation.start();}private void cancel() {if (runningAnimation != null && runningAnimation.isRunning()) {runningAnimation.cancel();runningAnimation = null;}}/*** @hide*/ArrayList<Tuple> getTuples() {return mTuples;}static class Tuple {final int[] mSpecs;final Animator animation;private Animator.AnimatorListener listener;private Tuple(int[] specs, Animator Animation, Animator.AnimatorListener listener) {mSpecs = specs;animation = Animation;this.listener = listener;}int[] getSpecs() {return mSpecs;}Animator getAnimation() {return animation;}public Animator.AnimatorListener getListener() {return listener;}}}
在初始化阴影属性的地方,初始化StateAnimation:
public static void initElevation(ShadowView view, TypedArray a, int[] ids) {int carbon_elevation = ids[0];int carbon_shadowColor = ids[1];int carbon_ambientShadowColor = ids[2];int carbon_spotShadowColor = ids[3];float elevation = a.getDimension(carbon_elevation, 0);view.setElevation(elevation);// 初始化StateAnimationif (elevation > 0)AnimUtils.setupElevationAnimator(((StateAnimatorView) view).getStateAnimator(), view);ColorStateList shadowColor = a.getColorStateList(carbon_shadowColor);view.setElevationShadowColor(shadowColor != null ? shadowColor.withAlpha(255) : null);if (a.hasValue(carbon_ambientShadowColor)) {ColorStateList ambientShadowColor = a.getColorStateList(carbon_ambientShadowColor);view.setOutlineAmbientShadowColor(ambientShadowColor != null ? ambientShadowColor.withAlpha(255) : null);}if (a.hasValue(carbon_spotShadowColor)) {ColorStateList spotShadowColor = a.getColorStateList(carbon_spotShadowColor);view.setOutlineSpotShadowColor(spotShadowColor != null ? spotShadowColor.withAlpha(255) : null);}}
按下按钮和松开按钮的状态和动画是一一对应的:
public static void setupElevationAnimator(StateAnimator stateAnimator, final ShadowView view) {// 按下时的状态和动画{final ValueAnimator animator = ValueAnimator.ofFloat(0, 0);animator.setDuration(SHORT_ANIMATION_DURATION);animator.setInterpolator(new FastOutSlowInInterpolator());Animator.AnimatorListener animatorListener = new AnimatorListenerAdapter() {@Overridepublic void onAnimationStart(Animator animation) {float elevationOrTranslationZ = getElevationOrTranslationZ(view);animator.setFloatValues(0, elevationOrTranslationZ);}};animator.addUpdateListener(animation -> view.setTranslationZ((Float) animation.getAnimatedValue()));stateAnimator.addState(new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled}, animator, animatorListener);}// 松开时的状态和动画{final ValueAnimator animator = ValueAnimator.ofFloat(0, 0);animator.setDuration(SHORT_ANIMATION_DURATION);animator.setInterpolator(new FastOutSlowInInterpolator());Animator.AnimatorListener animatorListener = new AnimatorListenerAdapter() {@Overridepublic void onAnimationStart(Animator animation) {float elevationOrTranslationZ = getElevationOrTranslationZ(view);animator.setFloatValues(elevationOrTranslationZ, 0);}};animator.addUpdateListener(animation -> view.setTranslationZ((Float) animation.getAnimatedValue()));stateAnimator.addState(new int[]{-android.R.attr.state_pressed, android.R.attr.state_enabled}, animator, animatorListener);}// 松开时的状态和动画{final ValueAnimator animator = ValueAnimator.ofFloat(0, 0);animator.setDuration(SHORT_ANIMATION_DURATION);animator.setInterpolator(new FastOutSlowInInterpolator());Animator.AnimatorListener animatorListener = new AnimatorListenerAdapter() {@Overridepublic void onAnimationStart(Animator animation) {animator.setFloatValues(view.getElevation(), 0);}};animator.addUpdateListener(animation -> view.setTranslationZ((Float) animation.getAnimatedValue()));stateAnimator.addState(new int[]{android.R.attr.state_enabled}, animator, animatorListener);}}
开始动画
在按下或松开按钮时,会回调按钮的drawableStateChanged():
@Overrideprotected void drawableStateChanged() {super.drawableStateChanged();if (rippleDrawable != null && rippleDrawable.getStyle() != RippleDrawable.Style.Background)rippleDrawable.setState(getDrawableState());if (stateAnimator != null)// 这个方法会执行与状态相对应的动画stateAnimator.setState(getDrawableState());}
完整代码可查看:
ShapeButton部分
相关文章:
Android原生实现控件点击弹起效果方案(API28及以上)
之前在实现控件阴影时有提到过,阴影效果的实现采用的是Android原生的View的属性,拔高Z轴。Z轴会让View产生阴影的效果。 Zelevation translationZ 拔高Z轴可以通过控制elevation和translationZ。 我们之前是通过elevation来单纯的控制Z轴;而…...
【数据结构-队列 二】【单调队列】滑动窗口最大值
废话不多说,喊一句号子鼓励自己:程序员永不失业,程序员走向架构!本篇Blog的主题是【单调队列】,使用【队列】这个基本的数据结构来实现,这个高频题的站点是:CodeTop,筛选条件为&…...
如何设置CentOS系统以禁用不必要的网络端口和服务?
要禁用CentOS系统中的不必要的网络端口和服务,可以按照以下步骤进行操作: 1. 查看当前正在运行的服务和端口:使用以下命令可以查看正在运行的服务和对应的端口号。 sudo netstat -tuln 2. 停用不必要的服务:根据netstat命令的输…...
【IDEA项目个别类爆红,但是项目可以正常运行】
打开项目时发现idea个别类爆红,但是项目可以正常运行 问题原因:Idea本身的问题,可能是其缓存问题,导致爆红 解决方案:重置Idea 很多时候排查不出代码问题,就尝试一下此操作。 选择目录:File–>Invalida…...
hive 之select 中文乱码
此处的中文乱码和mysql的库表 编码 latin utf 无关。 直接上案例。 有时候我们需要自定义一列,有时是汉字有时是字母,结果遇到这种情况了。 说实话看到这真是糟心。这谁受得了。 单独select 没有任何问题。 这是怎么回事呢? 经过一番检查&…...
优化|优化处理可再生希尔伯特核空间的非参数回归中的协变量偏移
原文:Optimally tackling covariate shift in RKHS-based nonparametric regression. The Annals of Statistics, 51(2), pp.738-761, 2023. 原文作者:Cong Ma, Reese Pathak, Martin J. Wainwright 论文解读者:赵进 编者按: …...
Netty深入浅出Java网络编程学习笔记(一) Netty入门篇
目录 一、概述 1、什么是Netty 2、Netty的优势 二、入门案例 1、服务器端代码 2、客户端代码 3、运行流程 组件解释 三、组件 1、EventLoop 处理普通与定时任务 关闭 EventLoopGroup 处理IO任务 服务器代码 客户端代码 分工细化 划分Boss 和Work 增加自定义EventLoopGroup 切换…...
自动化产线集控系统(西门子CNC 840D/840DSL远程控制)
1.1项目背景 RQQ/VF120机组目前为1人操作3台机床,需在机台旁监控。为了改善人员在班中劳动强度非常大的现状,调整好每台机床的节奏,以保证机床的最少的等待时间。本项目旨在通过远程监视设备运行过程关键参数,操作人员人员可远程监…...
MVVM 与 MVC区别和应用场景?
MVVM 和 MVC 1. MVC2. MVVM 1. MVC MVC 是 Model View Controller 的缩写 Model:模型层,是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。View:视图层,用户界面渲染逻辑,通常视图…...
Linux开发-Ubuntu软件源工具
开发&验证环境: 操作系统:ubuntu 20.04 软件源:http://archive.ubuntu.com/ubuntu 开发工具 sudo apt install vim sudo apt install git# gnu工具链 sudo apt install gcc sudo apt install g sudo apt install gdb# llvm工具链 sudo …...
环境下载地址
1. DOTNET环境下载 适用于 Visual Studio 的 .NET SDK 下载 (microsoft.com)https://dotnet.microsoft.com/zh-cn/download/visual-studio-sdks...
E. Block Sequence-Codeforces Round 903 (Div. 3)
E. Block Sequence dp题,设dp[i]表示i~n之间的数,需要最小删除数量 那么每一位数有两种情况,设数a[i]: 1.被删除:dp[i]dp[i1]1,这一位等于上一位的加一。 2.被保留:dp[i]min(dp[i],dp[ia[i]1]); #include<iostream…...
路由router
什么是路由? 一个路由就是一组映射关系(key - value)key 为路径,value 可能是 function 或 component 2、安装\引入\基础使用 只有vue-router3,才能应用于vue2;vue-router4可以应用于vue3中 这里我们安装vue-router3…...
学习编程-先改变心态
编程失败的天才 林一和我很久以前就认识了——我从五年级就认识他了。他是班上最聪明的孩子。如果每个人在家庭作业或考试准备方面需要帮助,他们都会去那里。 有趣的是,林一不是那种连续学习几个小时的孩子。 他的聪明才智似乎与生俱来,几乎毫…...
【Node.js】http 模块
1. http 模块 import http from http // 创建本地服务器接收数据 const server http.createServer((req, res) > {console.log(req.url)res.writeHead(200, { Content-Type: application/json // Content-Type: text/html;charsetutf-8 // 将内容以 html 标签和 utf-8 的…...
S/4 HANA 大白话 - 财务会计-2 总账主数据
接下来看看财务模块的一些具体操作。 总账相关主数据 公司每天运转,每天办公室有租金,有水电费,有桌椅板凳损坏,鼠标损坏要换,有产品买卖,有收入。那么所有这些都得记下来。记哪里?记在总账里…...
Redis根据中心点坐标和半径筛选符合的数据
目录 1.启动Redis编辑 2.导入maven依赖 3.添加redis配置 4.编写RedisService 5.使用 6.验证 1.启动Redis 2.导入maven依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifac…...
springboot 集成 zookeeper 问题记录
springboot 集成 zookeeper 问题记录 环境 springboot - 2.7.8 dubbo - 3.1.11 dubbo-dependencies-zookeeper-curator5 - 3.1.11 模拟真实环境,将 windows 上的 zookeeper 迁移到虚拟机 linux 的 docker 环境 failed to connect to zookeeper server 迁移到…...
java中的接口interface
一、面向对象基本概念 Java是一种面向对象的语言,其中「对象」就相当于是现实世界中的一个个具体的例子,而「类」就相当于是一个抽象的模板,将抽象的概念模板转化为具体的例子的过程就叫做「实例化」。 比如说人这个概念就是一个抽象化的「…...
多个git提交,只推送其中一个到远程该如何处理
用新分支去拉取当前分支的指定commit记录,之后推送到当前分支远程仓库实现推送指定历史提交的功能 1.查看当前分支最近五次提交日志 git log --oneline -5 2.拉取远程分支创建临时本地分支 localbranch 为本地分支名 origin/dev 为远程目标分支 git checkout …...
OFA图像描述模型在STM32嵌入式系统的边缘计算应用
OFA图像描述模型在STM32嵌入式系统的边缘计算应用 让嵌入式设备也能"看懂"世界并说出来 1. 项目背景与需求 你有没有想过,让一个小小的嵌入式设备不仅能"看到"图像,还能用语言描述出它看到了什么?这听起来像是科幻电影里…...
打破平台壁垒:AI驱动的全渠道内容生产新范式
打破平台壁垒:AI驱动的全渠道内容生产新范式 【免费下载链接】Awesome-Dify-Workflow 分享一些好用的 Dify DSL 工作流程,自用、学习两相宜。 Sharing some Dify workflows. 项目地址: https://gitcode.com/GitHub_Trending/aw/Awesome-Dify-Workflow …...
GLM-4.7-Flash功能体验:MoE架构+流式输出,感受30B大模型的丝滑对话
GLM-4.7-Flash功能体验:MoE架构流式输出,感受30B大模型的丝滑对话 1. 开篇:初识GLM-4.7-Flash 当我第一次在CSDN星图镜像广场看到GLM-4.7-Flash这个30B参数的大模型时,内心既期待又忐忑。期待的是它能带来怎样的智能体验&#x…...
零基础搭建知识库:5分钟部署通义千问3-Embedding-4B向量模型
零基础搭建知识库:5分钟部署通义千问3-Embedding-4B向量模型 1. 引言:为什么选择Qwen3-Embedding-4B? 想象一下,你手头有大量文档、报告或网页内容,想要快速建立一个能理解语义的智能知识库。传统的关键词搜索已经无…...
Path of Building:流放之路构筑规划的精准导航工具
Path of Building:流放之路构筑规划的精准导航工具 【免费下载链接】PathOfBuilding Offline build planner for Path of Exile. 项目地址: https://gitcode.com/gh_mirrors/pat/PathOfBuilding 在《流放之路》复杂多变的角色构建世界中,Path of …...
Comsol模拟混凝土中水分传递 低气压下水分转移引起的水泥浆龄期微观结构变化 低气压(AP)...
Comsol模拟混凝土中水分传递 低气压下水分转移引起的水泥浆龄期微观结构变化 低气压(AP)会影响混凝土中的水分传递,进而影响其微观结构和体积特性,但对其热力学机制却知之甚少 可文献复现 水泥基材料内部的水分运动会直接改变孔隙…...
手把手教你用超级千问语音设计世界制作游戏剧情配音
手把手教你用超级千问语音设计世界制作游戏剧情配音 1. 为什么游戏开发者需要语音设计工具 在游戏开发过程中,配音往往是最容易被忽视却又至关重要的环节。传统配音方式面临三大痛点: 成本高昂:专业配音演员费用动辄上千元每分钟效率低下&…...
DAMO-YOLO手机检测一文详解:tinynas主干网络轻量化设计优势
DAMO-YOLO手机检测一文详解:tinynas主干网络轻量化设计优势 1. 引言:为什么我们需要一个又快又准的手机检测器? 想象一下,你正在开发一个智能会议室管理系统,需要实时统计参会人数和他们的行为。其中一个关键功能是检…...
OpenClaw配置优化:提升GLM-4.7-Flash响应速度的3个技巧
OpenClaw配置优化:提升GLM-4.7-Flash响应速度的3个技巧 1. 为什么需要优化GLM-4.7-Flash的响应速度 上个月我在本地部署了OpenClaw对接GLM-4.7-Flash模型,最初的使用体验并不理想。一个简单的文件整理任务需要等待近20秒才能开始执行,而复杂…...
OpenClaw团队协作版:ollama-QwQ-32B支持多人任务队列的改造
OpenClaw团队协作版:ollama-QwQ-32B支持多人任务队列的改造 1. 为什么我们需要团队协作版的OpenClaw 上周我们小组遇到了一个典型问题:三个人同时使用同一台机器上的OpenClaw实例时,任务开始互相干扰。最严重的一次,A同事的自动…...
