注:该文章为(男人应似海)原创,如需转载请注明出处!
该组件的功能有三个:
(1) 可指定文字相对Button左边界显示的位置。
(2) 可根据指定的可视宽度自动将字符串后面无法显示的部分用省略号代替。
(3) 可实现跑马灯效果。
这三种功能可通过分别调用MyButton的三个init方法来实现。效果图如下:
代码:
package diy.ts.wader.widget;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
/******************************************************************
* 文件名称 : MyButton.java
* 作 者 : wader
* 创建时间 : 2011-8-19上午11:48:05
* 文件描述 : 按钮跑马灯及自动加省略号控件
* 修改历史 : 2011-8-19上午11:48:05
******************************************************************/
public class MyButton extends Button implements Runnable {
/**
* 类标志
*/
static String TAG = MyButton.class.getSimpleName();
/**
* 文本长度
*/
private float textWidth = 0f;
/**
* 可见宽度
*/
private float viewWidth = 0f;
/**
* 文字的横坐标
*/
private float x = 0f;
/**
* 文字的纵坐标
*/
private float y = 0f;
/**
* 用于计算的临时变量(组件宽度+文字宽度)
*/
private float viewWidthTextWidth = 0.0f;
/**
* 用于计算的临时变量(组件宽度+2*文字宽度)
*/
private float viewWidthDblTextWidth = 0.0f;
/**
* 是否已经开始滚动
*/
private boolean isScroll = false;
/**
* 是否重新滚动
*/
private boolean isRestart = false;
/**
* 是否采用省略号显示
*/
private boolean isEllipsis = false;
/**
* 绘图样式
*/
private Paint paint = null;
/**
* 文本内容
*/
private String text;
/**
* 经过省略的字符串
*/
private String ellipsisStr;
/**
* 刷新频率 (默认300毫秒)
*/
private int frequency = 300;
/**
* 滚动延时 (默认200毫秒)
*/
private int delay = 200;
/**
* 控制文字滚动的线程
*/
private Thread marqueeThread;
/**
* 判断是否需要绘制,用于跑马灯文本恒速变动的变量
*/
public boolean isNeedRefresh;
private float offsetX = 0;
/**
* 构造函数
*
* @param context
* context对象
*/
public MyButton(Context context) {
super(context);
}
/**
* 构造函数
*
* @param context
* context对象
* @param attrs
* AttributeSet对象
*/
public MyButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* 构造函数
*
* @param context
* context对象
* @param attrs
* AttributeSet对象
* @param defStyle
* 风格定义
*/
public MyButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* 文本初始化,可指定文字起始显示横坐标。每次更改文本内容或者文本效果等之后都需要重新初始化一下
*
* @param text
* 显示文本
* @param viewWidth
* 可视宽度
*/
public void init(String text, float viewWidth, float offsetX) {
// 判空操作
if (text == null || text.length() <= 0 || viewWidth <= 0) {
return;
}
// 获得paint对象
paint = getPaint();
// 变量初始化
this.text = text;
this.textWidth = paint.measureText(text);
this.viewWidth = viewWidth;
LayoutParams lp = this.getLayoutParams();
lp.width = (int) viewWidth + 1;
((View) this.getParent()).postInvalidate();
this.x = textWidth;
this.viewWidthTextWidth = viewWidth + textWidth;
this.viewWidthDblTextWidth = viewWidth + textWidth * 2;
this.y = getTextSize() + getPaddingTop();
this.offsetX = offsetX;
Log.e("MarqueeTextView", "viewWidth=" + viewWidth);
Log.e("MarqueeTextView", "textWidth=" + textWidth);
// 参数设置
paint.setColor(getTextColors().getDefaultColor());
this.setWidth((int) viewWidth);
}
/**
* 文本初始化,可自动添加省略号。每次更改文本内容或者文本效果等之后都需要重新初始化一下
*
* @param text
* 显示文本
* @param viewWidth
* 可视宽度
* @param isEllipsis
* 是否需要使用省略号
*/
public void init(String text, float viewWidth, boolean isEllipsis) {
// 初始化文本
init(text, viewWidth, 5);
// 是否采用省略号
this.isEllipsis = isEllipsis;
// 如果采用省略显示
if (isEllipsis && text != null && text.length() > 0) {
ellipsisStr = text;
if (textWidth > viewWidth) {
float tempWidth = 0f;
int strEnd = text.length();
String plusStr = "...";
// 计算文本以适合可视宽度
do {
tempWidth = paint.measureText(ellipsisStr.substring(0,
strEnd--) + plusStr);
} while (tempWidth > viewWidth && strEnd > 0);
// 裁剪标题
ellipsisStr = ellipsisStr.substring(0, strEnd) + plusStr;
}
}
}
/**
* 文本初始化,可于startScroll()方法一起使用实现跑马灯效果。每次更改文本内容或者文本效果等之后都需要重新初始化一下
*
* @param text
* 显示文本
* @param viewWidth
* 可视宽度
* @param isEllipsis
* 是否需要使用省略号
* @param frequency
* 刷新频率
* @param delay
* 滚动延时
*/
public void init(String text, float viewWidth, boolean isEllipsis,
int frequency, int delay) {
init(text, viewWidth, isEllipsis);
if (frequency >= 0 && delay >= 0) {
this.frequency = frequency;
this.delay = delay;
}
}
/**
* 开始滚动
*/
public void startScroll() {
if (!isNeedScroll()) {//判断是否需要滚动
return;
}
this.isScroll = true;
if (marqueeThread != null) {
marqueeThread.interrupt();
}
marqueeThread = new Thread(this);
marqueeThread.start();
}
/**
* 停止滚动
*/
public void stopScroll() {
this.isScroll = false;
if (marqueeThread != null) {
marqueeThread.interrupt();
x = textWidth;
}
}
/**
* 是否需要滚动
*/
public boolean isNeedScroll() {
return textWidth > viewWidth;
}
/**
* 重新滚动
*/
public void restart() {
isRestart = true;
}
/**
* 设置当前步进大小
*/
public void setScrollX(float x) {
this.x = x;
}
/**
* 重写函数,文本框的绘制
*
* @param canvas
* 画布对象
*/
@Override
public void onDraw(Canvas canvas) {
// 判空操作
if (text == null || paint == null) {
return;
}
// 如果开始滚动,并且文本宽度大于可视宽度
if (isScroll && textWidth > viewWidth) {
// 通过改变文字坐标实现滚动效果
canvas.drawText(text, viewWidthTextWidth - x - viewWidth, y, paint);
if (isNeedRefresh) {
// 滚动距离
x += 4;
}
// 如果超出可视区域则重置位置
if (x + viewWidth > viewWidthDblTextWidth || isRestart) {
x = textWidth - viewWidth;
isRestart = false;
}
} else {
if (isEllipsis) {//使用省略号显示
// canvas.drawText(ellipsisStr, ((viewWidth - textWidth)/2) -
// 10, y, paint);
canvas.drawText(ellipsisStr, 5, y, paint);
} else {// 不使用省略号显示也不实现滚动效果
// int i_x = (int)(viewWidth - textWidth)>>1;
canvas.drawText(text, offsetX, y, paint);
}
}
isNeedRefresh = false;
}
/**
* 线程run方法
*/
public void run() {
Thread currentThread = Thread.currentThread();
try {
while (currentThread == marqueeThread && isScroll && text != null
&& this.isNeedScroll()) {
// 判断是否需要延时跑动
if (delay > 0) {
synchronized (this) {
// 延时
wait(delay);
// 清零
delay = 0;
}
}
isNeedRefresh = true;
// 刷新文本
this.postInvalidate();
Thread.sleep(frequency);
// Log.w(TAG + "line 124", "marquee running "
// + marqueeThread.getName());
}
} catch (InterruptedException ex) {
} finally {
}
// Log.w(TAG + "line 124", "marquee running " + marqueeThread.getName()
// + "dead");
}
}
第一个功能较简单不多说什么。
第二个功能的实现在于一个自动去掉无法显示部分的算法,即下面方法中的do-while循环
public void init(String text, float viewWidth, boolean isEllipsis) {
// 初始化文本
init(text, viewWidth, 5);
// 是否采用省略号
this.isEllipsis = isEllipsis;
// 如果采用省略显示
if (isEllipsis && text != null && text.length() > 0) {
ellipsisStr = text;
if (textWidth > viewWidth) {
float tempWidth = 0f;
int strEnd = text.length();
String plusStr = "...";
// 计算文本以适合可视宽度
do {
tempWidth = paint.measureText(ellipsisStr.substring(0,
strEnd--) + plusStr);
} while (tempWidth > viewWidth && strEnd > 0);
// 裁剪文字
ellipsisStr = ellipsisStr.substring(0, strEnd) + plusStr;
}
}
}
第三个功能是通过实现Runnable实现run()方法,通过不断地postInvalidate()方法使onDraw()不断改变文字坐标来实现的。
注:如果需要使用的是TextView而不是Button,只需将代码中继承的父类改为TextView即可。