【问题标题】:Marquee Set Speed选框设置速度
【发布时间】:2022-03-01 22:13:17
【问题描述】:

我正在使用选取框来显示我的一个活动中的文本。我的问题是可以加快选取框的速度,以便更快地沿着屏幕滚动。下面是我的 XML 和 Java。

TextView et2 = (TextView) findViewById(R.id.noneednum);
    et2.setEllipsize(TruncateAt.MARQUEE);    
    et2.setText("");
    if (num.size() > 0) {
        for (String str : num) {
            et2.append(str + "    ");
        }
    }
    et2.setSelected(true);
}

和 XML:

<TextView
    android:id="@+id/noneednum"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerHorizontal="true"
    android:layout_centerVertical="true"
    android:ellipsize="marquee"
    android:fadingEdge="horizontal"
    android:gravity="center_vertical|center_horizontal"
    android:lines="1"
    android:marqueeRepeatLimit="marquee_forever"
    android:scrollHorizontally="true"
    android:singleLine="true"
    android:text="Large Text"
    android:textColor="#fff"
    android:textSize="140dp" />

【问题讨论】:

  • @SergeyBenner 第一个链接有TextView.MARQUEE_SPEED_FAST cannot be resolved,第二个选项看起来复杂一定是更简单的方法?
  • 第一个链接是问题 - 用于设置品牌速度的增强功能:) 第二个是解决方案的链接。我想没有其他办法,但有人可能已经以其他方式解决了......
  • 你想提高滚动速度
  • 仅供他人参考。当 android:maxLines="1" 时,选取框没有动画(我必须使用 android:singleLine,即使它已被贬值)。

标签: android


【解决方案1】:

您必须创建一个自定义类来滚动文本:

ScrollTextView.java

public class ScrollTextView extends TextView {

     // scrolling feature
     private Scroller mSlr;

     // milliseconds for a round of scrolling
     private int mRndDuration = 10000;

     // the X offset when paused
     private int mXPaused = 0;

     // whether it's being paused
     private boolean mPaused = true;

     /*
     * constructor
     */
     public ScrollTextView(Context context) {
         this(context, null);
         // customize the TextView
         setSingleLine();
         setEllipsize(null);
         setVisibility(INVISIBLE);
     }

     /*
     * constructor
     */
     public ScrollTextView(Context context, AttributeSet attrs) {
         this(context, attrs, android.R.attr.textViewStyle);
         // customize the TextView
         setSingleLine();
         setEllipsize(null);
         setVisibility(INVISIBLE);
     }

     /*
     * constructor
     */
     public ScrollTextView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         // customize the TextView
         setSingleLine();
         setEllipsize(null);
         setVisibility(INVISIBLE);
     }

     /**
     * begin to scroll the text from the original position
     */
     public void startScroll() {
         // begin from the very right side
         mXPaused = -1 * getWidth();
         // assume it's paused
         mPaused = true;
         resumeScroll();
     }

     /**
     * resume the scroll from the pausing point
     */
     public void resumeScroll() {

         if (!mPaused) return;

         // Do not know why it would not scroll sometimes
         // if setHorizontallyScrolling is called in constructor.
         setHorizontallyScrolling(true);

         // use LinearInterpolator for steady scrolling
         mSlr = new Scroller(this.getContext(), new LinearInterpolator());
         setScroller(mSlr);

         int scrollingLen = calculateScrollingLen();
         int distance = scrollingLen - (getWidth() + mXPaused);
         int duration = (new Double(mRndDuration * distance * 1.00000
         / scrollingLen)).intValue();

         setVisibility(VISIBLE);
         mSlr.startScroll(mXPaused, 0, distance, 0, duration);
         invalidate();
         mPaused = false;
     }

     /**
     * calculate the scrolling length of the text in pixel
     *
     * @return the scrolling length in pixels
     */
     private int calculateScrollingLen() {
         TextPaint tp = getPaint();
         Rect rect = new Rect();
         String strTxt = getText().toString();
         tp.getTextBounds(strTxt, 0, strTxt.length(), rect);
         int scrollingLen = rect.width() + getWidth();
         rect = null;
         return scrollingLen;
     }

     /**
     * pause scrolling the text
     */
     public void pauseScroll() {
         if (null == mSlr) return;

         if (mPaused)
         return;

         mPaused = true;

         // abortAnimation sets the current X to be the final X,
         // and sets isFinished to be true
         // so current position shall be saved
         mXPaused = mSlr.getCurrX();

         mSlr.abortAnimation();
     }

     @Override
     /*
     * override the computeScroll to restart scrolling when finished so as that
     * the text is scrolled forever
     */
     public void computeScroll() {
         super.computeScroll();

         if (null == mSlr) return;

         if (mSlr.isFinished() && (!mPaused)) {
           this.startScroll();
         }
     }

     public int getRndDuration() {
       return mRndDuration;
     }

     public void setRndDuration(int duration) {
       this.mRndDuration = duration;
     }

     public boolean isPaused() {
       return mPaused;
     }
}

在你的布局中这样写:

<yourpackagename.ScrollTextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:id="@+id/scrolltext" />

在你的活动中这样写:

ScrollTextView scrolltext=(ScrollTextView) findViewById(R.id.scrolltext);
scrolltext.setText(yourscrollingtext);
scrolltext.setTextColor(Color.BLACK);
scrolltext.startScroll();

如果你想增加滚动速度,那么减少:

private int mRndDuration = 10000;//reduce the value of mRndDuration to increase scrolling speed

【讨论】:

  • 对于多语言,您的自定义选取框不起作用?你知道其他方法吗?
  • @Ramakrishna 真棒,你的代码运行良好。我还有一个疑问如何滚动 1. 从上到下 2. 从下到上 3. 从右到左澄清我的疑问
  • 速度看起来很随机
  • @Ramakrishna 对我来说效果很好。但是这里下一个滚动仅在上一个滚动结束后才开始。示例:假设我有一个文本“ABC”。只有在“C”滚动离开屏幕(向左)之后,“下一个滚动的 A”才进入屏幕(从右)。结果我在前一个滚动的“C”之后得到一个空白屏幕,直到它滚动了。我需要开始下一个滚动,即下一个滚动的“A”应该在上一个滚动的“C”进入屏幕之后立即出现在屏幕上,这样两个滚动之间就没有空格。我希望我说清楚了。现在如何实现呢?
  • 很好的答案!我改变了两件事:|1。目前速度取决于文本的长度。要修复它,请创建int duration = (int) (1000f * distance / mScrollSpeed);,其中 mScrollSpeed 约为 100f。 |2。要解决第一次运行后速度变化的问题,请在计算布局后开始滚动 - 为此使用 OnGlobalLayoutListener:scrollTextView.getViewTreeObserver().addOnGlobalLayoutListener(new ... {scrollTextView.startScroll();//remove listener after that}});
【解决方案2】:

如果 TextView 是 AppCompatTextView 的实例,上述代码将失败。下面的代码有效的是它是 AppCompatTextView。在棉花糖中测试。

public static void setMarqueeSpeed(TextView tv, float speed) {
    if (tv != null) {
        try {
            Field f = null;
            if (tv instanceof AppCompatTextView) {
                f = tv.getClass().getSuperclass().getDeclaredField("mMarquee");
            } else {
                f = tv.getClass().getDeclaredField("mMarquee");
            }
            if (f != null) {
                f.setAccessible(true);
                Object marquee = f.get(tv);
                if (marquee != null) {
                    String scrollSpeedFieldName = "mScrollUnit";
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        scrollSpeedFieldName = "mPixelsPerSecond";
                    }
                    Field mf = marquee.getClass().getDeclaredField(scrollSpeedFieldName);
                    mf.setAccessible(true);
                    mf.setFloat(marquee, speed);
                }
            } else {
                Logger.e("Marquee", "mMarquee object is null.");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

【讨论】:

  • 这很漂亮。我喜欢它,它奏效了。谢谢你。如果其他人遇到问题,请尝试将post 方法与包含选框速度方法回调的可运行对象一起使用。这就是让我工作的原因
  • 它不适用于 android 9 及更高版本。因为 mPixelsPerSecond 字段在 android 9 上已更改。我尝试使用“mPixelsPerMs”但找不到该字段。
【解决方案3】:

这对我有用。如果 f.get(tv) 返回 null,请在调用 setMarqueeSpeed() 之前尝试调用 mTextView.setSelected(true)。 原答案:Android and a TextView's horizontal marquee scroll rate

private void setMarqueeSpeed(TextView tv, float speed, boolean speedIsMultiplier) {

    try {
        Field f = tv.getClass().getDeclaredField("mMarquee");
        f.setAccessible(true);

        Object marquee = f.get(tv);
        if (marquee != null) {

            String scrollSpeedFieldName = "mScrollUnit";
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.L)
                scrollSpeedFieldName = "mPixelsPerSecond";

            Field mf = marquee.getClass().getDeclaredField(scrollSpeedFieldName);
            mf.setAccessible(true);

            float newSpeed = speed;
            if (speedIsMultiplier)
                newSpeed = mf.getFloat(marquee) * speed;

            mf.setFloat(marquee, newSpeed);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

【讨论】:

  • 无法让它工作。谁能确认这是否有效?
  • 另外,除了使用反射还有什么其他方法吗?
【解决方案4】:

我在运行 Android 7.1 的设备上从此处和其他地方的多个帖子中解决了上述滚动问题

  1. 解决了速度问题
  2. 仅在需要时滚动/文本长于 TextView 的宽度
  3. 适用于扩展 TextView 或 AppCompatTextView

package com.myclass.classes;

import android.content.Context;
import android.graphics.Rect;
import android.text.Layout;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.animation.LinearInterpolator;
import android.widget.Scroller;
import android.widget.TextView;

public class ScrollTextView extends TextView {

// scrolling feature
private Scroller mSlr;

// the X offset when paused
private int mXPaused = 0;

// whether it's being paused
private boolean mPaused = true;

private float mScrollSpeed = 250f; //Added speed for same scrolling speed regardless of text

/*
 * constructor
 */
public ScrollTextView(Context context) {
    this(context, null);
    // customize the TextView
    setSingleLine();
    setEllipsize(null);
    setVisibility(VISIBLE);
    getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener); //added listener check
}

/*
 * constructor
 */
public ScrollTextView(Context context, AttributeSet attrs) {
    this(context, attrs, android.R.attr.textViewStyle);
    // customize the TextView
    setSingleLine();
    setEllipsize(null);
    setVisibility(VISIBLE);
    getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener); //added listener check
}

/*
 * constructor
 */
public ScrollTextView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    // customize the TextView
    setSingleLine();
    setEllipsize(null);
    setVisibility(VISIBLE);
    getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener); //added listener check

}


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

/**
 * begin to scroll the text from the original position
 */
private void startScroll() {
    boolean needsScrolling = checkIfNeedsScrolling();
    // begin from the middle
    mXPaused = -1 * (getWidth() / 2);
    // assume it's paused
    mPaused = true;
    if (needsScrolling) {
        
        resumeScroll();
    } else {
        pauseScroll();
    }
    removeGlobalListener();
}

/**
 * Removing global listener
 **/
private synchronized void removeGlobalListener() {
    try {
        if (onGlobalLayoutListener != null)
            getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener);
        onGlobalLayoutListener = null;
    } catch (Exception e) {
        e.printStackTrace();
    }
}

/**
 * Waiting for layout to initiate
 */
private OnGlobalLayoutListener onGlobalLayoutListener = () -> {
    startScroll();
};

/**
 * Checking if we need scrolling
 */
private boolean checkIfNeedsScrolling() {
    measure(0, 0);
    int textViewWidth = getWidth();
    if (textViewWidth == 0)
        return false;

    float textWidth = getTextLength();

    return textWidth > textViewWidth;
}

/**
 * resume the scroll from the pausing point
 */
public void resumeScroll() {

    if (!mPaused) return;

    // Do not know why it would not scroll sometimes
    // if setHorizontallyScrolling is called in constructor.
    setHorizontallyScrolling(true);

    // use LinearInterpolator for steady scrolling
    mSlr = new Scroller(this.getContext(), new LinearInterpolator());
    setScroller(mSlr);

    int scrollingLen = calculateScrollingLen();
    int distance = scrollingLen - (getWidth() + mXPaused);
    int duration = (int) (1000f * distance / mScrollSpeed);

    setVisibility(VISIBLE);
    mSlr.startScroll(mXPaused, 0, distance, 0, duration);
    invalidate();
    mPaused = false;
}

/**
 * calculate the scrolling length of the text in pixel
 *
 * @return the scrolling length in pixels
 */
private int calculateScrollingLen() {
    int length = getTextLength();
    return length + getWidth();
}

private int getTextLength() {
    TextPaint tp = getPaint();
    Rect rect = new Rect();
    String strTxt = getText().toString();
    tp.getTextBounds(strTxt, 0, strTxt.length(), rect);
    int length = rect.width();
    rect = null;
    return length;
}

/**
 * pause scrolling the text
 */
public void pauseScroll() {
    if (null == mSlr) return;

    if (mPaused)
        return;

    mPaused = true;

    // abortAnimation sets the current X to be the final X,
    // and sets isFinished to be true
    // so current position shall be saved
    mXPaused = mSlr.getCurrX();

    mSlr.abortAnimation();
}

@Override
/*
 * override the computeScroll to restart scrolling when finished so as that
 * the text is scrolled forever
 */
public void computeScroll() {
    super.computeScroll();

    if (null == mSlr) return;

    if (mSlr.isFinished() && (!mPaused)) {
        this.startScroll();
    }
}

public boolean isPaused() {
    return mPaused;
}
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-09-20
    • 1970-01-01
    • 2016-08-09
    • 2014-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多