TextView跑马灯效果不生效的原因分析及解决方案

今天同事遇到一个缺陷,跑马灯不生效,迟迟无法解决,于是帮忙看了下,最终顺利解决了,做个记录。

首先原来的代码是这样的:

JAVA代码实现(Kotlin):

holder.itemView.tv_title.text = title
          

XML中实现:
 <TextView
                android:id="@+id/tv_title"
                android:layout_width="@dimen/dip96"
                android:layout_height="@dimen/dip17"
                android:ellipsize="marquee"
                android:focusableInTouchMode="true"
                android:fontFamily="sans-serif-medium"
                android:gravity="center"
                android:marqueeRepeatLimit="marquee_forever"
                android:scrollHorizontally="true"
                android:singleLine="true"
                android:textColor="@color/color_333333"
                android:textSize="@dimen/dip12"
                tools:text="系统版本信息" />

都很正常。搜索晚上大多数的解决方案,无法是添加singleLine等字段,这里面其实都已经设置了。百度的所有方案都试过了,都是无效的,靠人不如靠己,就只能自己尝试调试源码解决了。幸好手里面有一部神机pixel4,和源码完全对得上,可以直接调试。

 

原因分析:

我这里就不一步一步的分析原因了,直接定位到原因点,是否展示跑马灯效果,是textView中这段代码控制的:


    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
    private void startMarquee() {
        // Do not ellipsize EditText
        if (getKeyListener() != null) return;

        if (compressText(getWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight())) {
            return;
        }

        if ((mMarquee == null || mMarquee.isStopped()) && (isFocused() || isSelected())
                && getLineCount() == 1 && 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);
        }
    }

前面我们能看到正常的判断,是否一行,是否选中,是否获取焦点等等,这些都没有问题。问题点就在canMarquee()方法里面。

 

    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));
    }

这里有一个很重要的问题,我们的绘制流程一般是measure->layout->draw的流程,而startMarquee的调用时机默认是在makeNewLayout中,也就是measure的流程中。但是mRight和mLeft的赋值却是在layout的流程当中,所以这里,首次的时候,最终得到的width的值一定是<=0的,这样的话,canMarquee返回值为false,所以跑马灯没有生效。

在源码当中,只有界面绘制出来,然后再去获取焦点的话,此时跑马灯效果才生效,这也是正常跑马灯效果展示的流程。但是如果xml中就声明了获取焦点,这时候有时也是不生效的,因为默认就具有焦点,调用canMarquee方法的时候,left和right很有可能还是0。

解决方案1:

既然这样,那我们就post一个runnable,再去获取焦点就好了。如下:

 holder.itemView.tv_title.text = name
            Handler().post{
                holder.itemView.tv_title.requestFocus()
            }

但是这样还是有问题,如果界面上有多个TextView的话,多个textView发生焦点抢占,这时候去执行

f ((mMarquee == null || mMarquee.isStopped()) && (isFocused() || isSelected())
                && getLineCount() == 1 && canMarquee())

判断的时候,因为返回isFoucused返回false,所以跑马灯还是不生效的。或者严格点说,多个textView,只有最后获取到焦点的那个才会生效。

解决方案2:

看源码,除了isFocused(),还有isSelected(),那我们能不能使用isSelected来解决这个问题呢?当然可以,代码稍微一改:

 holder.itemView.tv_title.text = name
            Handler().post{
                holder.itemView.tv_title.isSelected = true
            }

这样就完美了,多个textView也可以同时实现跑马灯的效果了。

说到这里,还有一个要注意的点,源码当中,只有select状态的切换,才会触发执行跑马灯效果,所以务必把textView的isSelected的属性,设置为false,切记。

源码如下:

 @Override
    public void setSelected(boolean selected) {
        boolean wasSelected = isSelected();

        super.setSelected(selected);
        //这里,只有发生状态的切换,才会触发startMarquee方法
        if (selected != wasSelected && mEllipsize == TextUtils.TruncateAt.MARQUEE) {
            if (selected) {
                startMarquee();
            } else {
                stopMarquee();
            }
        }
    }

 

 

 

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

失落夏天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值