NumberRunningTextView(数字会滚动的TextView)

来源:互联网 发布:淘货源怎么用传淘宝 编辑:程序博客网 时间:2024/05/03 01:52

NumberRunningTextView介绍

  NumberRunningTextView是一个自带数字滚动动画的TextView,通过使用setContent(String str)方法,传入相应的金额数字字符串(如”1354.00”),或者数字的字符串(如200),当页面初始化完成的时候,就可以看到数字滚动的效果,和支付宝中进入余额宝界面,显示余额滚动的效果类似,具体的效果如下:

使用

在布局文件中,使用NumberRunningTextView,代码如下:

演示金额滚动的NumberRunningTextView

 <com.chaychan.viewlib.NumberRunningTextView            android:id="@+id/tv_money"            android:layout_width="wrap_content"            android:layout_height="wrap_cointent"            android:layout_centerInParent="true"            android:text="0.00"            android:textColor="#fff"            android:textSize="30sp"            android:textStyle="bold"            />

演示整数数字滚动的NumberRunningTextView

 <com.chaychan.viewlib.NumberRunningTextView            android:id="@+id/tv_num"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_centerInParent="true"            android:text="200"            android:textColor="#fff"            android:textSize="30sp"            app:textType="num"            />

  二者的区别在于textType的设置,textType是用于指定内容的格式,总共有money(金钱格式,带小数)和num(整数格式),默认是金钱的格式,不配置textType的话,默认就是使用金钱格式。

在Java文件中根据id找到对应的view后,调用setContent()方法即可。

 tvMoney.setContent("1354.00"); tvNum.setContent("200");

关闭金额的自动格式化(每三位数字添加一个逗号)

  上图所示,最后显示的金额数字经过了处理,每三位添加一个逗号,这使得数字看起来比较好看,金额默认是使用这种格式,如果不想要这种格式的数字,可以在布局文件中,NumberRunningTextView的配置中,将useCommaFormat设置为false,这样最终的数字就不会是带有逗号了,效果如下:

关闭执行动画的时机

  当一开始进入界面的时候,初始化数据完毕,NumberRunningTextView设置数据完毕,会自动执行数字滚动的动画,如果进行刷新操作,从服务器获取新的数据,重新设置数据,NumberRunningTextView会自动判断传入的内容是否有变化,如果没有变化,则不会再次滚动,这和支付宝的余额宝界面中的金额类似,当在余额宝界面下拉刷新时,金额没有变化,数字不会再次滚动,而当提现后重新回到该界面,金额发生变化后,就会再次滚动,效果如下:

SwipeRefreshLayout的刷新回调中,只做了这样的处理,NumberRunningTextView设置的内容还是原来的数据。

 srlRoot.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {        @Override        public void onRefresh() {            tvMoney.setContent("1354.00");            tvNum.setContent("200");            srlRoot.setRefreshing(false);        }    });

  当你进行下拉刷新的时候,内容如果没有发生变化,数字是不会滚动的,如果内容发生变化,数字又会重新进行滚动,这里修改下拉刷新的代码,模拟数据变化的情况,演示一下:

 srlRoot.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {        @Override        public void onRefresh() {            tvMoney.setContent("1454.00");            tvNum.setContent("300");            srlRoot.setRefreshing(false);        }    });

效果如下:

  如果想要在刷新的时候,无论内容是否有变化都要执行滚动动画的话,可以在布局文件中,NumberRunningTextView的配置中,将runWhenChange设置为false即可,此时,无论内容是否有变化,都会执行滚动动画,效果如下:

修改帧数

  NumberRunningTextView默认是30帧,如果需要修改帧数,可以在布局文件中,NumberRunningTextView的配置中,将frameNum设置为自己想要的帧数。

NumberRunningTextView源码解析

  NumberRunningTextView的原理其实很简单,只是将一个数字除以帧数(默认是30帧),分成每段固定的大小进行递增。

首先介绍下NumberRunningTextView自定义的属性:

<declare-styleable name="NumberRunningTextView">    <!--帧数-->    <attr name="frameNum" format="integer"></attr>    <!--内容的格式-->    <attr name="textType">        <enum name="money" value="0"></enum>        <enum name="num" value="1"></enum>    </attr>    <!--是否使用每三位数字一个逗号-->    <attr name="useCommaFormat" format="boolean"></attr>    <!--是否当内容改变的时候使用动画,不改变则不使用动画-->    <attr name="runWhenChange" format="boolean"></attr></declare-styleable>

NumberRunningTextView继承于TextView

public class NumberRunningTextView extends TextView {    public NumberRunningTextView(Context context) {        this(context, null);    }    public NumberRunningTextView(Context context, AttributeSet attrs) {        this(context, attrs, android.R.attr.textViewStyle);    }    public NumberRunningTextView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.NumberRunningTextView);        frameNum = ta.getInt(R.styleable.NumberRunningTextView_frameNum, 30);        textType = ta.getInt(R.styleable.NumberRunningTextView_textType, MONEY_TYPE);        useCommaFormat = ta.getBoolean(R.styleable.NumberRunningTextView_useCommaFormat, true);        runWhenChange = ta.getBoolean(R.styleable.NumberRunningTextView_runWhenChange,true);    }    ...}

当调用setContent()方法时,里面做了相应的判断。

  /** * 设置需要滚动的金钱(必须为正数)或整数(必须为正数)的字符串 * * @param str */public void setContent(String str) {    //如果是当内容改变的时候才执行滚动动画,判断内容是否有变化    if (runWhenChange){        if (TextUtils.isEmpty(preStr)){            //如果上一次的str为空            preStr = str;            useAnimByType(str);            return;        }        //如果上一次的str不为空,判断两次内容是否一致        if (preStr.equals(str)){            //如果两次内容一致,则不做处理            return;        }        preStr = str;//如果两次内容不一致,记录最新的str    }    useAnimByType(str);}

  如果runWhenChange为true,即当内容改变的时候才会滚动,进行如下判断,如果上次的内容为空(即第一次设置),则执行滚动,否则判断此次内容和上一次内容是否一致,如果一致,则不做处理,如果两次内容不一致,即内容发生了变化,记录当前的内容,并执行相应的滚动操作。

useAnimByType()方法只是用于判断内容的类型,并执行相应的动画,NumRunningTextView的核心为playMoneyAnim()和playNumAnim()

  private void useAnimByType(String str) {    if (textType == MONEY_TYPE) {        playMoneyAnim(str);    } else {        playNumAnim(str);    }}

下面对这两个方法进行解析:

/** * 播放金钱数字动画的方法 * * @param moneyStr */public void playMoneyAnim(String moneyStr) {    String money = moneyStr.replace(",", "").replace("-", "");//如果传入的数字已经是使用逗号格式化过的,或者含有符号,去除逗号和负号    try {        finalMoneyNum = Double.parseDouble(money);        if (finalMoneyNum == 0) {            //如果传入的为0,则直接使用setText()            NumberRunningTextView.this.setText(moneyStr);            return;        }        nowMoneyNum = 0;//记录每帧增加后的数字,一开始为0        threadPool.execute(new Runnable() {            @Override            public void run() {                Message msg = handler.obtainMessage();                double size = finalMoneyNum / frameNum;//数字除以帧数,得到每帧跳跃的大小                msg.what = MONEY_TYPE;                msg.obj = size < 0.01 ? 0.01 : size;// 如果每帧的间隔比0.01小,就设置为0.01                handler.sendMessage(msg);// 发送通知改变UI            }        });    } catch (NumberFormatException e) {        e.printStackTrace();        NumberRunningTextView.this.setText(moneyStr);//如果转换Double失败则直接用setText    }}

1.先对传入的字符串进行修改,如果有含有逗号或者负号,则去除逗号和负号;

2.对字符串进行格式转换,如果转换出现异常,则直接调用setText()方法设置内容;

3.如果传入的数字是0,则不用使用滚动动画,直接调用setText()设置内容;

4.如果传入的是正确的金额数字,将数字除以帧数frameNum,得到每一帧的大小(间隔),通过使用handler的sendMessage,传递当前内容的类型和每一帧数的大小,在Handler的handleMessage()方法中进行相应的处理。其中的threadPool为含有一个线程的线程池。threadPool = Executors.newFixedThreadPool(1);

Handler中的相应处理:

 private Handler handler = new Handler() {    @Override    public void handleMessage(Message msg) {        super.handleMessage(msg);        switch (msg.what) {            case MONEY_TYPE://金钱数字的滚动                String str = formatter.format(nowMoneyNum).toString();//保留两位小数的字符串                // 更新显示的内容                if (useCommaFormat) {                    //使用每三位数字一个逗号的格式                    String formatStr = StringUtils.addComma(str);//三位一个逗号格式的字符串                    NumberRunningTextView.this.setText(formatStr);                } else {                    NumberRunningTextView.this.setText(str);                }                nowMoneyNum += (double) msg.obj;//记录当前每帧递增后的数字                if (nowMoneyNum < finalMoneyNum) {                    //如果当前记录的金额数字小于目标金额数字,即还没达到目标金额数字,继续递增                    Message msg2 = handler.obtainMessage();                    msg2.what = MONEY_TYPE;                    msg2.obj = msg.obj;                    handler.sendMessage(msg2);// 继续发送通知改变UI                } else {                    //已经达到目标的金额数字,显示最终的数字                    if (useCommaFormat) {                        NumberRunningTextView.this.setText(StringUtils.addComma(formatter.format(finalMoneyNum)));                    } else {                        NumberRunningTextView.this.setText(formatter.format(finalMoneyNum));                    }                }                break;            case NUM_TYPE://普通数字滚动                ...                break;        }    }};

handleMessage()方法中,根据内容的类型进行相应的处理。

1.根据是否使用逗号进行格式化,显示每帧增加后的数字;

2.如果其小于finalMoneyNum目标数字(最终的数字),则继续调用sendMessage()方法,形成递归操作,直至等于或大于目标数字;

3.达到目标数字后,根据是否需要使用逗号进行格式化显示最终的效果

其中formatter = new DecimalFormat(“0.00”);// 格式化金额,保留两位小数,用于使double类型数字保留两位小数。

  到这里具体的设计思路应该都清楚了,其实就是通过使用handler不断发消息,形成递归,让数字进行递增,当达到最终的数字的时候,就停止发送消息,直接设置最终的内容。playNumAnim()也是一样的思路,在这里就不重复说明了,感觉自己的写的注释已经足够详细了,顺便贴一下playNumAnim()的方法。

/** * 播放数字动画的方法 * * @param numStr */public void playNumAnim(String numStr) {    String num = numStr.replace(",", "").replace("-", "");//如果传入的数字已经是使用逗号格式化过的,或者含有符号,去除逗号和负号    try {        finalNum = Integer.parseInt(num);        if (finalNum < frameNum) {            //由于是整数,每次是递增1,所以如果传入的数字比帧数小,则直接使用setText()            NumberRunningTextView.this.setText(numStr);            return;        }        nowNum = 0;// 默认都是从0开始动画        threadPool.execute(new Runnable() {            @Override            public void run() {                Message msg = handler.obtainMessage();                int temp = finalNum / frameNum;//数字除以帧数,得到每帧跳跃的大小                msg.what = NUM_TYPE;                msg.obj = temp;                handler.sendMessage(msg);// 发送通知改变UI            }        });    } catch (NumberFormatException e) {        e.printStackTrace();        NumberRunningTextView.this.setText(numStr);//如果转换Double失败则直接用setText    }}

handler中对应的处理:

 private Handler handler = new Handler() {    @Override    public void handleMessage(Message msg) {        super.handleMessage(msg);        switch (msg.what) {            case MONEY_TYPE://金钱数字的滚动                ...                break;            case NUM_TYPE://普通数字滚动                NumberRunningTextView.this.setText(String.valueOf(nowNum));                nowNum += (Integer) msg.obj;//记录当前每帧递增后的数字                if (nowNum < finalNum) {                    //如果当前记录的数字小于目标数字,即还没达到目标数字,继续递增                    Message msg2 = handler.obtainMessage();                    msg2.what = NUM_TYPE;                    msg2.obj = msg.obj;                    handler.sendMessage(msg2);// 继续发送通知改变UI                } else {                    //已经达到目标的数字,显示最终的内容                    NumberRunningTextView.this.setText(String.valueOf(finalNum));                }                break;        }    }};

到这里为止,NumberRunningTextView的使用介绍和源码解析就结束了。下面介绍该控件的导入方式

导入方式

在项目根目录下的build.gradle中的allprojects{}中,添加jitpack仓库地址,如下:

allprojects {    repositories {        jcenter()        maven { url 'https://jitpack.io' }//添加jitpack仓库地址    }}

打开app的module中的build.gradle,在dependencies{}中,添加依赖,如下:

dependencies {        ......        compile 'com.github.chaychan:PowerfulViewLibrary:1.1.0'}

源码github地址:https://github.com/chaychan/PowerfulViewLibrary.git

  依旧花了半天时间,完成了我的第四篇博客。上次写博客是在3月8号,其实一直想写多一点博客,但是一直觉得要么不写,要写就要写质量高的博客,虽然需要耗费许久的时间,因为我写文章都追求清晰明了,通俗易懂,不让读者觉得一头雾水,追求质量而不是数量。写博客也要有好的题材,想到哪个知识点比较让读者感兴趣和可以帮助到读者日常的开发,此次写这个NumberRunningTextView是在做项目的时候,在显示用户余额的时候,根据需求需要数字滚动的效果,所以就上网找了思路,自己写了这样一个View,觉得应该很多人都会需要用到,希望可以帮助到大家。

0 0
原创粉丝点击