前言

Android 里 TextView 控件本身就带有跑马灯的效果,但会存在 EditText 和 Dialog 抢占焦距使跑马灯效果失效等问题。

1 TextView 实现跑马灯效果

用 TextView 实现跑马灯效果,只需关注5个属性:

  1. 单行显示
  2. 单行显示多余部分如何显示,即 ellipsize 属性的设置
  3. 获取焦点
  4. 跑马灯重复次数
  5. 强制的获得了焦点,让 View 响应所有的 touch 事件
<TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="@dimen/dp_10"
        android:background="@android:color/holo_green_light"
        android:singleLine="true"
        android:ellipsize="marquee"
        android:marqueeRepeatLimit="marquee_forever"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:text="Always believe that something wonderful is about to happen adsa"/>

效果图:

android跑马灯不跑 android 跑马灯_android跑马灯不跑


表面上看起来没什么问题,实际上存在很多问题

2 TextView 实现跑马灯之问题

2.1 问题一

页面上出现两个跑马灯效果时

<TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/holo_green_light"
        android:ellipsize="marquee"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:marqueeRepeatLimit="marquee_forever"
        android:padding="@dimen/dp_10"
        android:singleLine="true"
        android:text="Always believe that something wonderful is about to happen adsa" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/dp_10"
        android:background="@android:color/holo_green_light"
        android:ellipsize="marquee"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:marqueeRepeatLimit="marquee_forever"
        android:padding="@dimen/dp_10"
        android:singleLine="true"
        android:text="Always believe that something wonderful is about to happen adsa" />

效果图:

android跑马灯不跑 android 跑马灯_android_02


问题:会发现一个问题,只有第一个 TextView 会有走马灯的效果

原因:第一个 TextView 抢占了焦距

解决方法:自定义 TextView 来实现跑马灯

2.2 自定义 TextView

public class MarqueeTextView extends android.support.v7.widget.AppCompatTextView {
    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);
        // 设置单行
        setSingleLine();
//        setMaxLines(1);
        //设置 Ellipsize,setMaxLines(1) 和 setEllipsize 冲突
        setEllipsize(TextUtils.TruncateAt.MARQUEE);
        //获取焦距
        setFocusable(true);
        //走马灯的重复次数,-1代表无限重复
        setMarqueeRepeatLimit(-1);
        //强制获得焦点
        setFocusableInTouchMode(true);
    }

    /**
     * 使这个 View 永远获得焦距
     * @return true
     */
    @Override
    public boolean isFocused() {
        return true;
    }
}

布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".activity.MarqueeActivity">

    <com.example.rs.recyclerviewdemo.view.MarqueeTextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/holo_green_light"
        android:ellipsize="marquee"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:marqueeRepeatLimit="marquee_forever"
        android:padding="@dimen/dp_10"
        android:singleLine="true"
        android:text="Always believe that something wonderful is about to happen adsa" />

    <com.example.rs.recyclerviewdemo.view.MarqueeTextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/dp_10"
        android:background="@android:color/holo_green_light"
        android:ellipsize="marquee"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:marqueeRepeatLimit="marquee_forever"
        android:padding="@dimen/dp_10"
        android:singleLine="true"
        android:text="Always believe that something wonderful is about to happen adsa" />
</LinearLayout>

效果图:

android跑马灯不跑 android 跑马灯_android跑马灯不跑_03

2.3 问题二

此时如果在布局上加一个 EditText 控件,会是什么效果呢?

android跑马灯不跑 android 跑马灯_android_04


问题:加了 EditText 控件后,当 EditText 获得焦距后,第一个跑马灯停止了

原因:因为 EditText 抢占了焦距

解决方法:在自定义 TextView 中,重写父类的 onFocusChanged 方法

/**
     * 用于 EditText 存在时抢占焦点
     */
    @Override
    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
        if(focused){
            super.onFocusChanged(focused, direction, previouslyFocusedRect);
        }
    }

隐形问题:
当弹出软键盘时(即 EditText 获得焦距时),跑马灯会重头开始播放;当关闭软键盘时(即 EditText 失去焦距时),跑马灯也会重头开始播放
尚未解决,哪位大神指点下???

2.3 问题三

如果桌面上弹出一个对话框,会是什么效果?

android跑马灯不跑 android 跑马灯_sed_05


问题:当弹出对话框时,跑马灯效果停止了

原因:Dialog 抢占了焦距

解决方法:在自定义 TextView 中,重写父类的 onWindowFocusChanged 方法

/**
     * Window与Window间焦点发生改变时的回调
     * 解决 Dialog 抢占焦点问题
     * @param hasWindowFocus
     */
    @Override
    public void onWindowFocusChanged(boolean hasWindowFocus) {
        if(hasWindowFocus){
            super.onWindowFocusChanged(hasWindowFocus);
        }
    }

3 自定义跑马灯

自定义跑马灯完整代码

import android.content.Context;
import android.graphics.Rect;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.AttributeSet;

/**
 * Created by sgll on 2018/11/20.
 * 自定义:跑马灯
 * 解决:
 * 1、EditText 存在时抢占焦距,使跑马灯停止的问题:重写父类的 onFocusChanged 方法
 * 2、弹出对话框后,跑马灯停止的问题:重写父类的 onWindowFocusChanged 方法
 */
public class MarqueeTextView extends android.support.v7.widget.AppCompatTextView {

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

    public MarqueeTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        // 设置单行
        setSingleLine();
//        setMaxLines(1);
        //设置 Ellipsize,setMaxLines(1) 和 setEllipsize 冲突
        setEllipsize(TextUtils.TruncateAt.MARQUEE);
        //获取焦距
        setFocusable(true);
        //走马灯的重复次数,-1代表无限重复
        setMarqueeRepeatLimit(-1);
        //强制获得焦点
        setFocusableInTouchMode(true);
    }

    /**
     * 使这个 View 永远获得焦距
     * @return true
     */
    @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 抢占焦点问题
     * @param hasWindowFocus
     */
    @Override
    public void onWindowFocusChanged(boolean hasWindowFocus) {
        if(hasWindowFocus){
            super.onWindowFocusChanged(hasWindowFocus);
        }
    }
}

整体效果

android跑马灯不跑 android 跑马灯_android跑马灯不跑_06