Search code examples
javaandroidscrolltextviewmarquee

Is there a way to restart marquee/scrolling when the last letter of the text becomes visible?


I want a text inside a textbox to scroll to end and then restart.

Using marquee scrolls the whole text until the first letter reaches the "start point", but I want the text only to scroll until the last letter of the text becomes visible, then pause and the text should jump to start and start scrolling again.

Is there a way to do this. I have search but I can't find something that works for me.

Thanks, Sebi


Solution

  • I have rewrite the Code from the GitHub Project here (which was posted in the comment from Cheticamp) to java and shorten it to my needs. Now it starts the animation infinite.

    Here the java code:

    MarqueeTextView.java:

    public class MarqueeTextView extends androidx.appcompat.widget.AppCompatTextView {
    
    private final Scroller mScroller;
    private int mDelay;
    private int mDuration;
    private final Handler mHandler = new Handler();
    private final Runnable mAnimateRunnable = new Runnable() {
        @Override
        public void run() {
            //check if Animation is needed otherwise check next cycle (if text has changed for example)
            if (getTextViewWidthWithPadding() < getTextWidth()) {
                mScroller.startScroll(getStartX(), 0, 0, 0, 0);
                invalidate();
                int direction = getLayout().getParagraphDirection(0);
                mScroller.startScroll(getStartX(), 0, (getTextWidth() - getTextViewWidthWithPadding()) * direction, 0, mDuration);
            }
            mHandler.postDelayed(mAnimateRunnable, mDelay);
    
        }
    };
    
    public MarqueeTextView(Context context) {
        this(context, null);
    }
    
    public MarqueeTextView(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
        mScroller = new Scroller(context, new LinearInterpolator());
        setScroller(mScroller);
    }
    
    private int getTextWidth() {
        String text = getText().toString();
        TextPaint paint = getPaint();
        Rect bounds = new Rect();
        paint.getTextBounds(text, 0, text.length(), bounds);
        return bounds.width();
    }
    
    private int getTextViewWidthWithPadding() {
        return getWidth() - (getPaddingStart() + getPaddingEnd());
    }
    
    private int getStartX() {
        boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
        int lineRight = (int) getLayout().getLineRight(0);
        if (isRtl) {
            return lineRight - getTextViewWidthWithPadding();
        } else {
            return 0;
        }
    }
    
    public void startAnimation(int delay, int duration) {
        mDelay = delay;
        mDuration = duration;
        mHandler.postDelayed(mAnimateRunnable, delay);
    }
    
    }
    

    activity_main.xml:

    <MarqueeTextView
        android:id="@+id/name"
        android:layout_width="250dp"
        android:layout_height="wrap_content"
        android:ellipsize="none"
        android:singleLine="true"
        android:text="1. Simple text that shows how to use custom marquee"
        />
    

    MainActivty.java:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ((MarqueeTextView)findViewById(R.id.name)).startAnimation(3000, 2000);
    }