TextSwticher 与 TextView 实现上下滚动和跑马灯效果
来源:互联网 发布:淘宝怎么开微淘 编辑:程序博客网 时间:2024/05/07 18:57
作者:郑少锐 ,欢迎转载,也请保留这份申明,谢谢。
请保留这份申明:http://blog.csdn.net/u011418943/article/details/51871482
太久没写博客了,主要是想写一些有质量的,今天碰到这个需求觉得挺有意思的,即像标题写的那样,这里记录一下,废话不多说,先上一个效果图:
单行显示:
大于一行显示:
即,单行上下滚动,大于一行则实现跑马灯。
这里有两个难点:
· 如何判断字幕是超过了一行,这个受字体大小和字节编码影响的
· 如何根据超过了多少定制不同的滚动时间
首先还是先实现上下滚动的效果,从最基础的步骤来
1、TextSwitcher 上下滚动
我们需要上下滚动的效果,所以需要重新 TextSwitcher 的构造方法,这个跟ImageSwitcher 一个样,差别在 ImageSwitcher 的 makeview 返回的是Imageview,而 TextSwitcher 的makeview 返回的 TextView 。首先,先实现上下滚动:
public class TextSwitcherView extends TextSwitcher implements ViewFactory { private ArrayList<String> reArrayList = new ArrayList<String>(); private int resIndex = 0; private final int UPDATE_TEXTSWITCHER = 1; private int timerStartAgainCount = 0; private Context mContext; public TextSwitcherView(Context context) { super(context); // TODO Auto-generated constructor stub mContext = context; init(); } public TextSwitcherView(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; init(); // TODO Auto-generated constructor stub } private void init(){ this.setFactory(this); this.setInAnimation(getContext(),R.anim.vertical_in); this.setOutAnimation(getContext(), R.anim.vertical_out); Timer timer = new Timer(); timer.schedule(timerTask, 1,3000); } TimerTask timerTask = new TimerTask() { @Override public void run() { //不能在这里创建任何UI的更新,toast也不行 // TODO Auto-generated method stub Message msg = new Message(); msg.what = UPDATE_TEXTSWITCHER; handler.sendMessage(msg); } }; Handler handler = new Handler(){ public void handleMessage(Message msg) { switch (msg.what) { case UPDATE_TEXTSWITCHER: updateTextSwitcher(); break; default: break; } }; }; /** * 需要传递的资源 * @param reArrayList */ public void getResource(ArrayList<String> reArrayList) { this.reArrayList = reArrayList; } public void updateTextSwitcher() { if (this.reArrayList != null && this.reArrayList.size()>0) { this.setText(this.reArrayList.get(resIndex++)); if (resIndex > this.reArrayList.size()-1) { resIndex = 0; } } } @Override public View makeView() { // TODO Auto-generated method stub TextView tView = new TextView(getContext()); tView.setTextSize(20); tView.setTextColor(0xff24aaff); return tView; }}
数据的获取是通过一个 ArrayList 来获取的,然后用一个定时器Timer实现上下滚动,3s执行一次。现在的 makeview 返回的是一个TextView ,所以现在只是上下滚动。
上下滚动动画代码如下:
vertical_in.xml:
<?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android" > <translate android:duration="1000" android:fromYDelta="100%p" android:toYDelta="0%p" /></set>
vertical_out.xml:
<?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android" > <translate android:duration="1000" android:fromYDelta="0%p" android:toYDelta="-100%p" /></set>
2、TextView 添加跑马灯效果
我们知道,跑马灯的效果是focusable = true,即要焦点放在它身上,所以,这里我们继承 TextView,重新 isFocus 方法即可,即可实现跑马灯的效果,如下:
public class WaterTextView extends TextView { public WaterTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO Auto-generated constructor stub } public WaterTextView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub } public WaterTextView(Context context) { super(context); // TODO Auto-generated constructor stub } @Override @ExportedProperty(category = "focus") public boolean isFocused() { // TODO Auto-generated method stub return true; }}
既然要实现跑马灯,所以,要在 makeView 返回中,修改返回的是我们的重新的TextView 即可。如:
public View makeView() { // TODO Auto-generated method stub WaterTextView tView = new WaterTextView(getContext()); tView.setSingleLine(true); tView.setEllipsize(TruncateAt.MARQUEE); tView.setPadding(getResources().getDimensionPixelOffset(R.dimen.x10), getResources().getDimensionPixelOffset(R.dimen.x5), getResources().getDimensionPixelOffset(R.dimen.x10), getResources().getDimensionPixelOffset(R.dimen.x5)); tView.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimensionPixelOffset(R.dimen.x28)); tView.setMarqueeRepeatLimit(1); Log.d("zsr", "more line"); return tView; }
这里设置了一些跑马灯的属性,注意字体那里,有两个参数,第一个传入的是像素的标志位,因为 setTextSize 的默认参数为 sp,如果后面传入的是像素,是要换算的,所以这里,我们统一用像素,如果你对自适应不了解的话,可以看我以前写的博客:
http://blog.csdn.net/u011418943/article/details/51247828
你现在可以传入一些比较长的参数,看看是不是当字数超过一行地时候,有流水灯的效果。
3、如何判断超过了一行
在这里纠结了很多时间,刚开始的时候考虑像素什么问题,但获取的数据都不对,这时我的想法是这样的,既然规定了字体大小,那么字体的字节大小就可以确定的,再根据计算一个字节占了屏幕多少,就可以计算一个一行占了多少字节了。
虽然这种方法会存在一些问题,但目前我在公司的板子上都没出现过。获取字节,中文一般占两个字节,英语占一个字节。
下面是我的获取字节的方法:
public void subStr(String str, int subSLength) throws UnsupportedEncodingException{ char[] array = str.toCharArray(); //获取字节 Log.d("zsr", "strbyte/arraybyte: "+str.toString().getBytes("GBK").length+" "+array.length); if (str.toString().getBytes("GBK").length > subSLength) { int shi = str.toString().getBytes("GBK").length/subSLength; int ge = str.toString().getBytes("GBK").length%subSLength; if (shi > 0 && ge != 0) { //不小于一行,分开显示 for (int i = 0; i < array.length; i++) { if((char)(byte)array[i]!=array[i]){ byteCount += 2; //如果是中文,则自加2 stringCount += 1; }else{ byteCount += 1; //如果不是中文,则自加1 stringCount += 1; } if (byteCount >= subSLength) { getRealCount = stringCount; byteCount = 0; stringCount = 0; } } }else { subArrayList.add(str); //小于一行则正常显示 Log.d("zsr", "cannot read?"); } }
我的一行是 48 ,即subSLength = 48 ,既然我们已经成功截取了一行,这里就有两种方法了。
· 不采用跑马灯,采用分行,即截断分行一行一行显示
· 继续采用跑马灯,等检测到大于一行,让上下滚动效果消失,滚动完再上下滚动
第一种: 在上面的基础上修改:
public void subStr(String str, int subSLength) throws UnsupportedEncodingException{ char[] array = str.toCharArray(); //获取字节 Log.d("zsr", "strbyte/arraybyte: "+str.toString().getBytes("GBK").length+" "+array.length); if (str.toString().getBytes("GBK").length > subSLength) { int shi = str.toString().getBytes("GBK").length/subSLength; int ge = str.toString().getBytes("GBK").length%subSLength; if (shi > 0 && ge != 0) { //不小于一行,分开显示 for (int i = 0; i < array.length; i++) { if((char)(byte)array[i]!=array[i]){ byteCount += 2; //如果是中文,则自加2 stringCount += 1; }else{ byteCount += 1; //如果不是中文,则自加1 stringCount += 1; } if (byteCount >= 48) { getRealCount = stringCount; byteCount = 0; stringCount = 0; } } Log.d("zsr_count", "waterloopTime: "+waterloopTime); for (int i = 0; i <= shi; i++) { //把超过一行地数据存到一个数组 if (i == shi) { subArrayList.add(str.substring(getRealCount*i,str.length())); }else { subArrayList.add(str.substring(getRealCount*i,getRealCount*(i+1))); } } } }else { subArrayList.add(str); //小于一行则正常显示 Log.d("zsr", "cannot read?"); } }
这里的思路是,把多行的截取成一段段存到一个数组里,注意这里的字节和数组的元素是不一样的,中文英语都是一个,所以,我才在上面写上StringCount。那么更新这里,就写上:
/** * 需要传递的资源 * @param reArrayList */ public void setArrayListData(ArrayList<String> reArrayList) { this.reArrayList = reArrayList; this.dataflag = 1; } public void setDefaultData(String string) { this.string = string; this.dataflag = 0; }数据处理:public void updateTextSwitcher() { if (dataflag == 1) { //如果有数据,显示数据 if (this.reArrayList != null && this.reArrayList.size()>0) { try { //this.setText(subStr(reArrayList.get(resIndex++),48)); if (!subArrayFlag) { subStr(reArrayList.get(resIndex++), 48); if (subArrayList != null && subArrayList.size()>0) { Log.d("zsr", "size: "+subArrayList.size()); if (subArrayList.size() == 1) { //单行 this.setText(subArrayList.get(subIndex)); subArrayList.clear(); }else if(subArrayList.size() > 1) { this.setText(subArrayList.get(subIndex)); subArrayFlag = true; } } }else { subIndex++; if (subArrayList != null && subArrayList.size()>0) { if (subIndex == subArrayList.size()-1) { this.setText(subArrayList.get(subIndex)); subArrayFlag = false; subArrayList.clear(); subIndex = 0; } } } } catch (UnsupportedEncodingException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } if (resIndex >=reArrayList.size()) { resIndex = 0; } } }else { //没有 this.setText(this.string); } }
第二种,实现跑马灯:
怎么样取消上下滚动,让左右滚动呢?因为我们上面的是一个定时器Timer的,所以,只要改变参数就行了,为了方便大家看,我们重新写一个更新的方法,如:
private void updateText(){ if (this.dataflag == 1) { if (this.reArrayList != null && this.reArrayList.size()>0) { try { subStr(reArrayList.get(resIndex), 48); Log.d("zsr", "size: "+subArrayList.size()); if (subArrayList != null && subArrayList.size()>0) { if (subArrayList.size() == 1){ // 单行 waterTextStatus = false; this.setText(this.reArrayList.get(resIndex)); subArrayList.clear(); Log.d("zsr", "ONE"); }else if(subArrayList.size()>1){ // 多行 waterTextStatus = true; this.setText(this.reArrayList.get(resIndex)); Log.d("zsr", "MORE"); subArrayList.clear(); } } resIndex++; if (resIndex >= reArrayList.size()-1) { resIndex = 0; } } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }else { this.setText(this.string); } }
其实跟上面没啥区别。注意!这里,我用了waterTextStatus 来区别是不是大于一行。然后接着就是改变变量了
class MyTimerTask extends TimerTask { @Override public void run() { // TODO Auto-generated method stub if (!waterTextStatus) { msg = new Message(); msg.what = UPDATE_TEXTSWITCHER; handler.sendMessage(msg); }else { //多行 startAgainCount ++; if (startAgainCount > waterloopTime) { startAgainCount = 0; waterTextStatus = false; } } } };
线程的更新也很简单:
Handler handler = new Handler(){ public void handleMessage(Message msg) { switch (msg.what) { case UPDATE_TEXTSWITCHER: //updateTextSwitcher(); //index = next(); updateText(); break; default: break; } }; };
可以看到,如果多行,就不然它去刷新线程的UI更新,等它跑完了再去更新,至于怎么判断是否跑完呢?这里我也不知道怎么弄,这能用笨方法,就是延时,尴尬尴尬:
public void subStr(String str, int subSLength) throws UnsupportedEncodingException{ char[] array = str.toCharArray(); //获取字节 Log.d("zsr", "strbyte/arraybyte: "+str.toString().getBytes("GBK").length+" "+array.length); if (str.toString().getBytes("GBK").length > subSLength) { int shi = str.toString().getBytes("GBK").length/subSLength; int ge = str.toString().getBytes("GBK").length%subSLength; if (shi > 0 && ge != 0) { //不小于一行,分开显示 for (int i = 0; i < array.length; i++) { if((char)(byte)array[i]!=array[i]){ byteCount += 2; //如果是中文,则自加2 stringCount += 1; }else{ byteCount += 1; //如果不是中文,则自加1 stringCount += 1; } if (byteCount >= 48) { getRealCount = stringCount; byteCount = 0; stringCount = 0; } } Log.d("zsr_count", "shi/ge: "+shi+" "+ge); if (ge>0 && ge<=7) { waterloopTime = 3*shi; }else if (ge>7 && ge<=16) { waterloopTime = 3*shi+1; }else if(ge>16 && ge<=25){ waterloopTime = 4*shi+1; }else if(ge>25 && ge<=35){ waterloopTime = 5*shi+1; }else { waterloopTime = 6*shi+2; } Log.d("zsr_count", "waterloopTime: "+waterloopTime); for (int i = 0; i <= shi; i++) { if (i == shi) { subArrayList.add(str.substring(getRealCount*i,str.length())); }else { subArrayList.add(str.substring(getRealCount*i,getRealCount*(i+1))); } } } }else { subArrayList.add(str); //小于一行则正常显示 Log.d("zsr", "cannot read?"); } }
可以看到,这里的waterloopTime 是根据截取的行数和个数来计算的,当然,我现在心里一直有点忐忑,延时能不用就不要用的,如果你有好建议,也请告诉,谢谢。
布局很简单:
<com.vst.launchertext.TextSwitcherView android:id="@+id/watertext" android:layout_width="match_parent" android:layout_height="match_parent" > </com.vst.launchertext.TextSwitcherView>
对了,像这些标志位的申明,我们需要用volatile 关键字修饰,目的是让它能及时通过线程更新。
算法已解决,欢迎查看我的这篇文章:
http://blog.csdn.net/u011418943/article/details/51889962
- TextSwticher 与 TextView 实现上下滚动和跑马灯效果
- js实现上下滚动的跑马灯效果
- ViewGroup实现可以上下、左右滚动跑马灯效果
- 跑马灯效果 文字上下滚动效果
- Textview的文字滚动,跑马灯效果的实现
- 一分钟实现 Android textview 跑马灯文字滚动效果
- 自定义View—实现滚动TextView(跑马灯)效果
- TextView实现滚动字幕的效果【跑马灯】
- TextView实现跑马灯效果(文字滚动)
- Android TextView实现滚动跑马灯效果Marquee
- Android实现跑马灯Textview滚动效果的问题解决
- 自定义textview实现竖直滚动跑马灯效果
- TextView上下滚动实现通知效果
- 上下滚动的图片demo 上下跑马灯效果
- TextView实现跑马灯效果
- TextView实现跑马灯效果
- TextView实现跑马灯效果
- TextView实现跑马灯效果
- 求一个数的所有因子的积
- C库函数之strcpy,strncpy,memcpy
- JavaScript 将类数组对象转化为数组
- Linux系统自带spi驱动加载及应用程序编写方法详解
- JMeter带json数据的post请求测试
- TextSwticher 与 TextView 实现上下滚动和跑马灯效果
- Android面试一天一题——概述篇
- 自己实现的一个可以滚动展示数字的控件:DPScrollNumberLabel
- Java中Long与long的区别(转)
- 爱我T4 T4B T4+ A2.150129 MT6582官方线刷包+工具+驱动 解决rom被破坏问题亲测可用
- 大话设计模式读书笔记(一)
- leetcode2 Add Two Numbers
- 312. Burst Balloons
- Svn命令详解