android app 固定纵向,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);

}

}

}

}

}

}

布局文件如下:

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值