7

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?

Community
  • 1
  • 1
AndroidNoob
  • 2,653
  • 2
  • 37
  • 51
  • What's the point of a marquee animation if the text is not long enough to exceed the bounds of the TextView? – Karakuri Feb 18 '15 at 16:55
  • It's for some title text, and for the sake of consistency/branding I wanted the animation always on EDIT: and it looks nice obviously :P – AndroidNoob Feb 18 '15 at 16:57

6 Answers6

17

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);
}
tachyonflux
  • 19,305
  • 6
  • 43
  • 65
  • Great solution, however the interpolator should be set in the set not the translate. – Leon Dec 03 '15 at 12:20
  • @karaokyo Textview's text gets cut at the end while displayed which is fine(as per the view width), but the animation also shows the truncated text for animation instead of full text.Can you please tell me why? If i set textview.setselected to true then full text shows up in the animation but the animation starts without user interaction. – hemanth kumar Dec 17 '15 at 09:28
  • 1
    This animation is not only moving the text but the complete textview. I have a black background on textview and a white activity. Whole textview moves (with the background) and not just the text. – Shubham A. Feb 02 '16 at 09:15
  • it cuts some text at the end – user924 Feb 17 '20 at 13:23
3

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 :)

AndroidNoob
  • 2,653
  • 2
  • 37
  • 51
2

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);
Ehsan Rosdi
  • 411
  • 5
  • 15
1

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.

JodiMiddleton
  • 300
  • 1
  • 8
  • I considered this approach and it is pretty dirty, but it would do the job. The main problem is determining the amount of spaces to add to make it hit the scrolling limit... – AndroidNoob Feb 18 '15 at 17:20
  • I can see this being an issue with a dynamic sized text view. You can probably use the padding code from here: http://stackoverflow.com/questions/388461/how-can-i-pad-a-string-in-java to make it even on both sides. Then use the width of the TextView as a multiplier factor. All dirty but looks like even forcing this is a bit of a hack. – JodiMiddleton Feb 18 '15 at 17:25
0

Marquee animation for short text

public class MarqueeLayout extends FrameLayout {

    private Animation animation;

    public MarqueeLayout(Context context, AttributeSet attrs) {
        super(context, attrs);

        animation = new TranslateAnimation(
                Animation.RELATIVE_TO_SELF, +1f,
                Animation.RELATIVE_TO_SELF, -1f,
                Animation.RELATIVE_TO_SELF, 0f,
                Animation.RELATIVE_TO_SELF, 0f
        );

        animation.setRepeatCount(Animation.INFINITE);
        animation.setRepeatMode(Animation.RESTART);
    }

    public void setDuration(int durationMillis) {
        animation.setDuration(durationMillis);
    }

    public void startAnimation() {
        startAnimation(animation);
    }
}

Layout:

        <MarqueeLayout
            android:id="@+id/marquee_layout"
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <TextView
                android:id="@+id/message_text"
                android:singleLine="true"
                android:textColor="@color/marquee_color"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>

        </MarqueeLayout>

Activity:

MarqueeLayout marqueeLayout = (MarqueeLayout) view.findViewById(R.id.marquee_layout);
marqueeLayout.setDuration(15000);
marqueeLayout.startAnimation();
0

To have the effect of rail of text passing the screen:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">

<translate
    android:fromXDelta="300%"
    android:toXDelta="-100%"
    android:duration="5000"
    android:repeatCount="infinite"
    android:repeatMode="restart"
    />
</set>
Andronicus
  • 23,098
  • 14
  • 38
  • 73