Marquee Set Speed

I Am using a marquee to show the text in one of my Activitys. My question is it possible to speed up the rate of the marquee so it scrolls along the screen faster. Below is my XML and 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);
}

And 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" />

Here is Solutions:

We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.

Solution 1

You have to create a custom class for scrolling the text:

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

In your layout write like this:

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

In your activity write like this:

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

If you want to increase the scrolling speed then reduce the value of :

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

Solution 2

Above code fails if the TextView is an instance of AppCompatTextView. Below code works is it is AppCompatTextView. Tested in Marshmallow.

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

Solution 3

This works for me. If f.get(tv) returns null, try calling mTextView.setSelected(true) before calling setMarqueeSpeed().
Original answer: 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();
    }
}

Solution 4

I resolved above scrolling problems on my device running Android 7.1 taking from multiple posts here and elsewhere

  1. Solved speed issue
  2. Scrolling only if needed / text is longer than width of TextView
  3. Works with extending TextView or 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;
}
}

Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply