Display projects often use text marquees, including horizontal and vertical directions. Common needs include controlling the playback speed. I found a lot of related effects on the Internet, but they were not satisfactory. I found a MarqueeView yesterday, which has relative functions. It fits the scene and is easy to configure. The usage method and problem points will be listed below, and a demo is attached at the end
First look at the effect:
1. Customize View (just copy and paste it into your own project)
public class MarqueeView extends TextView {
//滚动方向
//不滚动
public static final int SCROLL_NO = 1;
//从下往上
public static final int SCROLL_BT = 2;
//从右往左
public static final int SCROLL_RL = 3;
//垂直滚动需要的数据
private float lineSpace;
private float verticalSpeed = 0.1f;
private List<String> textList = new ArrayList<>();
private StringBuilder textBuilder = new StringBuilder();
//水平滚动需要的数据
private float horizontalSpeed = 2f;
private Rect rect;
private Paint paint;
//默认不滚动
private int scrollType;
//每次更滚动的距离
private float scrollStep = 0f;
public MarqueeView(Context context) {
this(context, null);
}
public MarqueeView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MarqueeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MarqueeView);
scrollType = array.getInt(R.styleable.MarqueeView_scrollType, SCROLL_NO);
int color = array.getColor(R.styleable.MarqueeView_textColor, 0x000000);
lineSpace = array.getInt(R.styleable.MarqueeView_lineSpace, 0);
array.recycle();
setSpeed(0, 1f);
setSpeed(1, 5f);
paint = getPaint();
paint.setColor(color);
rect = new Rect();
}
@Override
public void setTextColor(int color) {
super.setTextColor(color);
paint.setColor(color);
}
/**
* 设置滚动方向
*
* @param scrollType 滚动方向
*/
public void setScrollType(int scrollType) {
this.scrollType = scrollType;
invalidate();
}
/**
* 设置滚动速度
*
* @param type 滚动方向 0垂直 1水平
*/
public void setSpeed(int type, float speed) {
if (0 == type) {
verticalSpeed = speed;
} else {
horizontalSpeed = speed;
}
invalidate();
}
/**
* 设置行高
*
* @param lineSpace 行高
*/
public void setLineSpace(float lineSpace) {
this.lineSpace = lineSpace;
invalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
String text = getText().toString();
if (!TextUtils.isEmpty(text) && scrollType == SCROLL_BT) {
//由下往上滚动需要测量高度
setTextList(widthMeasureSpec, text);
}
}
/**
* 根据TextView宽度和字体大小,计算显示的行数。
*
* @param widthMeasureSpec 测量模式
* @param text 文本
*/
private void setTextList(int widthMeasureSpec, String text) {
textList.clear();
float width = MeasureSpec.getSize(widthMeasureSpec);
float length = 0;
for (int i = 0; i < text.length(); i++) {
if (length <= width) {
textBuilder.append(text.charAt(i));
length += paint.measureText(text.substring(i, i + 1));
if (i == text.length() - 1) {
if (length <= width) {
textList.add(textBuilder.toString());
} else {
if (textBuilder.toString().length() == 1) {
//每行最多显示一个字
textList.add(text.substring(text.length() - 1));
} else {
//去掉最后一个字,否则最后一个字显示不完整
textList.add(textBuilder.toString().substring(0, textBuilder.toString().length() - 1));
//最后一个字单独一行
textList.add(text.substring(text.length() - 1));
}
}
}
} else {
if (textBuilder.toString().length() == 1) {
//每行最多显示一个字
textList.add(textBuilder.toString());
textBuilder.delete(0, textBuilder.length());
i--;
length = 0;
} else {
//去掉最后一个字,否则最后一个字显示不完整
textList.add(textBuilder.toString().substring(0, textBuilder.toString().length() - 1));
textBuilder.delete(0, textBuilder.length() - 1);
i--;
length = paint.measureText(text.substring(i, i + 1));
}
}
}
//清空textBuilder
textBuilder.delete(0, textBuilder.length());
}
@Override
public void onDraw(Canvas canvas) {
String text = getText().toString();
if (TextUtils.isEmpty(text)) {
super.onDraw(canvas);
return;
}
switch (scrollType) {
case SCROLL_NO:
super.onDraw(canvas);
break;
case SCROLL_BT:
//从下往上滚动,首次不显示文字,后续从下往上显示
float textSize = paint.getTextSize();
for (int i = 0; i < textList.size(); i++) {
float currentY = getHeight() + (i + 1) * textSize - scrollStep;
if (i > 0) {
currentY = currentY + i * lineSpace;
}
if (textList.size() > 1) {
canvas.drawText(textList.get(i), 0, currentY, paint);
} else {
canvas.drawText(textList.get(i), getWidth() / 2 - paint.measureText(text) / 2, currentY, paint);
}
}
scrollStep = scrollStep + verticalSpeed;
if (scrollStep >= getHeight() + textList.size() * textSize + (textList.size() - 1) * lineSpace) {
scrollStep = 0;
}
invalidate();
break;
case SCROLL_RL:
//从右向左滚动,首次不显示文字,后续每次往左偏移speed像素
paint.getTextBounds(text, 0, text.length(), rect);
int textWidth = rect.width();
int viewWidth = getWidth();
float currentX = viewWidth - scrollStep;
canvas.drawText(text, currentX, getHeight() / 2 + (paint.getFontMetrics().descent - paint.getFontMetrics().ascent) / 2 - paint.getFontMetrics().descent, paint);
scrollStep = scrollStep + horizontalSpeed;
if (scrollStep >= viewWidth + textWidth) {
scrollStep = 0;
}
invalidate();
break;
default:
break;
}
}
}
attrs.xml (under the res/values directory)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MarqueeView">
<attr name="scrollType">
<enum name="no" value="1" />
<enum name="vertical" value="2" />
<enum name="horizontal" value="3" />
</attr>
<attr name="speedType">
<enum name="slow" value="4" />
<enum name="normal" value="5" />
<enum name="fast" value="6" />
<enum name="express" value="7" />
</attr>
<attr name="textColor" format="color" />
<!--行高,只对垂直滚动有效-->
<attr name="lineSpace" format="integer" />
</declare-styleable>
</resources>
Layout file (simple example, see demo for details)
<com.hjly.marqueetextdemo.MarqueeView
android:id="@+id/marquee_horizontal"
android:layout_width="match_parent"
android:layout_height="80dp"
android:background="#abc"
android:singleLine="true"
app:textColor="#333"
android:textSize="30sp" />
Highlights:
Text color, this control cannot use android:textColor="@color/white"
Instead, you need to use app:textColor="@color/white" to set the text color
Otherwise, there will be no color, and the result is that there will be no text. When I organized the demo before, the code was all written, and it had no effect as soon as I ran it. I found the original demo and compared it for a long time before I found out that it was a problem with this attribute.
MainActivity (simple example, see demo for details)
marquee_horizontal = findViewById(R.id.marquee_horizontal);
marquee_horizontal.setFocusable(true);
marquee_horizontal.setFocusableInTouchMode(true);
marquee_horizontal.setSpeed(1, (float) 5);
marquee_horizontal.setScrollType(SCROLL_RL);
marquee_horizontal.setText("水平跑马灯单行");
The important properties are to set the scrolling speed and scrolling direction
So far, the function has been realized