Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android TextView: is there a way to force the marquee animation with short text?

I have a TextView with a some text inside and I want it to animate with the scrolling marquee animation. I saw this popular question about forcing the marquee animation, however the code in the answers only work if the text is long enough to go outside the bounds of the TextView (and thus the text is truncated), I was looking for a solution to permanently make the text have this marquee animation on regardless of the width of the text; is this possible?

like image 506
AndroidNoob Avatar asked Feb 18 '15 16:02

AndroidNoob


4 Answers

Make your own animation.

anim/marquee.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:fromXDelta="100%"
        android:toXDelta="-100%"
        android:duration="10000"
        android:repeatCount="infinite"
        android:repeatMode="restart"
        android:interpolator="@android:anim/linear_interpolator"/>
</set>

and in your activity,

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.my_activity);

    TextView myTextView = (TextView) findViewById(R.id.myTextView);
    Animation marquee = AnimationUtils.loadAnimation(this, R.anim.marquee);
    myTextView.startAnimation(marquee);
}
like image 62
tachyonflux Avatar answered Nov 10 '22 21:11

tachyonflux


Taking @JodiMiddleton's suggestion about padding the text I constructed a few helper methods to pad the text to a target width based on a TextPaint object (ensuring correct sizing from fonts etc when measuring):

/**
 * Pad a target string of text with spaces on the right to fill a target
 * width
 * 
 * @param text The target text
 * @param paint The TextPaint used to measure the target text and
 *            whitespaces
 * @param width The target width to fill
 * @return the original text with extra padding to fill the width
 */
public static CharSequence padText(CharSequence text, TextPaint paint, int width) {

    // First measure the width of the text itself
    Rect textbounds = new Rect();
    paint.getTextBounds(text.toString(), 0, text.length(), textbounds);

    /**
     * check to see if it does indeed need padding to reach the target width
     */
    if (textbounds.width() > width) {
        return text;
    }

    /*
     * Measure the text of the space character (there's a bug with the
     * 'getTextBounds() method of Paint that trims the white space, thus
     * making it impossible to measure the width of a space without
     * surrounding it in arbitrary characters)
     */
    String workaroundString = "a a";
    Rect spacebounds = new Rect();
    paint.getTextBounds(workaroundString, 0, workaroundString.length(), spacebounds);

    Rect abounds = new Rect();
    paint.getTextBounds(new char[] {
        'a'
    }, 0, 1, abounds);

    float spaceWidth = spacebounds.width() - (abounds.width() * 2);

    /*
     * measure the amount of spaces needed based on the target width to fill
     * (using Math.ceil to ensure the maximum whole number of spaces)
     */
    int amountOfSpacesNeeded = (int)Math.ceil((width - textbounds.width()) / spaceWidth);

    // pad with spaces til the width is less than the text width
    return amountOfSpacesNeeded > 0 ? padRight(text.toString(), text.toString().length()
            + amountOfSpacesNeeded) : text;
}

/**
 * Pads a string with white space on the right of the original string
 * 
 * @param s The target string
 * @param n The new target length of the string
 * @return The target string padded with whitespace on the right to its new
 *         length
 */
public static String padRight(String s, int n) {
    return String.format("%1$-" + n + "s", s);
}

So when you use the methods based on a TextView you would call:

textView.setText(padText(myTargetString, textView.getPaint(), textView.getWidth()));

It's not elegant and I'm almost certain there's improvements that could be made (not to mention a better way of doing it) but nonetheless I'm using it in my code and it appears to be doing the trick :)

like image 27
AndroidNoob Avatar answered Nov 10 '22 19:11

AndroidNoob


I have found a one line code that will do the trick!

Just repeat your short text few times, or separate them with a long blank space.

activity_main.xml

<TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ellipsize="marquee"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:marqueeRepeatLimit="marquee_forever"
        android:scrollHorizontally="true"
        android:singleLine="true"
        android:text=""
        android:textColor="#ffffff"/>

MainActivity.java

String shortText = "A short text.";
TextView textView = findViewById(R.id.textView);
textView.setText(shortText + "             " + shortText); 
// repeat the above concatenation as many as required 
// just enough to make it marqueeable for auto scrolling 
// or you can just increase the length of the blank space
textView.setSelected(true);
like image 41
Ehsan Rosdi Avatar answered Nov 10 '22 21:11

Ehsan Rosdi


It sounds dirty but the path of least resistance will probably be to pad the text with spaces to allow the scrolling.

You can removed them on Click if needed.

like image 1
JodiMiddleton Avatar answered Nov 10 '22 21:11

JodiMiddleton