Android 跑马灯--谷歌“偷渡”法
2018-06-09 本文已影响0人
奈蜇
遇到问题
一个跑马灯的需求,在网上扒了几个,发现都是写一个线程去循环画需要显示的字。效果来说不是很理想。
偶然下我发现了TextView自己就带了跑马灯的属性,只是谷歌为了方便文本内容超过了TextView的范围实现的。这个效果还是很不错的。
一般情况给TextView设置了 setEllipsize(TextUtils.TruncateAt.MARQUEE);是不跑的。会跑动的条件第一是TextView要显示的内容超出TextView的显示范围,第二是TextView获得了焦点。
实现
根据这个两个特性我们可以自己根据TextView改造出一个跑马灯来
我就直接贴代码了:
import android.content.Context;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.support.v7.widget.AppCompatTextView;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.ViewTreeObserver;
@SuppressWarnings("unused")
public class MarqueeView extends AppCompatTextView {
private volatile int spaceCount = 0;
private String layout_width;
public MarqueeView(Context context) {
super(context);
}
public MarqueeView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
if (attrs != null) {
//直接获取XML中设置的宽度
layout_width = attrs.getAttributeValue("http://schemas.android.com/apk/res/android", "layout_width");
switch (layout_width) {
case "-2"://宽度设置为wrap_content的情况。这个时候宽度是文本内容的宽度决定的。
setWidthByText(getText().toString(),getTextSize());
initView();
break;
default:
getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {//系统确认了宽度和高度时回调的
if(getViewTreeObserver().isAlive()){
getViewTreeObserver().removeOnGlobalLayoutListener(this);
int viewWidth= getMeasuredWidth();
float spaceWidth = getCharacterWidth(getTextSize());
spaceCount = viewWidth / (int)spaceWidth;
initView();
}
}
});
break;
}
}
}
public MarqueeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private void initView() {
setSelected(true);
setSingleLine(true);
setEllipsize(TextUtils.TruncateAt.MARQUEE);
setMarqueeRepeatLimit(-1);
}
@Override
public boolean isFocused() {
//强制保持TextView是一直存在焦点的,而实际是没有获得焦点的。(俗称骗自己有焦点)
return true;
}
@Override
public void setFocusable(boolean focusable) {
super.setFocusable(false);
}
@Override
public void setFocusable(int focusable) {
super.setFocusable(false);
}
@Override
public void setFocusableInTouchMode(boolean focusableInTouchMode) {
super.setFocusableInTouchMode(false);
}
@Override
public void setText(CharSequence text, BufferType type) {
if(layout_width != null && layout_width.equals("-2")){
setWidthByText(text.toString(),getTextSize());
}
if(spaceCount != 0) {
text = getCount(spaceCount - 2 ) + text;
}
super.setText(text, type);
}
@Override
public CharSequence getText() {
return super.getText().toString().trim();
}
public void setWidthByText(final String string,final float textSize){
setWidth((int)getCharacterWidth(string, textSize));
float spaceWidth = getCharacterWidth(textSize);
spaceCount = (int) (getCharacterWidth(string, textSize)/spaceWidth);
}
public float getCharacterWidth(final float size) {
Paint paint = new Paint();
paint.setTextSize(size);
return paint.measureText(" ");
}
public float getCharacterWidth(String string ,float size) {
Paint paint = new Paint();
paint.setTextSize(size);
return paint.measureText(string);
}
public String getCount(int count) {
if (count < 0) return "";
StringBuilder st = new StringBuilder();
for (int i = 0; i < count; i++) {
st.append(" ");
}
return st.toString();
}
}
重点是利用空格去填充TextView,让原本要显示的内容隐藏。而需要多少个空格可以通过
Paint paint = new Paint();
paint.setTextSize(size);
paint.measureText(" ");//返回这个文本的宽度单位是px.
而调用getText()的时候只需要复写一下方法,通过trim()去掉两端的空格。
super.getText().toString().trim();
实现的效果:
效果图,GIF的帧率太低了,所以感觉很卡顿。
好了,基本需求解决了,这个只是一个demo,你们可以根据你的实际需求利用好TextView的这个特性去实现你自己的跑马灯。