千家信息网

Android中怎么用TextView实现跑马灯效果

发表于:2025-11-08 作者:千家信息网编辑
千家信息网最后更新 2025年11月08日,这篇"Android中怎么用TextView实现跑马灯效果"文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面
千家信息网最后更新 2025年11月08日Android中怎么用TextView实现跑马灯效果

这篇"Android中怎么用TextView实现跑马灯效果"文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇"Android中怎么用TextView实现跑马灯效果"文章吧。

    【前言】

    在Textview设置的宽度有限,而需要显示的文字又比较多的情况下,往往需要给Textview设置跑马灯效果才能让用户完整地看到所有设置的文字,所以给TextView设置跑马灯效果的需求是很常见的

    一、新手设置跑马灯效果

    1、先在xml中给Textview设置好对应的属性

     

    2、然后在代码中设置请求获取焦点即可

            TextView tv = findViewById(R.id.tv);        tv.requestFocus();

    这样设置之后,跑马灯的效果就出来了

    【关键点讲解】

    1、android:layout_width 是限制为固定宽度,同时文本的长度大于所设置的宽度,要是设置android:layout_widthwrap_content, 那么Textview的宽度会随着文本长度变长而拉宽,这样就不能出现跑马灯效果
    2、android:singleLine="true"设置Textview只能一行显示,要是不设置为true,默认会自动换行,显示为多行,这样的话,也不能出现跑马灯效果
    3、android:ellipsize="marquee"设置要是文本长度超出Textview的宽度时候,文本应该以跑马灯效果显示,这个是设置跑马灯效果最关键的设置,android:ellipsize还可以取值startendmiddlenone,分别是开头显示省略号结尾显示省略号中间显示省略号直接截断
    4、android:focusable="true"设置Textview可以获取焦点,跑马灯效果需要获取到焦点时候才生效,Textview默认是不获取焦点的
    5、android:focusableInTouchMode="true"设置在触摸模式下可以获取焦点,目前智能机基本都是自动进入触摸模式,其实目前只要设置android:focusableInTouchMode="true",默认android:focusable也会变为true了
    6、android:marqueeRepeatLimit="-1"设置跑马灯循环的次数,-1表示无限循环,不设置的话,默认是循环3次
    7、 tv.requestFocus();设置获取焦点, 只有当该view的focusable属性为true时候才生效

    【总结】

    1、一定要设置android:focusableInTouchMode="true",若是只设置了android:focusable="true"android:focusableInTouchMode没设置,那么跑马灯效果是不生效的,因为进入触摸模式之后,isFocusable()返回false,下面看看Texivew startMarquee()源码就知道需要满足什么条件才会开始跑马灯特效:

         private void startMarquee() {        // Do not ellipsize EditText        if (getKeyListener() != null) return;        if (compressText(getWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight())) {            return;        }                                // 1、跑马灯控制类没有创建或者跑马灯效果已经停止        if ((mMarquee == null || mMarquee.isStopped()) &&         // 2、当前Textview是获取到焦点或者被选中状态        (isFocused() || isSelected())        // 3、文本的行数只有一行         && getLineCount() == 1         // 4、文本长度大于Textview的宽度          && canMarquee()) {            if (mMarqueeFadeMode == MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) {                mMarqueeFadeMode = MARQUEE_FADE_SWITCH_SHOW_FADE;                final Layout tmp = mLayout;                mLayout = mSavedMarqueeModeLayout;                mSavedMarqueeModeLayout = tmp;                setHorizontalFadingEdgeEnabled(true);                requestLayout();                invalidate();            }            if (mMarquee == null) mMarquee = new Marquee(this);            mMarquee.start(mMarqueeRepeatLimit);        }    }         private boolean canMarquee() {        int width = mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight();        return width > 0 && (mLayout.getLineWidth(0) > width                || (mMarqueeFadeMode != MARQUEE_FADE_NORMAL && mSavedMarqueeModeLayout != null                        && mSavedMarqueeModeLayout.getLineWidth(0) > width));    }

    二、高端玩家设置跑马灯效果

    从上面总结的TextView跑马灯源码可以看到,只要isFocusable()或者isSelected()方法返回true,那么就没必要管是否触摸模式,是否可以获取焦点之类的问题了,所以我们可以自定义一个类继承于TextView,然后重写isFocusable()直接返回true即可:

    public class MarqueeTextView extends TextView {    public MarqueeTextView(Context context) {        super(context);        initView(context);    }    public MarqueeTextView(Context context, AttributeSet attrs) {        super(context, attrs);        initView(context);    }    public MarqueeTextView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        initView(context);    }    private void initView(Context context) {        this.setEllipsize(TextUtils.TruncateAt.MARQUEE);        this.setSingleLine(true);        this.setMarqueeRepeatLimit(-1);    }    //最关键的部分    public boolean isFocused() {        return true;    }}

    1、直接在Xml中使用自定义的MarqueeTextView,那么跑马灯效果就出来了,无需任何额外配置

     

    来看看效果:

    三、延伸阅读

    假如有这样一个需求:因为显示文本的空间有限,所以只能用跑马灯的效果来给用户展示文本,但是在用户完整地看完一遍文本之后,需要隐藏掉Textview,那么问题来了,我们怎么知道跑马灯效果什么时候跑完一遍呢?先来看看Textview跑马灯部分Marquee类的部分源码:

           void start(int repeatLimit) {       //重复次数设置0,那就直接停止跑马灯            if (repeatLimit == 0) {                stop();                return;            }           //...省略掉大部分不相关的代码                mChoreographer.postFrameCallback(mStartCallback);            }        }             private Choreographer.FrameCallback mStartCallback = new Choreographer.FrameCallback() {            @Override            public void doFrame(long frameTimeNanos) {                mStatus = MARQUEE_RUNNING;                mLastAnimationMs = mChoreographer.getFrameTime();                tick();            }        };        void tick() {            if (mStatus != MARQUEE_RUNNING) {                return;            }            if (textView != null && (textView.isFocused() || textView.isSelected())) {                long currentMs = mChoreographer.getFrameTime();                long deltaMs = currentMs - mLastAnimationMs;                mLastAnimationMs = currentMs;                float deltaPx = deltaMs * mPixelsPerMs;                mScroll += deltaPx;                //要是跑马灯滚动的距离大于最大距离,那么回到给mRestartCallback                if (mScroll > mMaxScroll) {                    mScroll = mMaxScroll;                    mChoreographer.postFrameCallbackDelayed(mRestartCallback, MARQUEE_DELAY);                } else {                    mChoreographer.postFrameCallback(mTickCallback);                }                textView.invalidate();            }        }                 private Choreographer.FrameCallback mRestartCallback = new Choreographer.FrameCallback() {            @Override            public void doFrame(long frameTimeNanos) {                if (mStatus == MARQUEE_RUNNING) {                    if (mRepeatLimit >= 0) {                        mRepeatLimit--;                    }                    start(mRepeatLimit);                }            }        }

    从上面对Marquee源码分析可知,跑马灯跑完一轮之后会调用到MarqueemRestartCallback对象的doFrame方法,那么我们来一招"偷龙转凤",通过反射把mRestartCallback对象替换成我们自己实例化的对象,那么在跑马灯跑完一轮之后就会回调到我们替换的对象中,这样就实现了对跑马灯效果跑完一轮的监听,实现源码如下:

    public class MarqueeTextView extends androidx.appcompat.widget.AppCompatTextView {    private Choreographer.FrameCallback mRealRestartCallbackObj;    private Choreographer.FrameCallback mFakeRestartCallback;    private OnShowTextListener mOnShowTextListener;    public MarqueeTextView(Context context, OnShowTextListener onShowTextListener) {        super(context);        initView(context);        this.mOnShowTextListener = onShowTextListener;    }    public MarqueeTextView(Context context, AttributeSet attrs) {        super(context, attrs);        initView(context);    }    public MarqueeTextView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        initView(context);    }    private void initView(Context context) {        //绕过隐藏api的限制        Reflection.unseal(context.getApplicationContext());        //设置跑马灯生效条件        this.setEllipsize(TextUtils.TruncateAt.MARQUEE);        this.setSingleLine(true);        this.setFocusable(true);        //反射设置跑马灯监听        try {            //从TextView类中找到定义的字段mMarquee            Field marqueeField = ReflectUtil.getDeclaredField(TextView.class, "mMarquee");            //获取Marquee类的构造方法Marquee(TextView v)            Constructor declaredConstructor = ReflectUtil.getDeclaredConstructor(Class.forName("android.widget.TextView$Marquee"), TextView.class);            //实例化一个Marquee对象,传入参数是Textview对象            Object marqueeObj = declaredConstructor.newInstance(this);            //从Marquee类中找到定义的字段mRestartCallback,重新开始一轮跑马灯时候会回调到这个对象doFrame()方法            Field restartCallbackField = ReflectUtil.getDeclaredField(Class.forName("android.widget.TextView$Marquee"), "mRestartCallback");            //从Marquee实例对象中获取到真实的mRestartCallback对象            mRealRestartCallbackObj = (Choreographer.FrameCallback) restartCallbackField.get(marqueeObj);            //构造一个假的mRestartCallback对象,用来监听什么时候跑完一轮跑马灯效果            mFakeRestartCallback = new Choreographer.FrameCallback() {                @Override                public void doFrame(long frameTimeNanos) {                    //这里还是执行真实的mRestartCallback对象的代码逻辑                    mRealRestartCallbackObj.doFrame(frameTimeNanos);                    Log.i("min77","跑马灯文本显示完毕");                    //回调通知跑完一轮                    if(MarqueeTextView.this.mOnShowTextListener != null){                        MarqueeTextView.this.mOnShowTextListener.onComplete(0);                    }                }            };            //把假的mRestartCallback对象设置给Marquee对象,其实就是代理模式            restartCallbackField.set(marqueeObj, mFakeRestartCallback);            //把自己实例化的Marquee对象设置给Textview            marqueeField.set(this, marqueeObj);        } catch (Exception e) {            e.printStackTrace();            Log.e("min77",e.getMessage());        }    }    //最关键的部分    public boolean isFocused() {        return true;    }    /**     * 是否显示完整文本     */    public interface OnShowTextListener{        void onComplete(int delayMillisecond);    }}

    效果如下:

    以上就是关于"Android中怎么用TextView实现跑马灯效果"这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注行业资讯频道。

    跑马灯 效果 对象 文本 焦点 宽度 时候 一轮 内容 模式 源码 关键 实例 方法 要是 部分 长度 代码 用户 省略号 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 网络服务器搭建于管理 坦克世界晚上服务器崩溃 方舟服务器倍率怎么调好玩 计算机网络技术三级 网课 华品博瑞网络技术有限公司面试 天河小程序软件开发价格表 软件开发工具产品 关系型数据库面试题 广东省ipfs云服务器云空间 江苏华为服务器维修维保多少钱 关于加强网络安全教育 绝地求生体验服务器繁忙 域名解析到多个服务器 软件开发财政预算 东营 云商网络技术支持 数据库第一章课后习题答案 计算机网络技术是专科吗 sql数据库修复卡住了 智慧食堂整体解决方案软件开发 哈尔滨海上飞互联网科技有限公司 app商城软件开发 服务器管理添加与删除 梧州教育软件开发 数据库备份了两个bak怎么导入 哪些行业需要软件开发 数据库技术查询工具 2022年工作网络安全计划 数据库数据具有什么性 国网网络安全活动总结 贵州独山软件开发有限公司
    0