1. 程式人生 > >TextView跑馬燈-兩種實現方式

TextView跑馬燈-兩種實現方式

Android中TextView跑馬燈有多種實現方式;
一種是自定義控制元件,另外一種是寫一個工具類

自定義控制元件

/**
 * Created by iblade.Wang on 2018/10/24 10:29
 */
public class MarqueeTextView extends TextView  {
    public MarqueeTextView(Context context) {
        this(context, null);
    }

    public MarqueeTextView(Context context, @Nullable AttributeSet attrs)
{ this(context, attrs, 0); } public MarqueeTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { //預設迴圈一次 setRepeatCount(1); } /** * 強制獲取焦點 * * @return */
@Override public boolean isFocused() { return true; } /** * 用於EditText搶注焦點的問題 */ @Override protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { if (focused) { super.onFocusChanged(focused, direction,
previouslyFocusedRect); } } /** * Window與Window間焦點發生改變時的回撥 * 解決Dialog搶了焦點後跑馬燈停止 */ @Override public void onWindowFocusChanged(boolean hasWindowFocus) { if (hasWindowFocus) { super.onWindowFocusChanged(hasWindowFocus); } } /** * 設定跑馬燈迴圈次數,其中-1表示無限迴圈 * * @param limit */ public void setRepeatCount(int limit) { //設定單行 setSingleLine(); //設定Ellipsize setEllipsize(TextUtils.TruncateAt.MARQUEE); //獲取焦點 setFocusable(true); //走馬燈的重複次數,-1表示無限迴圈 setMarqueeRepeatLimit(limit); //強制獲得焦點 setFocusableInTouchMode(true); } }

工具類


/**
 * Created by iblade.Wang on 2018/10/25 14:41
 */
public class TextViewUtils {
    /**
     * set TextView Ellipsize marquee
     *
     * @param textView
     */
    public static void marqueeView2(final TextView textView) {
        //mRndDuration用於控制速度
        final int mRndDuration = 200;
        textView.setSingleLine();
        textView.setEllipsize(null);
        //只有當textView完成繪製後才能計算滾動距離
        if (0 != textView.getWidth()) {
            scrollText2(textView, mRndDuration);
        } else {
            textView.getViewTreeObserver()
                    .addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
                        @Override
                        public void onGlobalLayout() {
                            if (0 != textView.getWidth()) {
                                textView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                                scrollText2(textView, mRndDuration);
                            }
                        }
                    });
        }
    }

    private static void scrollText2(final TextView textView, int mRndDuration) {
        Scroller slr = new Scroller(textView.getContext(), new LinearInterpolator());
        textView.setScroller(slr);
        //字型長度
        int scrollingLen = (int) (textView.getPaint().measureText(textView.getText().toString()) + 0.5f);
        //滾動時長
        int duration = textView.getText().length() * mRndDuration;
        int viewWidth = textView.getWidth();
        int distance = scrollingLen - viewWidth + textView.getLeft() + textView.getCompoundPaddingLeft() + textView.getCompoundPaddingRight();
        if (distance <= 0) {
            return;
        }
        slr.startScroll(0, 0, distance, 0, duration);
        textView.invalidate();

        final class HandlerListener implements HandlerUtils.OnReceiveMessageListener {
            @Override
            public void handlerMessage(Message msg) {
                int width = textView.getLayoutParams().width;
                //寬度不是自適應時,會有不左對齊的bug,暫時用這種辦法規避
                if (width != ViewGroup.LayoutParams.WRAP_CONTENT) {
                    textView.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
                }
                textView.setText(textView.getText());
                textView.getLayoutParams().width = width;
            }
        }
        HandlerListener handlerListener = new HandlerListener();
        HandlerUtils.HandlerHolder handlerHolder = new HandlerUtils.HandlerHolder(handlerListener);
        handlerHolder.sendEmptyMessageDelayed(0x1, duration + 500);
    }

//todo ---------------------以下是通過動畫完成,drawableLeft會有問題------------------------------------

    /**
     * set TextView Ellipsize marquee
     *
     * @param textView
     */
    public static void marqueeView(final TextView textView) {
        final int mRndDuration = 200;
        textView.setSingleLine();
        textView.setEllipsize(null);
        //只有當textView完成繪製後才能計算滾動距離
        if (0 != textView.getWidth()) {
            scrollText(textView, mRndDuration);
        } else {
            textView.getViewTreeObserver()
                    .addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
                        @Override
                        public void onGlobalLayout() {
                            if (0 != textView.getWidth()) {
                                textView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                                scrollText(textView, mRndDuration);
                            }

                        }
                    });
        }
    }

    private static void scrollText(final TextView textView, int mRndDuration) {
        //字型長度
        int scrollingLen = (int) (textView.getPaint().measureText(textView.getText().toString()) + 0.5f);
        //滾動時長
        int duration = textView.getText().length() * mRndDuration;
        int viewWidth = textView.getWidth();
        int distance = scrollingLen - viewWidth + textView.getLeft() + textView.getCompoundPaddingLeft() + textView.getCompoundPaddingRight();
        if (distance <= 0) {
            return;
        }
        ObjectAnimator animator = ObjectAnimator.ofFloat(textView, "translationX", 0f, -distance);
        animator.setDuration(duration);
        animator.start();
    }


//    /**
//     * calculate TextView singleLine widthPixels
//     *
//     * @param textView textView
//     * @return widthPixels
//     */
//    private static int calculateTextViewLen(TextView textView) {
//
//        TextPaint tp = textView.getPaint();
//        Rect rect = new Rect();
//        String strTxt = textView.getText().toString();
//        tp.getTextBounds(strTxt, 0, strTxt.length(), rect);
//        int scrollingLen = rect.width();
//        rect = null;
//        return scrollingLen;
//    }
//
//    private static int calculateTextViewLen2(TextView textView) {
//        return (int) (textView.getPaint().measureText(textView.getText().toString()) + 0.5f);
//    }
}

其中工具類中用到了Handler弱引用的工具類,《Android中Handler弱引用的工具類》
但是測試中發現弱引用,部分複雜頁面中又被GC 回收的風險,不建議使用,可以替代為如下:

 private static void scrollText2(final TextView textView, int mRndDuration) {
        Scroller slr = new Scroller(textView.getContext(), new LinearInterpolator());
        textView.setScroller(slr);
        //字型長度
        int scrollingLen = (int) (textView.getPaint().measureText(textView.getText().toString()) + 0.5f);
        //滾動時長
        int duration = textView.getText().length() * mRndDuration;
        int viewWidth = textView.getWidth();
        int distance = scrollingLen - viewWidth + textView.getLeft() + textView.getCompoundPaddingLeft() + textView.getCompoundPaddingRight();
        //只允許向左滾動
        if (distance <= 0) {
            return;
        }
        slr.startScroll(0, 0, distance, 0, duration);
        textView.invalidate();
        //有記憶體洩露隱患
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {

                int width = textView.getLayoutParams().width;
                //寬度不是自適應時,會有不左對齊的bug,暫時用這種辦法規避
                if (width != ViewGroup.LayoutParams.WRAP_CONTENT) {
                    textView.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
                }
                textView.setText(textView.getText());
                textView.getLayoutParams().width = width;
            }
        }, duration + 500);