实现Android中TextView的跑马灯效果

转载请标明出处:http://blog.csdn.net/edisonchang/article/details/50000879

前几天一直下雪,早上过来上班,路上特别拥堵,还好赶上了早餐。回到工位后,刚入职不久的产品小鲜肉匆忙过来,希望我在应用列表的头部加一个跑马灯的文字滚动效果,并且能在中午能够看到结果。

对完需求后我就开始了调研和开发工作,因为Android的TextView天生支持跑马灯效果,所以一切看起来都是水到渠成,信手拈来。首先,使用android 原生的TextView控件实现走马灯的滚动效果,需要了解以下几项属性:

     android:ellipsize="marquee" 
     android:marqueeRepeatLimit="marquee_forever" 

android:ellipsize=”marquee” 表示当文本长度大于View的宽度时以横向滚动方式显示;
android:marqueeRepeatLimit以设置滚动次数,marquee_forever表示无限循环 。

但是仅设置这两个属性,运行代码后TextView是没有滚动效果的,从Textview 实现跑马灯的源码可以看出TextView需要持有焦点才能实现跑马灯效果。

final TextView textView = mView.get();
            if (textView != null && (textView.isFocused() || textView.isSelected())) {
                long currentMs = mChoreographer.getFrameTime();
                long deltaMs = currentMs - mLastAnimationMs;
                mLastAnimationMs = currentMs;
                float deltaPx = deltaMs / 1000f * mPixelsPerSecond;
                mScroll += deltaPx;
                if (mScroll > mMaxScroll) {
                    mScroll = mMaxScroll;
                    mChoreographer.postFrameCallbackDelayed(mRestartCallback, MARQUEE_DELAY);
                } else {
                    mChoreographer.postFrameCallback(mTickCallback);
                }
                textView.invalidate();
            }

解决这个问题也不复杂,对我们的TextView加上
android:focusable=”true”
android:focusableInTouchMode=”true” 两个属性, 运行代码后TextView就自动滚动起来。从上面的例子可以看出TextView如果实现跑马灯效果,需要同时具备三个条件:

(1)android:ellipsize=”marquee”
(2)TextView单行显示,且内容必须超出TextView大小
(3)TextView要获得焦点

产品同学看了demo的跑马灯效果后,还是比较满意的,与此同时也提出另一些需求,他希望在列表的头部和一些特定的item里面都有滚动效果的TextView,并且可以同时滚动。 这一下确实还是有点为难,TextView要能滚动必须获得当前焦点,而对于一个View上的元素来讲,至多只有一个View能够获取焦点,所以多个TextView同时实现跑马灯效果,已经不是简单的配置就能满足需求了。

但是,我们还是很快就想到了解决方案。其实,从源码中,我们也可以看出,TextView的滚动显示与焦点有关系,一旦TextView失去焦点,TextView会立即停止滚动,所以要想实现多个TextView的同时跑马灯滚动,还得从TextView 的isFocused()方法入手。解决手段简单粗暴,我们直接重写isFocused()方法,让这个方法一直返回true, 果然多个TextView的跑马灯效果可以一并动起来了,示例代码如下:

public class MarqueeTextView extends TextView {

    public MarqueeTextView(Context context) {
        this(context, null);
    }

    public MarqueeTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MarqueeTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean isFocused() {
        return true;
    }
}

但是事情往往没有想象中的顺利,将demo测试过的代码合到我们主程序时,却发现首次进入页面后ListView head 中的TextView 无法自动滚动,如果home键退出,或者切到其他页面再返回,就会顺利的滚动起来。

原因在于TextView启动跑马灯时,ListView并没有Layout完成,此时TextView的mLayout 为空 ,导致首次无法自动滚动,解决方法可以延时调用下 setSelected 方法,这里还有一些细节要注意,

   @Override
    public void setSelected(boolean selected) {
        boolean wasSelected = isSelected();

        super.setSelected(selected);

        if (selected != wasSelected && mEllipsize == TextUtils.TruncateAt.MARQUEE) {
            if (selected) {
                startMarquee();
            } else {
                stopMarquee();
            }
        }
    }

以上是setSelected方法的源码实现,从源码中我们发现如果原来TextView 已经是被选中状态,再次调用也是没有效果的,所以我们可以先调用 setSelected(false), 再调用 setSelected(true),这样一来就可以保证TextView的正常滚动 。

    public void setText(String text) {
       super.setText(text);
        setHorizontallyScrolling(true);

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                setSelected(false);
                setSelected(true);
            }
        }, 3000);
    }

至此,跑马灯效果顺利实现了,产品也效果也比较满意。如果您对文章内容有兴趣,请多多关注,有问题请回复

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值