Android数字纵向滚动_android数字滚动。-程序员宅基地

技术标签: 移动  android  界面  

有的时候我们为了追求界面的美观性,希望将数字显示出来的时候有动画效果,比如界面显示315,我们可以让这几个数字自动纵向滚动,最后停留在315,这样显示出来效果更好一些。


要实现这种自动滚动的效果,我们首先想到的是自定义SurfaceView,在SurfaceView中启动一个线程来完成需要的工作。我这里自定义的SurfaceView有一个方法

setCircleAndNumber(int scrollCircle, int scrollNumber),scrollCircle为想要数字旋转的圈数(从0开始,顺次1,2,一直到9,算一圈,完后再从0开始,循环),scrollNumber为最终停留的数字,比如刚才说的315,主类如下:


package com.example.scrollnumber;

import com.example.scrollnumber.R;
import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {
	ScrollNumber scrollNumber;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		scrollNumber = (ScrollNumber) findViewById(R.id.scroll_number);
		scrollNumber.setCircleAndNumber(2, 315);
	}
}

这里旋转2圈后,停留在315这个数字


首先我们需要设置一个背景,这个背景是正好包含我们要滚动的数字,四周留一点空隙即可

float startX = 30f; // 数字的初始x坐标
			float startY = 30f; // 数字的初始y坐标
			float curY = startY; // 当前的y坐标

			FontMetrics fontMetrics = paint.getFontMetrics();
			float numberHeight = fontMetrics.bottom - fontMetrics.top; // 数字的高度
			RectF r = new RectF(startX, startY - numberHeight / 2 - 10, startX
					+ paint.measureText("" + scrollNumber), startY + numberHeight / 2);

完后我们从绘制000开始(以滚动到315为例),我们需要同时绘制出111,在000的上面,但是在背景外,所以111初始时是看不见的,完后我们开始向下滚动000,每次滚动10,每50毫秒滚动一次,这样让上面的111慢慢显示出来,基本原理就是用canvas.drawText,不断改变y坐标,当y坐标大于初始坐标加上字体高度时,就应该绘制下一个数字了,这里也就是等到111显示出来的时候,我们就绘制上面的222

if (curY < startY + numberHeight) {
						curY += movingStep;
						
						if (elapseCount == scrollCircle * radix + toNumber - 1 && curY > startY + numberHeight) { // 如果已经在滚向最后一位数字,并且如果滚动movingStep会超过要移动到的最终距离,则只滚动需要的部分
							curY = startY + numberHeight;
						}
					} else { // 两个数字之间的高度差是numberHeight,所以当新的数字出现时,要给curY和elapseCount重新赋值
						curY = startY;
						elapseCount++;
					。。。 }

以此类推,直到我们转过了传过来的旋转的圈数后(这个例子里面是2圈),我们再从0旋转到3,那么最高位就停止了,我们记录下来,然后我们用alreadyBits记录已经滚动到位的位数,用totalBits记录总位数,两者相比,如果不相等说明没有滚动完,就接着继续滚动下一位,以此类推

if (alreadyBits == totalBits) { // 所有位数滚动完毕
								isRun = false;
							} else { // 还有位数没有滚动完毕
								int nextValue = (scrollNumber + "").charAt(alreadyBits) - 48; // 下一位要滚动到的值
								
								toNumber = elapseCount; // 刚滚动完毕那位的值
								
								if (toNumber >= nextValue) { // 如果下一位比当前位要小或者相等,那么要滚动到的位数就要+10,比如21,那么当2滚动到位的时候,1那位的值是2,它还要滚动一圈才能到1
									toNumber = radix + nextValue;
								} else {
									toNumber = nextValue;
								}
							}

直到最后alreadyBits和totalBits相等,我们将isRun设为false,这个布尔型变量用来控制线程是否继续执行,完整代码如下:


package com.example.scrollnumber;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.FontMetrics;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;

public class ScrollNumber extends SurfaceView implements Callback {
	private boolean isRun;
	private SurfaceHolder holder;
	private int scrollCircle; // 滚动圈数,0-9为一圈
	private int scrollNumber; // 滚动到的数字

	public ScrollNumber(Context context) {
		super(context);
	}
	
	public ScrollNumber(Context context, AttributeSet attr) {
		super(context, attr);
		
		holder = getHolder();
		holder.addCallback(this);
	}
	
	@Override
	public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
	}

	@Override
	public void surfaceCreated(SurfaceHolder arg0) {
		isRun = true;
		new NumberThread(holder).start();
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder arg0) {
		isRun = false;
	}
	
	public void setCircleAndNumber(int scrollCircle, int scrollNumber) {
		this.scrollCircle = scrollCircle;
		this.scrollNumber = scrollNumber;
	}

	class NumberThread extends Thread {
		SurfaceHolder holder;

		public NumberThread(SurfaceHolder holder) {
			this.holder = holder;
		}

		@Override
		public void run() {
			super.run();

			float startX = 30f; // 数字的初始x坐标
			float startY = 30f; // 数字的初始y坐标
			float curY = startY; // 当前的y坐标

			Paint paint = new Paint();
			paint.setColor(Color.WHITE);
			paint.setAntiAlias(true);
			paint.setTextSize(20);
			FontMetrics fontMetrics = paint.getFontMetrics();
			float numberHeight = fontMetrics.bottom - fontMetrics.top; // 数字的高度
			RectF r = new RectF(startX, startY - numberHeight / 2 - 10, startX
					+ paint.measureText("" + scrollNumber), startY + numberHeight / 2);
			
			int elapseCount = 0; // 每一位要转过的数字的个数
			float width = paint.measureText("0"); // 一个数字的宽度
			int totalBits = ("" + scrollNumber).length(); // 要滚动的数字的位数
			boolean[] scrollFinished = new boolean[totalBits]; // 纪录每一位是否滚动完毕
			int toNumber = scrollNumber / (int)Math.pow(10, totalBits - 1); // 每一位要滚动到的数字,初始值为最高位的值
			int alreadyBits = 0; // 当前已滚动完毕的位数
			int movingStep = 10; // 每次滚动的距离
			int radix = 10; // 基数,也就是0-9共10个数字
			int interval = 50; // 每次滚动的间隔

			while (isRun) {
				Canvas canvas = null;

				try {
					canvas = holder.lockCanvas();

					canvas.clipRect(r);

					canvas.drawColor(Color.BLACK);

					for (int i = 0; i < totalBits; i++) {
						if (scrollFinished[i]) { // 该位滚动完成,直接绘制该位数字
							canvas.drawText(("" + scrollNumber).charAt(i) - 48 + "", startX + width * i, startY, paint);
						} else { // 尚在滚动中,需要绘制该位以及下一位数字
							canvas.drawText("" + (elapseCount + 1) % radix, startX + width * i, curY - numberHeight, paint);
							canvas.drawText("" + elapseCount % radix, startX + width * i, curY, paint);
						}
					}
					
					if (curY < startY + numberHeight) {
						curY += movingStep;
						
						if (elapseCount == scrollCircle * radix + toNumber - 1 && curY > startY + numberHeight) { // 如果已经在滚向最后一位数字,并且如果滚动movingStep会超过要移动到的最终距离,则只滚动需要的部分
							curY = startY + numberHeight;
						}
					} else { // 两个数字之间的高度差是numberHeight,所以当新的数字出现时,要给curY和elapseCount重新赋值
						curY = startY;
						elapseCount++;
						
						if (elapseCount == scrollCircle * radix + toNumber) { // alreadyBits位已滚动到位
							scrollFinished[alreadyBits] = true;
							
							scrollCircle = 0; // 一旦最高位转动完毕,后面的位数转动都不再会超过一圈,所以圈数赋值为0
							elapseCount = (scrollNumber + "").charAt(alreadyBits) - 48; // 获取到alreadyBits位的值
							
							alreadyBits++;
							
							if (alreadyBits == totalBits) { // 所有位数滚动完毕
								isRun = false;
							} else { // 还有位数没有滚动完毕
								int nextValue = (scrollNumber + "").charAt(alreadyBits) - 48; // 下一位要滚动到的值
								
								toNumber = elapseCount; // 刚滚动完毕那位的值
								
								if (toNumber >= nextValue) { // 如果下一位比当前位要小或者相等,那么要滚动到的位数就要+10,比如21,那么当2滚动到位的时候,1那位的值是2,它还要滚动一圈才能到1
									toNumber = radix + nextValue;
								} else {
									toNumber = nextValue;
								}
							}
						}
					}

					Thread.sleep(interval);
				} catch (Exception e) {
					Log.d("ScrollNumber", "Error:" + e.toString());
				} finally {
					if (canvas != null) {
						holder.unlockCanvasAndPost(canvas);
					}
				}
			}
		}
	}
}

布局文件如下:

<RelativeLayout 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:background="#000"
    >

    <com.example.scrollnumber.ScrollNumber
        android:id="@+id/scroll_number"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />

</RelativeLayout>

如果有更好的方法或者别的思路,欢迎提出来共同学习


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/gesanri/article/details/44495245

智能推荐

erdas正射校正、数据融合、影像镶嵌_erdas imagine制作dtm和dom-程序员宅基地

文章浏览阅读1.3w次,点赞15次,收藏107次。在几个传统影像处理软件中,erdas的处理速度往往是最快的(比起ENVI、argis),而且img格式稳定,不易变化,个人使用首推erdas,当然大规模生产的话还是任务订单式的GXL好,它在批处理的路上走得更远,以web方式提交任务,以集群方式处理数据,高并发的处理能力,估计很多传统做影像处理的人要失业,扯远了。下面是望神州公司的一个erdas操作教程,本人无意抄袭,只想给正在用ERDAS的人..._erdas imagine制作dtm和dom

C语言 数据类型 字符型_字符 值-程序员宅基地

文章浏览阅读497次。n==\12==\xa都为换行的意思 \t缩格制表符 \\就是\ \' \"由此可以看出,无论用十进制、八进制还是十六进制都可以用来表示字符。要注意的是,在输入时空格也算字符、双字节编码 L是双字节字符前缀。数字与数字字符互转:+ -‘0’大小写字母互转:+ -32。_字符 值

柔性轴承故障诊断与寿命预测——信号的高维数据特征提取与-映射降维融合方法(LLE, PCA)_轴承故障信号特征提取哪些特征比较有用-程序员宅基地

文章浏览阅读683次。均方根值RMS、峭度K、波峰因子Crest、峰峰值P-P、脉冲因子Impluse、裕度因子Margin、波形因子W、时域信息熵Ht、总功率谱Gt、莱斯频率fx、频率重心fc、频率方差Vf、谐波指标H、均方频率MSF、频域信息熵Hf,等等当然我们需要根据实际的情况来设计时频域的一些指标,构建多个敏感的指标是极其关键的,因为如果指标本身对故障并不敏感,那我们做融合后就基本也是没有效果的,所以并不是上述普遍的指标就可以拿来构建,我们需要根据全寿命周期找出时频域的特征,期间会使用较多的信号处理方法。_轴承故障信号特征提取哪些特征比较有用

kuka机器人报电源管理报错_KUKA机器人出错提示和故障排除信息-程序员宅基地

文章浏览阅读3.2k次。广州子锐机器人技术有限公司:KUKA机器人出错提示和故障排除信息,提示窗口将显示各种类型的显示。它们既可以是不必确认的信息,也可以是必须予以确认的提示 一个提示可以由下列部分构成:1.1 提示组说明性提示例如按下某个不允许的键,它给使用者一个说明。状态提示提示设备的状态。该状态致使控制器发生反应(例如紧急关断等)消除提示的起因后,提示将被删除。安全起见,有时会设置一个有待确认的后续提示。确认性提..._kuka充电电池或电池保险丝损坏 — 无法缓存

osg之osgViewer基础应用_osgviewer::helphandler-程序员宅基地

文章浏览阅读2.5k次。最开始的应用是读取模型了int main(){ osg::ref_ptr viewer=new osgViewer::Viewer; osg::ref_ptr node=new osg::Node; node=osgDB::readNodeFile("glider.osg"); //添加帮助 vie_osgviewer::helphandler

mysql空间释放_MySQL Delete 后,如何快速释放磁盘空间-程序员宅基地

文章浏览阅读898次。一、起因:收到运维需求需要清理两张监控告警的日志表,数据删除之后,发现磁盘空间并未释放。二、分析:InnoDB 数据库在使用 delete 进行删除操作的时候,只会将已经删除的数据标记为删除,并没有把数据文件删除,因此并不会彻底的释放空间。这些被删除的数据会被保存在一个链接清单中,当有新数据写入的时候,MySQL 会重新利用这些已删除的空间进行再写入。三、解决:官方推荐可以使用 OPTIMIZE ..._mysql delete sys_quartz_log 怎么释放空间

随便推点

Unsupported compiler 'com.apple.compilers.llvmgcc42' selected for architecture 'armv7'-程序员宅基地

文章浏览阅读601次。Xcode8 报错:Unsupported compiler 'com.apple.compilers.llvmgcc42' selected for architecture 'armv7'解决方法:Change your compiler for C/C++/ObjectiveC Go to--->Build Settings--->Build OPtions-

python入门(13)异常与文件_except filenotfounderror:-程序员宅基地

文章浏览阅读1.4k次,点赞2次,收藏3次。在Python中,我们可以自定义异常类来满足特定的异常情况。自定义异常可以继承自内置的异常类或其他已定义的异常类,以便更好地区分和处理特定类型的错误。if b == 0:try:main()在上述示例中,我们定义了一个名为的自定义异常类。它继承自内置的Exception类,并添加了__init__和__str__方法用于初始化异常对象和返回异常信息。在函数中,我们进行了除法运算,如果除数为零,则抛出异常,并传入错误信息。在main()函数中,我们捕获并处理异常,并打印异常信息。_except filenotfounderror:

Android面试攻略_详细了解在当今的社会里android工程师应具备什么的技能?并能详细说说自己的见解。-程序员宅基地

文章浏览阅读1.6k次。文章背景在外界看来IT是一个”高薪”的行业,都羡慕不已,熟不知IT也是一个苦逼的行业,每天加班加点,时刻用生命在写代码啊,导致许多程序猿和程序媛们都找不到对象,好不容易有个家庭的也影响夫妻生活、家庭生活哪,那么作为攻城狮为之能弥补方法之一无非就是不断提升自己,不断加薪,那么问题就来了,天下老板都爱画饼、只见其说,不见其果,那么攻城狮们跳槽成了加薪比较好的手段之一,导致IT部门人员更替频繁、_详细了解在当今的社会里android工程师应具备什么的技能?并能详细说说自己的见解。

Zendframework 1.6整合Smarty_setting private or protected class member is not a-程序员宅基地

文章浏览阅读1.4k次。参考文章: http://devzone.zend.com/node/view/id/120部分中文翻译: http://www.codebit.cn/pub/html/php_mysql/tutorial/integrating_smarty_with_the_zend_framework/ 通过 smarty 扩展zend framework view控制器的关键是集成 Zend__setting private or protected class member is not allowed

Qt-装饰者模式_qt装饰模式-程序员宅基地

文章浏览阅读1.1k次,点赞2次,收藏4次。1. 定义装饰者模式 装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。装饰者和被装饰对象有共同的超类型你可以用一个或多个装饰者包装一个对象。既然装饰者和被装饰对象有相同的类型,所以在任何需要原始对象(被包装)的场合,可以用装饰过的对象代替它。装饰者可以在所委托被装饰者的行为之前与/或之后,加上自己的行为,以达到特定的目的。对象可以在任_qt装饰模式

新开普掌上校园服务管理平台service.action RCE漏洞复现 [附POC]-程序员宅基地

文章浏览阅读307次。新开普掌上校园服务管理平台service.action接口处存在远程命令执行漏洞,攻击者可在未经身份认证的情况下,调用后台接口,执行恶意系统命令。_新开普掌上校园服务管理平台

推荐文章

热门文章

相关标签