Android實現(xiàn)跑馬燈效果

實現(xiàn)方式1

    <TextView
        android:id="@+id/tvTip"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#FFFDE9EB"
        android:ellipsize="marquee"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:marqueeRepeatLimit="marquee_forever"
        android:paddingLeft="10dp"
        android:paddingTop="12dp"
        android:paddingRight="10dp"
        android:paddingBottom="12dp"
        android:scrollHorizontally="true"
        android:singleLine="true"
        android:text="物流公告:受疫情影響,2020-08-20起,深圳地區(qū)暫時不支持發(fā)貨"
        android:textColor="#FFEB2C3E"
        android:textSize="15sp">

    </TextView>

    //設(shè)置跑馬燈效果芥炭,避免跑馬燈效果失效
    private void setMarqueeText() {
        mTvTip.setEllipsize(TextUtils.TruncateAt.MARQUEE);
        mTvTip.setSingleLine(true);
        mTvTip.setSelected(true);
        mTvTip.setFocusable(true);
        mTvTip.setFocusableInTouchMode(true);
    }

跑馬燈相關(guān)屬性

android:singleLine="true"  //設(shè)置單行
android:scrollHorizontally="true"
android:ellipsize="marquee"  //跑馬燈
android:focusable="true"  //獲得焦點
android:focusableInTouchMode="true"
android:marqueeRepeatLimit="marquee_forever"  //無限循環(huán)

實現(xiàn)方式2:自定義跑馬燈類

上面方式1能暫時實現(xiàn)跑馬燈效果晴股,但在多次點擊事件之后容易失焦。而且在Android4.4上實現(xiàn)有短暫停頓踊赠。

MarqueeTextView

public class MarqueeTextView extends AppCompatTextView {

    /**
     * 滾動次數(shù)
     */
    private int marqueeNum = -1;//-1為永久循環(huán)呵扛,大于0是循環(huán)次數(shù)。

    public void setMarqueeNum(int marqueeNum) {
        this.marqueeNum = marqueeNum;
    }

    public MarqueeTextView(Context context) {
        super(context);
        setAttr();
    }

    public MarqueeTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setAttr();
    }

    public MarqueeTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setAttr();
    }

    /**
     * 始終獲取焦點
     * 跑馬燈在TextView處于焦點狀態(tài)的時候才會滾動
     */
    @Override
    public boolean isFocused() {
        return true;
    }

    /**
     * 設(shè)置相關(guān)屬性
     */
    private void setAttr() {
        this.setEllipsize(TextUtils.TruncateAt.MARQUEE);//設(shè)置跑馬等效果
        this.setMarqueeRepeatLimit(marqueeNum);//設(shè)置跑馬燈重復(fù)次數(shù)
        this.setSingleLine(true);//設(shè)置單行
    }
}
    <com.zly.demo10.MarqueeTextView2
        android:id="@+id/tv_person_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:background="#FFFDE9EB"
        android:paddingLeft="10dp"
        android:paddingTop="12dp"
        android:paddingRight="10dp"
        android:paddingBottom="12dp"
        android:text="物流公告:受疫情影響筐带,2020-08-20起今穿,深圳地區(qū)暫時不支持發(fā)貨"
        android:textColor="#FFEB2C3E"
        android:textSize="15sp" />

實現(xiàn)方式3:自定義跑馬燈類(自定義繪制控件)

MarqueeTextView

/**
 * 自定義跑馬燈
 */

public class MarqueeTextView extends View {
    /**
     * 界面刷新時間(ms)
     */
    public static final int INVALIDATE_TIME = 12;
    /**
     * 每次移動的像素點(px)
     */
    public static final int INVALIDATE_STEP = 2;
    /**
     * 是否第一次
     */
    //private boolean isfirstCycle = true;

    private String drawingText;
    private TextPaint paint;
    public boolean exitFlag;
    private float textWidth;
    private int posX = 0;
    private float posY;
    private int width;
    private RectF rf;
    private boolean hasInit;

    private Handler mHandler = new Handler();

    public MarqueeTextView(Context context) {
        this(context, null);
    }

    public MarqueeTextView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MarqueeTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initView(context, attrs, defStyle);
    }

    private void initView(Context context, AttributeSet attrs, int defStyle) {
        paint = new TextPaint();
        rf = new RectF(0, 0, 0, 0);
        paint.setAntiAlias(true);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MarqueeTextView);
        setTextSize(typedArray.getDimension(R.styleable.MarqueeTextView_mtvtextsize, 30f));
        setTextColor(typedArray.getColor(R.styleable.MarqueeTextView_mtvtextcolor, Color.WHITE));
        setText(typedArray.getString(R.styleable.MarqueeTextView_mtvtext));

    }

    public void setText(String text) {
        this.drawingText = text;
        posX = 0;
    }

    public void setTextSize(float dp) {
        if (this.paint == null)
            return;
        this.paint.setTextSize(dp);
    }

    public void setTextColor(int color) {
        if (this.paint == null)
            return;
        this.paint.setColor(color);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        width = MeasureSpec.getSize(widthMeasureSpec);

        if (drawingText != null) {
            textWidth = paint.measureText(drawingText, 0, drawingText.length());
        }
        if (posX == 0 && !hasInit) {
            rf.right = 0;
            rf.bottom = MeasureSpec.getSize(heightMeasureSpec);
            posX = 0;
            posY = getTextDrawingBaseline(paint, rf);
            hasInit = true;
        }
        setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        if (getVisibility() != View.VISIBLE || TextUtils.isEmpty(drawingText)) {
            return;
        }
        canvas.save();
        canvas.drawText(drawingText, 0, drawingText.length(), posX, posY, paint);
        canvas.restore();
    }

    private Runnable moveRun = new Runnable() {

        @Override
        public void run() {
            //控制文本寬度大于控件寬度才進(jìn)行滾動
            if (width >= textWidth) {
                posX = 0;
                invalidate();
                return;
            }
            //左移
            posX -= INVALIDATE_STEP;
            //當(dāng)文字和空格完全移出屏幕,x值從1開始移動
            if (posX < -1 * textWidth - paint.measureText("", 0, "".length())) {
                posX = getWidth();
            }
            invalidate();
            if (!exitFlag) {
                mHandler.postDelayed(this, INVALIDATE_TIME);
                return;
            }
            posX = 0;

        }
    };

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        stopMove();
    }

    @Override
    protected void onWindowVisibilityChanged(int visibility) {
        super.onWindowVisibilityChanged(visibility);
        if (visibility == View.VISIBLE) {
            startMove();
        } else {
            stopMove();
        }
    }

    @Override
    public void onWindowFocusChanged(boolean hasWindowFocus) {
        super.onWindowFocusChanged(hasWindowFocus);
        if (hasWindowFocus) {
            startMove();
        } else {
            stopMove();
        }

    }


    private void stopMove() {
        exitFlag = true;
        if (mHandler == null)
            return;
        mHandler.removeCallbacksAndMessages(null);
    }

    public void startMove() {
        exitFlag = false;
        if (mHandler == null)
            return;
        mHandler.removeCallbacksAndMessages(null);
        mHandler.postDelayed(moveRun, 0);
    }

    public void startMove(long delay) {
        exitFlag = false;
        if (mHandler == null)
            return;
        mHandler.removeCallbacksAndMessages(null);
        mHandler.postDelayed(moveRun, delay);
    }

    /**
     * 獲取繪制文字的baseline
     *
     * @param paint
     * @param targetRect
     * @return
     */
    public static float getTextDrawingBaseline(Paint paint, RectF targetRect) {
        if (paint == null || targetRect == null) {
            return 0;
        }
        Paint.FontMetrics fontMetric = paint.getFontMetrics();
        return targetRect.top + (targetRect.height() - fontMetric.bottom + fontMetric.top) / 2.0f - fontMetric.top;
    }
}

attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MarqueeTextView">
        <attr name="mtvtextsize" format="dimension"/>
        <attr name="mtvtextcolor" format="color"/>
        <attr name="mtvtext" format="string"/>
    </declare-styleable>
</resources>

使用

    <com.zly.demo10.MarqueeTextView
        android:id="@+id/tvTip3"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_marginTop="16dp"
        android:background="#FFFDE9EB"
        app:mtvtext="物流公告:受疫情影響伦籍,2020-08-20起蓝晒,深圳地區(qū)暫時不支持發(fā)貨"
        app:mtvtextcolor="#FFEB2C3E"
        app:mtvtextsize="15sp"
        tools:text="物流公告:受疫情影響腮出,2020-08-20起,深圳地區(qū)暫時不支持發(fā)貨" />
        mTvTip3.setText("倉為品牌特賣倉儲中心芝薇,庫存較少利诺,請盡a電視劇花短時間撒分散打發(fā)第三方大師傅大師傅奧德賽飛a");
        mTvTip3.setTextColor(Color.BLACK);
        mTvTip3.startMove(1000);

第三方跑馬燈庫

MarqueeView:可垂直跑、可水平跑的跑馬燈剩燥。

MarqueeViewLibrary:一個很方便使用和擴(kuò)展的跑馬燈Library慢逾,通過提供不同的MarqueeFactory來定制不同的跑馬燈View, 并且提供了常用類型的跑馬燈效果:SimpleMarqueeView灭红。

第三方跑馬燈庫MarqueeView的使用

 implementation 'com.sunfusheng:MarqueeView:1.4.1'
image.png

XML

<com.sunfusheng.marqueeview.MarqueeView
    android:id="@+id/marqueeView"
    android:layout_width="match_parent"
    android:layout_height="30dp"
    app:mvAnimDuration="1000"
    app:mvDirection="bottom_to_top"
    app:mvInterval="3000"
    app:mvTextColor="@color/white"
    app:mvTextSize="14sp"
    app:mvSingleLine="true"
    app:mvFont="@font/huawenxinwei"/>

設(shè)置字符串列表數(shù)據(jù)侣滩,或者設(shè)置自定義的Model數(shù)據(jù)類型

MarqueeView marqueeView = (MarqueeView) findViewById(R.id.marqueeView);

List<String> messages = new ArrayList<>();
messages.add("1. 大家好,我是孫福生变擒。");
messages.add("2. 歡迎大家關(guān)注我哦君珠!");
messages.add("3. GitHub帳號:sunfusheng");
messages.add("4. 新浪微博:孫福生微博");
messages.add("5. 個人博客:sunfusheng.com");
messages.add("6. 微信公眾號:孫福生");
marqueeView.startWithList(messages);

// 或者設(shè)置自定義的Model數(shù)據(jù)類型
public class CustomModel implements IMarqueeItem {
    @Override
    public CharSequence marqueeMessage() {
        return "...";
    }
}

List<CustomModel> messages = new ArrayList<>();
marqueeView.startWithList(messages);

// 在代碼里設(shè)置自己的動畫
marqueeView.startWithList(messages, R.anim.anim_bottom_in, R.anim.anim_top_out);

設(shè)置字符串?dāng)?shù)據(jù)

String message = "心中有陽光,腳底有力量娇斑!心中有陽光策添,腳底有力量!心中有陽光毫缆,腳底有力量唯竹!";
marqueeView.startWithText(message);

// 在代碼里設(shè)置自己的動畫
marqueeView.startWithText(message, R.anim.anim_bottom_in, R.anim.anim_top_out);

設(shè)置事件監(jiān)聽

marqueeView.setOnItemClickListener(new MarqueeView.OnItemClickListener() {
    @Override
    public void onItemClick(int position, TextView textView) {
        Toast.makeText(getApplicationContext(), String.valueOf(marqueeView1.getPosition()) + ". " + textView.getText(), Toast.LENGTH_SHORT).show();
    }
});

在 Activity 或 Fragment 中

@Override
public void onStart() {
    super.onStart();
    marqueeView.startFlipping();
}

@Override
public void onStop() {
    super.onStop();
    marqueeView.stopFlipping();
}

在 ListView 或 RecyclerView 的 Adapter 中

@Override
public void onViewDetachedFromWindow(@NonNull ViewHolder holder) {
    super.onViewDetachedFromWindow(holder);
    holder.marqueeView.stopFlipping();
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市苦丁,隨后出現(xiàn)的幾起案子浸颓,更是在濱河造成了極大的恐慌,老刑警劉巖旺拉,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件产上,死亡現(xiàn)場離奇詭異,居然都是意外死亡蛾狗,警方通過查閱死者的電腦和手機(jī)晋涣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來沉桌,“玉大人谢鹊,你說我怎么就攤上這事∑涯粒” “怎么了撇贺?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長冰抢。 經(jīng)常有香客問我松嘶,道長,這世上最難降的妖魔是什么挎扰? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任翠订,我火速辦了婚禮巢音,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘尽超。我一直安慰自己官撼,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布似谁。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪菠净。 梳的紋絲不亂的頭發(fā)上派近,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天战坤,我揣著相機(jī)與錄音碟嘴,去河邊找鬼。 笑死雀瓢,一個胖子當(dāng)著我的面吹牛司浪,可吹牛的內(nèi)容都是我干的吁伺。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼间校,長吁一口氣:“原來是場噩夢啊……” “哼胁附!你這毒婦竟也來了揭绑?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤悼沈,失蹤者是張志新(化名)和其女友劉穎贱迟,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體絮供,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡衣吠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了壤靶。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缚俏。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出袍榆,到底是詐尸還是另有隱情胀屿,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布包雀,位于F島的核電站宿崭,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏才写。R本人自食惡果不足惜葡兑,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望赞草。 院中可真熱鬧讹堤,春花似錦、人聲如沸厨疙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽沾凄。三九已至梗醇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間撒蟀,已是汗流浹背叙谨。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留保屯,地道東北人手负。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像姑尺,于是被迫代替她去往敵國和親竟终。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內(nèi)容