一、跑马灯需要满足的条件
private void startMarquee() {
// Do not ellipsize EditText
if (getKeyListener() != null) return;
if (compressText(getWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight())) {
//宽度大于0,或者硬件加速
return;
}
if ((mMarquee == null || mMarquee.isStopped()) && (isFocused() || isSelected())
&& getLineCount() == 1 && canMarquee()) {
//获焦或者selected状态,由于focus相对于selected复杂,建议使用selected
//TextLayout行数必须为1
if (mMarqueeFadeMode == MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) {
mMarqueeFadeMode = MARQUEE_FADE_SWITCH_SHOW_FADE;
final Layout tmp = mLayout;
mLayout = mSavedMarqueeModeLayout;
mSavedMarqueeModeLayout = tmp;
setHorizontalFadingEdgeEnabled(true);
requestLayout();
invalidate();
}
if (mMarquee == null) mMarquee = new Marquee(this);
mMarquee.start(mMarqueeRepeatLimit);
}
}
二、代码
鉴于有博客使用focus驱动跑马灯,但这种调用可能产生焦点判断问题,非常不推荐。因此使用这里避免外部调用selected可以以很小代价实现跑马灯,如果非要使用setSelected,系统中有个setActivated和setEnable可以用来代替setSelected。
public class MarqueeTextView extends AppCompatTextView {
private static final String TAG = "MarqueeTextView";
private boolean isMarqueeEnable = false;
public MarqueeTextView(Context context) {
this(context, null);
}
public MarqueeTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MarqueeTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
/**
* TextView.canMarquee() == false 时是不会滚动的
* 一般原因是行数问题影响,导致宽度不合适,而android:lines是无效的
* focus 或者 selected状态才能跑马灯
*/
setMaxLines(1);
setSingleLine(true);
if (isMarqueeEnable) {
setMarqueeRepeatLimit(-1);
setEllipsize(TextUtils.TruncateAt.MARQUEE);
} else {
setMarqueeRepeatLimit(0);
setEllipsize(TextUtils.TruncateAt.END);
}
super.setSelected(isMarqueeEnable);
}
public void setMarqueeEnable(boolean enable) {
if (isMarqueeEnable != enable) {
isMarqueeEnable = enable;
if (enable) {
super.setSelected(true);
setMarqueeRepeatLimit(-1);
setEllipsize(TextUtils.TruncateAt.MARQUEE);
} else {
super.setSelected(false);
setMarqueeRepeatLimit(0);
setEllipsize(TextUtils.TruncateAt.END);
}
}
}
public boolean isMarqueeEnable() {
return isMarqueeEnable;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (!isMarqueeEnable) {
return;
}
if (getLineCount() > 1) {
Log.e(TAG, "the marquee will not work if TextLineCount > 1");
}
if (getMarqueeRepeatLimit() <= 0) {
Log.e(TAG, "the marquee may not work if MarqueeRepeatLimit != -1");
}
}
@Override
public void setSelected(boolean selected) {
//复写此方法,禁止外部调用,保证只有内部调用
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
}
三、宽度问题导致跑马灯跑不起来
经常能遇到layoutParams.width 为WRAP_CONTENT的情况,实际上这个原因并不是跑不起来的直接原因,因为wrap_content 本身导致宽度总是大于文本“宽度”,因此不要使用WRAP_CONTENT即可