【前言】

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

一、新手设置跑马灯效果

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

 <TextView
        android:id="@ id/tv"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/show_float"
        android:singleLine="true"
        android:ellipsize="marquee"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:marqueeRepeatLimit="-1"
        android:layout_marginTop="20dp"
        android:padding="10dp"
        android:text="欢迎来到跑马灯新手村,这是新手示例~"
        android:textColor="@color/white"
        android:background="@drawable/com_live_rounded_rectangle"/>

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,那么跑马灯效果就出来了,无需任何额外配置

 <com.example.MarqueeTextView
        android:id="@ id/tv"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/show_float"
        android:layout_marginTop="20dp"
        android:padding="10dp"
        android:text="欢迎来到跑马灯高端玩家局,这是高端玩法示例~"
        android:textColor="@color/white"
        android:background="@drawable/com_live_rounded_rectangle"/>

来看看效果:

在这里插入图片描述

三、延伸阅读

     假如有这样一个需求:因为显示文本的空间有限,所以只能用跑马灯的效果来给用户展示文本,但是在用户完整地看完一遍文本之后,需要隐藏掉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实现跑马灯效果代码的文章就介绍到这了,更多相关Android TextView跑马灯效果内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

Android用TextView实现跑马灯效果代码的更多相关文章

  1. html5 canvas合成海报所遇问题及解决方案总结

    这篇文章主要介绍了html5 canvas合成海报所遇问题及解决方案总结,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Html5 video标签视频的最佳实践

    这篇文章主要介绍了Html5 video标签视频的最佳实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  3. HTML5在微信内置浏览器下右上角菜单的调整字体导致页面显示错乱的问题

    HTML5在微信内置浏览器下,在右上角菜单的调整字体导致页面显示错乱的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

  4. ios – containerURLForSecurityApplicationGroupIdentifier:在iPhone和Watch模拟器上给出不同的结果

    我使用默认的XCode模板创建了一个WatchKit应用程序.我向iOSTarget,WatchkitAppTarget和WatchkitAppExtensionTarget添加了应用程序组权利.(这是应用程序组名称:group.com.lombax.fiveminutes)然后,我尝试使用iOSApp和WatchKitExtension访问共享文件夹URL:延期:iOS应用:但是,测试NSURL

  5. Ionic – Splash Screen适用于iOS,但不适用于Android

    我有一个离子应用程序,其中使用CLI命令离子资源生成的启动画面和图标iOS版本与正在渲染的启动画面完美配合,但在Android版本中,只有在加载应用程序时才会显示白屏.我检查了config.xml文件,所有路径看起来都是正确的,生成的图像出现在相应的文件夹中.(我使用了splash.psd模板来生成它们.我错过了什么?这是config.xml文件供参考,我觉得我在这里做错了–解决方法在config.xml中添加以下键:它对我有用!

  6. ios – 无法启动iPhone模拟器

    /Library/Developer/CoreSimulator/Devices/530A44CB-5978-4926-9E91-E9DBD5BFB105/data/Containers/Bundle/Application/07612A5C-659D-4C04-ACD3-D211D2830E17/ProductName.app/ProductName然后,如果您在Xcode构建设置中选择标准体系结构并再次构建和运行,则会产生以下结果:dyld:lazysymbolbindingFailed:Symbol

  7. Xamarin iOS图像在Grid内部重叠

    heyo,所以在Xamarin我有一个使用并在其中包含一对,所有这些都包含在内.这在Xamarin.Android中看起来完全没问题,但是在Xamarin.iOS中,图像与标签重叠.我不确定它的区别是什么–为什么它在Xamarin.Android中看起来不错但在iOS中它的全部都不稳定?

  8. 在iOS上向后播放HTML5视频

    我试图在iPad上反向播放HTML5视频.HTML5元素包括一个名为playbackRate的属性,它允许以更快或更慢的速率或相反的方式播放视频.根据Apple’sdocumentation,iOS不支持此属性.通过每秒多次设置currentTime属性,可以反复播放,而无需使用playbackRate.这种方法适用于桌面Safari,但似乎在iOS设备上的搜索限制为每秒1次更新–在我的情况下太慢了.有没有办法在iOS设备上向后播放HTML5视频?解决方法iOS6Safari现在支持playbackRat

  9. 使用 Swift 语言编写 Android 应用入门

    Swift标准库可以编译安卓armv7的内核,这使得可以在安卓移动设备上执行Swift语句代码。做梦,虽然Swift编译器可以胜任在安卓设备上编译Swift代码并运行。这需要的不仅仅是用Swift标准库编写一个APP,更多的是你需要一些框架来搭建你的应用用户界面,以上这些Swift标准库不能提供。简单来说,构建在安卓设备上使用的Swiftstdlib需要libiconv和libicu。通过命令行执行以下命令:gitclonegit@github.com:SwiftAndroid/libiconv-libi

  10. 跑马灯动画点击事件

    代码块

随机推荐

  1. Flutter 网络请求框架封装详解

    这篇文章主要介绍了Flutter 网络请求框架封装详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Android单选按钮RadioButton的使用详解

    今天小编就为大家分享一篇关于Android单选按钮RadioButton的使用详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

  3. 解决android studio 打包发现generate signed apk 消失不见问题

    这篇文章主要介绍了解决android studio 打包发现generate signed apk 消失不见问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

  4. Android 实现自定义圆形listview功能的实例代码

    这篇文章主要介绍了Android 实现自定义圆形listview功能的实例代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  5. 详解Android studio 动态fragment的用法

    这篇文章主要介绍了Android studio 动态fragment的用法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  6. Android用RecyclerView实现图标拖拽排序以及增删管理

    这篇文章主要介绍了Android用RecyclerView实现图标拖拽排序以及增删管理的方法,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下

  7. Android notifyDataSetChanged() 动态更新ListView案例详解

    这篇文章主要介绍了Android notifyDataSetChanged() 动态更新ListView案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下

  8. Android自定义View实现弹幕效果

    这篇文章主要为大家详细介绍了Android自定义View实现弹幕效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  9. Android自定义View实现跟随手指移动

    这篇文章主要为大家详细介绍了Android自定义View实现跟随手指移动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. Android实现多点触摸操作

    这篇文章主要介绍了Android实现多点触摸操作,实现图片的放大、缩小和旋转等处理,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部