这个方案适用于 API 28 及之前,当他们引入
非 SDK 接口限制 后就不再适用了。
我遇到了同样的问题,但在网上找不到解决方案。通过查看TextView的源代码并参考其他提供控制滚动速度选项的答案,我设法自己解决了这个问题。
查看源代码,它将间隙或空白设置为您的TextView大小的1/3。因此,如果您的TextView宽度为600像素,则会在消息的开始和结束之间创建200像素的间隙。此解决方案旨在使该值可配置。
棘手的部分是,您需要覆盖的逻辑都在超类的私有类、字段和方法中。这使得解决方案变得不那么琐碎。
将此类添加到您的项目中... CustomTextView.java
public class CustomTextView extends AppCompatTextView {
private static final float NEW_GAP = 0F;
Object marqueeObject;
Field mStatusField;
Field mGhostStartField;
Field mMaxScrollField;
Field mGhostOffsetField;
Field mFadeStopField;
Field mMaxFadeScrollField;
public CustomTextView(Context context) {
super(context);
setSelected(true);
}
public CustomTextView(Context context, AttributeSet attrs) {
super(context, attrs);
setSelected(true);
}
public CustomTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setSelected(true);
}
@Override
protected void onDraw(Canvas canvas) {
try {
initMarqueeObject();
if (didMarqueeRestart()) {
updateMarqueeFieldValues();
}
}
catch(Exception exception) {
exception.printStackTrace();
}
super.onDraw(canvas);
}
private void initMarqueeObject() throws Exception {
if (marqueeObject == null) {
Field marqueeField = getClass().getSuperclass().getSuperclass().getDeclaredField("mMarquee");
marqueeField.setAccessible(true);
marqueeObject = marqueeField.get(this);
initMarqueeFields();
}
}
private void initMarqueeFields() throws Exception {
mStatusField = marqueeObject.getClass().getDeclaredField("mStatus");
mStatusField.setAccessible(true);
mGhostStartField = marqueeObject.getClass().getDeclaredField("mGhostStart");
mGhostStartField.setAccessible(true);
mMaxScrollField = marqueeObject.getClass().getDeclaredField("mMaxScroll");
mMaxScrollField.setAccessible(true);
mGhostOffsetField = marqueeObject.getClass().getDeclaredField("mGhostOffset");
mGhostOffsetField.setAccessible(true);
mFadeStopField = marqueeObject.getClass().getDeclaredField("mFadeStop");
mFadeStopField.setAccessible(true);
mMaxFadeScrollField = marqueeObject.getClass().getDeclaredField("mMaxFadeScroll");
mMaxFadeScrollField.setAccessible(true);
}
private boolean didMarqueeRestart() throws Exception {
byte currentState = mStatusField.getByte(marqueeObject);
return currentState == 0x1;
}
private void updateMarqueeFieldValues() throws Exception {
float textWidth = getWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight();
float originalGap = textWidth / 3.0F;
float originalValueStartValue = mGhostStartField.getFloat(marqueeObject);
float lineWidth = originalValueStartValue + textWidth - originalGap;
float mGhostStart = lineWidth - textWidth + NEW_GAP;
mGhostStartField.setFloat(marqueeObject, mGhostStart);
mMaxScrollField.setFloat(marqueeObject, mGhostStart + textWidth);
mGhostOffsetField.setFloat(marqueeObject, lineWidth + NEW_GAP);
mFadeStopField.setFloat(marqueeObject, lineWidth);
mMaxFadeScrollField.setFloat(marqueeObject, mGhostStart + lineWidth + lineWidth);
}
}
现在您可以使用NEW_GAP
变量轻松调整额外的空白间距。在此示例中,它设置为0像素。将该常量设置为您希望使用的任何像素值。
注意:此解决方案是从AppCompatTextView
扩展的。如果您正在从TextView
扩展,则应取消注释initMarqueeObject()
方法中的相关代码。
然后,只需在XML中将旧TextView替换为这个新类即可...
<com.yourpackagename.CustomTextView
android:focusable="true"
android:focusableInTouchMode="true"
android:singleLine="true"
android:scrollHorizontally="true"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
在您的代码中不需要调用setSelected(true)
,因为这在新类内部已经完成。
这种方法可能不会永远有效,因为如果他们重命名某些变量或更改TextView
的逻辑,它将会失效。但是在我测试过的所有版本中,它都运行良好。
希望这可以帮到您!