最近要做一个跑马灯功能,要求滚动效果展示不同的内容,并且支持对内容进行增删。
GitHub 中已经有一些比较优秀的库,他们是基于ViewFlipper 实现的,可以设置动画效果和动画时长等
MarqueeView
MarqueeViewLibrary
但是当设置singleLine = true 的时候,超过屏幕的数据就自动为省略号或者不显示了
这样就满足不了我的需求,当然我需要的需求也简单,仅一个TextView 就能满足。
所以我直接自定义TextView 实现效果。
废话不多说,直接上代码
public class MarqueeView extends TextView implements View.OnClickListener {
public final static String TAG=MyText.class.getSimpleName();
private float textLength = 0f;// 文本长度
private float viewWidth = 0f;
private float step = 0f;// 文字的横坐标
private float y = 0f;// 文字的纵坐标
private float temp_view_plus_text_length = 0.0f;// 用于计算的临时变量
private float temp_view_plus_two_text_length = 0.0f;// 用于计算的临时变量
public boolean isStarting = false;// 是否开始滚动
private Paint paint = null;// 绘图样式
private String text = "";// 文本内容
private float currentScrollX;// 当前滚动的位置
private float lastIndex = 0f;//上一次滚动的位置
private List<String> strings = new ArrayList<>();
private int marqueeIndex = 0;
private WindowManager windowManager;
public void setWindowManager(WindowManager windowManager) {
this.windowManager = windowManager;
}
//更新list后,从第1条重新开始
public void setStrings(List<String> strings) {
this.strings = strings;
marqueeIndex = 0;
}
public MarqueeView(Context context) {
super(context);
initView();
}
public MarqueeView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public MarqueeView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView();
}
/**
* 初始化控件
*/
private void initView() {
setOnClickListener(this);
}
public void setTextContent(String text){
setText(text);
init();
}
/**
* 文本初始化,每次更改文本内容或者文本效果等之后都需要重新初始化一下
*/
public void init() {
try {
paint = getPaint();
paint.setColor(getTextColors().getDefaultColor());
text = getText().toString();
textLength = paint.measureText(text);
viewWidth = getWidth();
if (viewWidth == 0) {
if (windowManager != null) {
Display display = windowManager.getDefaultDisplay();
viewWidth = display.getWidth();
}
}
step = textLength;
temp_view_plus_text_length = viewWidth + textLength;
temp_view_plus_two_text_length = viewWidth + textLength * 2;
y = getTextSize() + getPaddingTop();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 开始滚动
*/
public void startScroll() {
try {
isStarting = true;
setViewText();
invalidate();
} catch (Exception e) {
e.printStackTrace();
}
}
private void setViewText() {
if (marqueeIndex == strings.size()){
marqueeIndex = 0;
}
setTextContent(strings.get(marqueeIndex));
marqueeIndex++;
}
/**
* 停止滚动
*/
public void stopScroll() {
try {
isStarting = false;
invalidate();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onDraw(Canvas canvas) {
try {
lastIndex = currentScrollX;
canvas.drawText(text, temp_view_plus_text_length - step, y, paint);
currentScrollX = temp_view_plus_text_length - step;
//重点,通过滚动的坐标,来判断一条数据是否完全消失,
//如果只有单条数据,可直接注释这两行代码
Log.d(TAG,"===lastIndex==="+lastIndex +"=currentScrollX==" + currentScrollX);
if (lastIndex < 0 && currentScrollX > 0){
setViewText();
}
if (!isStarting) {
return;
}
step += 5;// 速度
if (step > temp_view_plus_two_text_length) {
step = textLength;
}
invalidate();
} catch (Exception e) {
e.printStackTrace();
}
}
// 控制
@Override
public void onClick(View v) {
try {
if (isStarting) {
stopScroll();
}else {
startScroll();
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public boolean isFocused() {
return true;
}
@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
if (focused) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
}
}
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
if (hasWindowFocus)
super.onWindowFocusChanged(hasWindowFocus);
}
}