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

Android可滑动的分时图以及常用动画

先看一下效果:

自定义View

其中顶部是模仿的股票数据分时图,以前也写过详细的文章传送门,只不过不支持左右滑动,这款是在那个基础上的修改

在说一下分时图的思路吧:

  1. 可以看作是一条条相连的直线首尾相接,通过不断给View数据,达到动态添加的效果
  2. 左右滑动也没有想象中那么复杂,我们有所有的分时图数据集合,当屏幕显示不开时,我只选取最新的一段数据给View显示出来,当右滑时,我截取对应的的数据段让View显示,就可以达到滑动效果

其中滑动的核心代码如下:
mShowList 为需要显示的集合
mTimeList 为全部数据集合
通过 subList 截取需要的数据段,截取后实时去刷新当前View

    /*** 滑动监听* *///当前的X轴坐标,我要记录上次滑动的最后坐标,才能实时判断是否左右滑动private float startTouchX = 0f; //滑动的距离,通过他来截取对应的数据段private int moveTouchX = 0;@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN://手指按下Log.e(TAG, "onTouchEvent: 123456789->按下");//moveTouchX = 0;startTouchX = event.getX();break;case MotionEvent.ACTION_MOVE:if (mTimeList.size() > 50){stopRe = true;//手指移动if (startTouchX > event.getX()){Log.e(TAG, "onTouchEvent: 123456789->向左滑动--" + moveTouchX);if (moveTouchX > 0){moveTouchX = moveTouchX - 1;mShowList.clear();mShowList.addAll(mTimeList.subList(mTimeList.size() - (50 + moveTouchX), mTimeList.size() - moveTouchX));postInvalidate();}else {moveTouchX = 0;stopRe = false;}}else {Log.e(TAG, "onTouchEvent: 123456789->向右滑动---" + moveTouchX);if (mTimeList.size() > (50 + moveTouchX)){moveTouchX = moveTouchX + 1;mShowList.clear();mShowList.addAll(mTimeList.subList(mTimeList.size() - (50 + moveTouchX), mTimeList.size() - moveTouchX));}postInvalidate();}}startTouchX = event.getX();break;case MotionEvent.ACTION_UP://手指松开Log.e(TAG, "onTouchEvent: 123456789->松开");break;}return true;}

下方三个动画(属性动画)也是我遇到过比较常见的

第一个是放大缩小,比如在送礼按钮上,和礼物图标上,更有冲击感
第二个是上下移动,比如某个按钮上的气泡
第三个是卡片点击左右两边时有一个缓冲效果
当然常见的还有渐变效果,比如占位图消失时,加一个渐变消失用户体验可能会更好

下面贴一下代码

可滑动分时图: TimeView

/*** 作者:zch* 时间:2023/10/10 15:08* 描述:自定义分时图View*/
public class TimeView extends View {private Paint paint;private int mCanvasHeight;private int mCanvasWidth;private int mStartX = 0;private int mStartY;private int paintColor = 0;private int canvasColor = 0;private int bgColor = 0;private boolean isShowBg = false;//是否停止刷新,但是总数据一样往里面添加private boolean stopRe = false;//总数据private ArrayList<Integer> mTimeList = new ArrayList<>();//需要显示在屏幕上的数据private ArrayList<Integer> mShowList = new ArrayList<>();public TimeView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}//开始绘画逻辑@SuppressLint("DrawAllocation")@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//首先定义画笔paint = new Paint();//防锯齿paint.setAntiAlias(true);//获取画板高度mCanvasHeight = getMeasuredHeight();//获取画板宽度mCanvasWidth = getMeasuredWidth();//设置画笔颜色paint.setColor(paintColor == 0 ? Color.YELLOW : paintColor);//设置画板背景canvas.drawColor(canvasColor == 0 ? Color.GRAY : canvasColor);//设置画笔粗细paint.setStrokeWidth((float)3);//Y轴的起止都在折线图的四分之一处开始,因为数据的范围是折线图的二分之一//这样数据波动位置就处于折线图居中位置mStartY = mCanvasHeight / 4;//画线drawView(canvas);}/***注释:*绘画逻辑*/private void drawView(Canvas canvas){mStartX = 0;//如果有数据if (mShowList.size() > 0){int startX = 0,startY = 0;int stopX,stopY;for (int i = 0; i < mShowList.size(); i++){Integer t = mShowList.get(i);stopY = t+ mStartY;mStartX = mStartX + 10;//stopX = t.getStopX();stopX = mStartX;//首次的时候起始位置就等于终止位置if (i == 0){startX = stopX;startY = stopY;}//画线canvas.drawLine(startX,startY,stopX,stopY,paint);//是否画背景if (isShowBg){setDrawBg(startX,startY,stopX,stopY,canvas);}//下次画线的时候的起始位置等于上次画线位置的终止位置startX = stopX;startY = stopY;}}}/*** 滑动监听* *///当前的X轴坐标,我要记录上次滑动的最后坐标,才能实时判断是否左右滑动private float startTouchX = 0f;//滑动的距离,通过他来截取对应的数据段private int moveTouchX = 0;@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN://手指按下Log.e(TAG, "onTouchEvent: 123456789->按下");//moveTouchX = 0;startTouchX = event.getX();break;case MotionEvent.ACTION_MOVE:if (mTimeList.size() > 50){stopRe = true;//手指移动if (startTouchX > event.getX()){Log.e(TAG, "onTouchEvent: 123456789->向左滑动--" + moveTouchX);if (moveTouchX > 0){moveTouchX = moveTouchX - 1;mShowList.clear();mShowList.addAll(mTimeList.subList(mTimeList.size() - (50 + moveTouchX), mTimeList.size() - moveTouchX));postInvalidate();}else {moveTouchX = 0;stopRe = false;}}else {Log.e(TAG, "onTouchEvent: 123456789->向右滑动---" + moveTouchX);if (mTimeList.size() > (50 + moveTouchX)){moveTouchX = moveTouchX + 1;mShowList.clear();mShowList.addAll(mTimeList.subList(mTimeList.size() - (50 + moveTouchX), mTimeList.size() - moveTouchX));}postInvalidate();}}startTouchX = event.getX();break;case MotionEvent.ACTION_UP://手指松开Log.e(TAG, "onTouchEvent: 123456789->松开");break;}return true;}/***注释:* 画折线下背景*/private void setDrawBg(int tx,int ty,int px,int py,Canvas canvas){//设置画笔Paint paint = new Paint();//防锯齿paint.setAntiAlias(true);//设置颜色paint.setColor(bgColor == 0 ? Color.WHITE : bgColor);//画阴影部分Path bg = new Path();bg.moveTo(tx, ty);bg.lineTo(tx, mCanvasHeight);bg.lineTo(px, mCanvasHeight);bg.lineTo(px, py);bg.lineTo(tx, ty);bg.close();//添加到画板上canvas.drawPath(bg, paint);}/***注释:* 设置分数图数据,由外部传输*/public void setViewData(ArrayList<Integer> m) {this.mTimeList = m;//刷新界面 - 无需在UI线程,在工作线程即可被调用,invalidate()必须在UI线程if (!stopRe){mShowList.clear();//只取最后50个数据if (mTimeList.size() > 50){mShowList.addAll(mTimeList.subList(mTimeList.size()-50, mTimeList.size() - 1));}else {mShowList.addAll(mTimeList);}postInvalidate();}}/***注释:*设置分时图背景和画笔颜色* p - 画笔颜色* c - 背景颜色* b - 折线下背景颜色*/public void setViewColor(int p,int c,int b){this.paintColor = p;this.canvasColor = c;this.bgColor = b;}/***注释:* 是否显示折线下的背景*/public void setShowBgBottom(boolean b){this.isShowBg = b;}}

使用:
这里用定时来动态获取数据,其中 initData 为请求数据,这里用的模拟数据

    /*** 通过线程来动态设置View达到动画效果*/private val thread: Thread = object : Thread() {@RequiresApi(api = Build.VERSION_CODES.N)override fun run() {while (true) {try {initData()//如果数据终止X的值大于界面宽度停止线程或者其他操作/*if (mTimeList.size > 0 && mTimeList.get(mTimeList.size - 1).stopX > tvTime!!.measuredWidth) {//清除容器数据,重新添加startX = 0mTimeList.clear()//thread.stop(); //终止线程}*//**休息时间 */sleep(1000)} catch (e: InterruptedException) {e.printStackTrace()}}}}
    private val mTimeList: ArrayList<Int> = ArrayList()@RequiresApi(Build.VERSION_CODES.N)private fun initData() {//设置Y轴数据的终止位置,无需设置起始位置,因为每条线的起始位置就是上一条线的终点位置val ra = Random()val stopY: Int = ra.nextInt(40)//给分时图设置数据mTimeList.add(stopY)tvTime?.setViewData(mTimeList)}

缩放动画:
这里分两部分,先缩,监听缩结束后去放,如此反复

    //缩,将View传进来即可fun showS1(view: View) {val scaleAnimation = ScaleAnimation(1f, 0.5f,1f, 0.5f,Animation.RELATIVE_TO_SELF, 0.5f,Animation.RELATIVE_TO_SELF, 0.5f)scaleAnimation.setAnimationListener(object : Animation.AnimationListener {override fun onAnimationStart(animation: Animation) {}override fun onAnimationEnd(animation: Animation) {showF1(view)}override fun onAnimationRepeat(animation: Animation) {}})scaleAnimation.duration = 300scaleAnimation.fillAfter = trueview.startAnimation(scaleAnimation)}//放fun showF1(view: View) {val scaleAnimation = ScaleAnimation(0.5f, 1f,0.5f, 1f,Animation.RELATIVE_TO_SELF, 0.5f,Animation.RELATIVE_TO_SELF, 0.5f)scaleAnimation.duration = 300scaleAnimation.setAnimationListener(object : Animation.AnimationListener {override fun onAnimationStart(animation: Animation) {}override fun onAnimationEnd(animation: Animation) {view.clearAnimation()showS1(view)}override fun onAnimationRepeat(animation: Animation) {}})scaleAnimation.fillAfter = trueview.startAnimation(scaleAnimation)}

上下动画

    fun sx(){val view = findViewById<TextView>(R.id.tv_vip1)val upAnim = ObjectAnimator.ofFloat(view, "translationY", 0F, -50F, 0F)upAnim.duration = 2000upAnim.interpolator = LinearInterpolator()upAnim.addListener(object :AnimatorListenerAdapter(){override fun onAnimationEnd(animation: Animator?) {super.onAnimationEnd(animation)//动画结束}})upAnim.repeatCount = ValueAnimator.INFINITE;//无限循环//启动upAnim.start()}

卡片倾斜动画

    /*** 倾斜动画 f- 倾斜角度,这里建议 30-50*/fun showQX(view: View, f: Float?) {val valueAnimator = ValueAnimator.ofFloat(0f, f!!)valueAnimator.addUpdateListener { animation ->val deltaY = animation.animatedValue as Floatview.rotationY = deltaY}valueAnimator.addListener(object : Animator.AnimatorListener {override fun onAnimationStart(animator: Animator) {}override fun onAnimationEnd(animator: Animator) {showHuiFu(view, f)}override fun onAnimationCancel(animator: Animator) {}override fun onAnimationRepeat(animator: Animator) {}})//默认duration是300毫秒valueAnimator.duration = 300valueAnimator.start()}/*** 恢复倾斜动画 */fun showHuiFu(view: View, f: Float?) {val valueAnimator = ValueAnimator.ofFloat(f!!, 0f)valueAnimator.addUpdateListener { animation ->val deltaY = animation.animatedValue as Floatview.rotationY = deltaY}//默认duration是300毫秒valueAnimator.duration = 300valueAnimator.start()}

相关文章:

Android可滑动的分时图以及常用动画

先看一下效果&#xff1a; 自定义View 其中顶部是模仿的股票数据分时图&#xff0c;以前也写过详细的文章传送门&#xff0c;只不过不支持左右滑动&#xff0c;这款是在那个基础上的修改 在说一下分时图的思路吧&#xff1a; 可以看作是一条条相连的直线首尾相接&#xff0c…...

软考系统架构师常考知识点整理(含案例分析、论文历年题目总结)

系统架构师常考知识点总结 计算机组成原理 1、同步/异步区分 CPU访问内存通常是同步方式 CPU与I/O接口交换信息通常是同步方式 CPU与PCI总线交换信息通常是同步方式 I/O接口与打印机交换信息则通常采用基于缓存池的异步方式, 2、双工通信方式 对端到端通信总线的信号传…...

Netty通信在中间件组件中的广泛使用-Dubbo3举例

Netty是一个高性能异步IO通信框架&#xff0c;封装了NIO&#xff0c;对各种bug做了很好的优化解决。所以很多中间件底层的通信都会使用Netty&#xff0c;比如说&#xff1a;Dubbo3&#xff0c;rocketmq&#xff0c;ElasticSearch等。 比方说&#xff0c;我们使用dubbo作为rpc跨…...

基于Java的在线拍卖系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技…...

Maven Pom

目录 Pom 父&#xff08;Super&#xff09;POM POM 标签大全详解 POM( Project Object Model&#xff0c;项目对象模型 ) 是 Maven 工程的基本工作单元&#xff0c;是一个XML文件&#xff0c;包含了项目的基本信息&#xff0c;用于描述项目如何构建&#xff0c;声明项目依赖…...

【运维日常】mongodb 集群生产实践

本站以分享各种运维经验和运维所需要的技能为主 《python零基础入门》&#xff1a;python零基础入门学习 《python运维脚本》&#xff1a; python运维脚本实践 《shell》&#xff1a;shell学习 《terraform》持续更新中&#xff1a;terraform_Aws学习零基础入门到最佳实战 《k8…...

【MATLAB源码-第45期】基于matlab的16APSK调制解调仿真,使用卷积编码软判决。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 1. 16APSK调制解调 16APSK (16-ary Amplitude Phase Shift Keying) 是一种相位调制技术&#xff0c;其基本思想是在恒定幅度的条件下&#xff0c;改变信号的相位&#xff0c;从而传送信息。 - 调制&#xff1a;在16APSK中&am…...

HarmonyOS学习路之方舟开发框架—学习ArkTS语言(状态管理 八)

其他状态管理概述 除了前面章节提到的组件状态管理和应用状态管理&#xff0c;ArkTS还提供了Watch和$$来为开发者提供更多功能&#xff1a; Watch用于监听状态变量的变化。$$运算符&#xff1a;给内置组件提供TS变量的引用&#xff0c;使得TS变量和内置组件的内部状态保持同步…...

SQL按照id集合顺序返回

SQL按照id集合顺序返回 一、需求二、SQL三、MyBatis编写四、FIELD函数五、环境 一、需求 sql这样的 SELECT id, name FROM is_parent_viewshop WHERE id IN (2350, 2396, 3768, 3718, 3692) 按照id顺序返回&#xff0c;sql如何写 二、SQL SELECT id, name FROM is_parent_vi…...

04训练——基于YOLO V8的自定义数据集训练——在windows环境下使用pycharm做训练-1总体步骤

在上文中,笔者介绍了使用google公司提供的免费GPU资源colab来对大量的自定义数据集进行模型训练。该方法虽然简单好用,但是存在以下几方面的短板问题: 一是需要通过虚拟服务器做为跳板机来访问,总体操作起来非常繁杂。 二是需要将大量的数据上传缓慢,管理和使用非常不友…...

iview表格 异步修改列数据卡顿 滚动条失效

使用表格row-key属性 将row-key属性设置为true <Table ref"table" border :row-key"true" :columns"tableColumns" :loading"loading":data"tableData"></Table>...

【Linux】常驻内核和虚拟内存的区别

常驻内存和虚拟内存是计算机内存管理的两个概念。 常驻内存指的是应用程序在运行时&#xff0c;所占用的内存空间不会随着应用程序的启动和关闭而有所变化&#xff0c;这种内存是常驻在计算机的物理内存中的。即使我们关闭了电脑&#xff0c;这些程序也会保持在内存中&#xf…...

Qt 串口接收包含汉字的数据,汉字乱码

通过串口显示打印的调试信息时&#xff0c;接受的中文无法识别&#xff0c;全部乱码&#xff0c;解决办法&#xff1a; QByteArray array m_serial->readAll(); QString array QString::fromLocal8Bit(arr)...

设计模式 - 结构型模式考点篇:适配器模式(类适配器、对象适配器、接口适配器)

目录 一、适配器模式 一句话概括结构式模式 1.1、适配器模式概述 1.2、案例 1.2.1、类适配器模式实现案例 1.2.2、对象适配器 1.2.3、接口适配器 1.3、优缺点&#xff08;对象适配器模式&#xff09; 1.4、应用场景 一、适配器模式 一句话概括结构式模式 教你将类和对…...

android Google官网 :支持不同的语言和文化 rtl / ltr : 本地化适配:RTL(right-to-left) 适配

参考 google官网&#xff1a; 支持不同的语言和文化 应用包含可能专门针对特定文化而设计的资源。例如&#xff0c;应用可以包含针对特定文化的字符串&#xff0c;这些字符串将转换为当前语言区域的语言。 将具有文化特异性的资源与应用的其他资源分开是一种很好的做法。And…...

Visual Studio Code配置C/C++开发环境

C/C开发中的IDE非常多&#xff0c;网上有推荐安装Visual Studio 2019/2020/2022。但是登录官方网址下载&#xff0c;此软件体积非常大(8G以上)&#xff0c;且企业版、专业版会收费。 因此&#xff0c;我们推荐大家可以尝试通过Visual Studio Code来配置C/C开发环境 环境准备 Mi…...

室内渲染的艺术:创造理想空间的视觉魔法!

在繁忙的生活中&#xff0c;我们常常渴望拥有一个属于自己的安静空间。这个空间可以是一间温馨的卧室&#xff0c;也可以是一间舒适的客厅&#xff0c;甚至可以是一个小小的书房。而这个空间的营造&#xff0c;离不开室内渲染。 室内渲染是一种艺术&#xff0c;它用色彩、光线…...

php发送get、post请求的6种方法简明总结?

这篇文章主要介绍php发送get、post请求的6种方法简明总结,分别为使用file_get_contents 、fopen、fsockopen、curl来发送GET和POST请求,需要的朋友可以参考下 方法1: 用file_get_contents 以get方式获取内容&#xff1a; <?php $urlhttp://www.51growup.com/; $html f…...

Go基础之变量和常量

Go基础之变量和常量 文章目录 Go基础之变量和常量一. 标识符、关键字、内置类型和函数1.1 标识符1.2 关键字1.3 保留字1.4 内置类型1.4.1 值类型&#xff1a;1.4.2 引用类型&#xff1a;(指针类型)1.5 内置函数1.6 内置接口error 二.Go变量命名规范2.1 采用驼峰体命名2.2 简单、…...

红队专题-Cobalt strike4.5二次开发

红队专题 招募六边形战士队员IDEA 自动换行原版CS反编译破解jar包反编译拔掉暗桩初始环境效果 stageless beacon http通信协议 过程分析上线&心跳get请求teamserver 处理请求 参考链接 招募六边形战士队员 一起学习 代码审计、安全开发、web攻防、逆向等。。。 私信联系 …...

Java数据结构之Deque(双端队列)

一、Queue和Deque异同介绍 我们知道&#xff0c;Queue是队列&#xff0c;只能一头进&#xff0c;另一头出。 如果把条件放松一下&#xff0c;允许两头都进&#xff0c;两头都出&#xff0c;这种队列叫双端队列&#xff08;Double Ended Queue&#xff09;&#xff0c;学名Deq…...

flink以增量+全量的方式更新广播状态

背景 flink在实现本地内存和db同步配置表信息时&#xff0c;想要做到类似于增量(保证实时性) 全量(保证和DB数据一致)的效果&#xff0c;那么我们如何通过flink的广播状态外部定时器定时全量同步的方式来实现呢&#xff1f; 实现增量全量的效果 package wikiedits.schedule…...

Java:org.apache.commons.io包的工具类:IOUtils、FileUtils、FilenameUtils

文档 https://commons.apache.org/proper/commons-io/https://central.sonatype.com/artifact/commons-io/commons-io/2.14.0/jar 工具类 IOUtils io工具类FileUtils 文件操作工具类FilenameUtils 文件名工具类 依赖 <dependency><groupId>commons-io</gro…...

【JavaEE】文件操作

文章目录 前言什么是文件树型结构组织和目录文件路径文件类型文件权限Java中的文件操作File 类的常见属性File 类常见构造方法File 类常用方法 前言 文件是我们日常生活中使用非常广泛的&#xff0c;我们使用任何一个程序都离不开文件操作&#xff0c;这个文件不仅仅指平时可以…...

高精度电流源的应用领域有哪些

高精度电流源是一种能够提供稳定、准确、可控的电流输出的仪器设备&#xff0c;广泛应用于多个领域。以下是一些高精度电流源的应用领域。 科学研究&#xff1a;在物理学、化学、材料科学等领域中&#xff0c;需要进行精确的电流实验和测试。高精度电流源可以提供稳定的电流输出…...

多线程 - 线程池

线程池 相关的背景知识 线程池存在的意义: 使用进程来实现并发编程,效率太低了,任务太重了,为了提高效率,此时就引入了线程,线程也叫做“轻量级进程”,创建线程比创建进程更高效;销毁线程比销毁进程更高效;调度线程比调度进程更高效…此时,使用多线程就可以在很多时候代替进程…...

vue3 setup中defineEmits与defineProps的使用案例

目录 一、defineEmits的使用 二、 defineProps的使用 总结 一、defineEmits的使用 使用说明 1、在子组件中调用defineEmits并定义要发射给父组件的方法 const emits defineEmits([foldChange]) 2、使用defineEmits会返回一个方法&#xff0c;使用一个变量emits(变量名随意…...

Vs - Qt - 下拉窗口示例

下列代码定义了一个窗口&#xff0c;窗口采用竖直布局&#xff1a;一个按钮及一个label。按下按钮时候&#xff0c;窗口扩张&#xff0c;显示label控件。再次按下按钮时&#xff0c;窗口收缩&#xff0c;隐藏label控件。 详细代码如下&#xff1a; #include <QApplication&g…...

深圳自贸区的形成与发展

深圳自贸区的形成与发展源于中国政府推出的自贸试验区政策。自贸试验区是指在特定区域内&#xff0c;允许实行特殊的行政管理措施和贸易政策&#xff0c;以促进贸易自由化、投资便利化和经济转型升级。 深圳自贸区成立于2015年4月以来&#xff0c;主要着眼于优化区域布局、提高…...

机器人中的数值优化(二十一)—— 伴随灵敏度分析、线性方程组求解器的分类和特点、优化软件

本系列文章主要是我在学习《数值优化》过程中的一些笔记和相关思考&#xff0c;主要的学习资料是深蓝学院的课程《机器人中的数值优化》和高立编著的《数值最优化方法》等&#xff0c;本系列文章篇数较多&#xff0c;不定期更新&#xff0c;上半部分介绍无约束优化&#xff0c;…...