美文网首页Android基础问题
Android 跑马灯--谷歌“偷渡”法

Android 跑马灯--谷歌“偷渡”法

作者: 奈蜇 | 来源:发表于2018-06-09 15:24 被阅读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的这个特性去实现你自己的跑马灯。

    相关文章

      网友评论

        本文标题:Android 跑马灯--谷歌“偷渡”法

        本文链接:https://www.haomeiwen.com/subject/vvpteftx.html