实现Android中TextView的跑马灯效果

来源:互联网 发布:中国网络好声音苏棱然 编辑:程序博客网 时间:2024/04/30 23:41

转载请标明出处: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 0
原创粉丝点击