一、背景
最近项目要实现一个置顶的子串滚动显示,类似广告条,需要悬浮在所有应用上面。
原本想直接使用Android TextView自带的Marquee效果,但是这个需要控件被选中才会执行跑马灯,而且要文本内容的宽度超出文本控件本身的宽度时才会执行,而项目需要的效果是不管文本内容有多少都要执行滚动效果,所以只能放弃。
最后改用自定义一个marqueetextview控件来实现自动跑马灯效果,参考了Android自定义TextView实现文字自动滚动_android textview自动滚动-CSDN博客的设计,不过原文章的设计还是要文本内容宽度超过控件宽度时才会触发滚动,所以在其基础上做了修改。
二、实现方案
整体思路:
1. 自定义一个AlwaysMarqueeTextView类继承TextView,添加一个定时器进行刷新文本的显示位置
2. 重写onDraw方法,根据文本的起始为mOffsetX进行重新绘制控件
3. 新加setMarqueeText(int resId)和setMarqueeText(CharSequence text)给外部调用,用于替换TextView的setText()方法
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.TextView;
import java.util.Timer;
import java.util.TimerTask;
public class AlwaysMarqueeTextView extends TextView {
private static final String TAG = "AlwaysMarqueeTextView";
private int mOffsetX = 0;
private Rect mRect;
private String mText = "您的设备已过期,请尽快续费!";
private Timer mTimer;
private TimerTask mTimerTask;
private int mTextWidth;
private int mSpeed = 10;
private static final int PFS = 24;
public AlwaysMarqueeTextView(Context context) {
this(context, null);
}
public AlwaysMarqueeTextView(Context context, AttributeSet attrs) {
super(context, attrs);
mRect = new Rect();
mTimer = new Timer();
mTimerTask = new MyTimerTask();
//更新帧率24
mTimer.schedule(mTimerTask, 0, 1000 / PFS);
}
public void setMarqueeText(CharSequence text) {
setText(text);
mText = text.toString();
TextPaint textPaint = getPaint();
textPaint.getTextBounds(mText, 0, mText.length(), mRect);
mTextWidth = mRect.width();
}
public void setMarqueeText(int resId) {
setText(resId);
mText = getText().toString();
TextPaint textPaint = getPaint();
textPaint.getTextBounds(mText, 0, mText.length(), mRect);
mTextWidth = mRect.width();
}
private class MyTimerTask extends TimerTask {
@Override
public void run() {
if (mOffsetX < - (mTextWidth * 2) - getPaddingEnd()){
mOffsetX = getWidth();
}
mOffsetX -= mSpeed;
postInvalidate();
}
}
@Override
protected void onDraw(Canvas canvas) {
mText = getText().toString();
TextPaint textPaint = getPaint();
textPaint.setColor(getCurrentTextColor());
//获取文本区域大小,保存在mRect中。
textPaint.getTextBounds(mText, 0, mText.length(), mRect);
mRect.right = getWidth();//将绘画区域的右边设置为textview的宽度值
float mTextCenterVerticalToBaseLine =
( - textPaint.ascent() + textPaint.descent()) / 2 - textPaint.descent();
canvas.drawText(mText, mOffsetX, getHeight() / 2 + mTextCenterVerticalToBaseLine, textPaint);
}
/**
* 视图移除时销毁任务和定时器
*/
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
Log.e(TAG, "killTimer");
if (mTimerTask != null){
mTimerTask.cancel();
mTimerTask = null;
}
if (mTimer != null){
mTimer.cancel();
mTimer = null;
}
}
public void setSpeed(int speed){
this.mSpeed = speed;
}
}
附带实现置顶自动滚动文本框的代码:
import android.content.Context;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.WindowManager;
import android.widget.TextView;
import com.xxx.example.R;
public class FloatMessageView {
private WindowManager windowManager;
private AlwaysMarqueeTextView txt_floatView;
private Context mContext;
public FloatMessageView(Context context) {
windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
mContext = context;
createFloatView();
}
public void createFloatView() {
int screenWidth = windowManager.getDefaultDisplay().getWidth();
txt_floatView = new AlwaysMarqueeTextView(mContext);
txt_floatView.setMarqueeText(R.string.message_device_outdated);
txt_floatView.setBackgroundColor(Color.TRANSPARENT);
txt_floatView.setTextColor(Color.RED);
txt_floatView.setTextSize(28);
txt_floatView.setSpeed(2);
txt_floatView.setMaxLines(1);
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
layoutParams.gravity = Gravity.CENTER | Gravity.TOP;
layoutParams.width = screenWidth / 2;
layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
layoutParams.y = 15;
layoutParams.format = PixelFormat.RGBA_8888;
txt_floatView.setLayoutParams(layoutParams);
windowManager.addView(txt_floatView, layoutParams);
}
public void updateFloatMessage(CharSequence msg, int color) {
txt_floatView.setTextColor(color);
txt_floatView.setMarqueeText(msg);
}
}