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?
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);
}
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 :)
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);
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With