The derived class, named ScrollTextView, provides an effect just like the HTML marquee tag.
Moreover, the scrolling can be restarted, paused and resumed.
Here are the source codes.
The key points are:
- setHorizontallyScrolling() and use android.widget.Scroller to make the text scroll;
- Use the attached TextPaint of the TextView to measure the length of the text in pixel;
- setSingleLine() and setEllipsize(null) to make the text not wrapped and ellipsized;
- Override computeScroll() to restart the scrolling when finished
/*************************************************************************/
package com.dirtybear.android;
import android.content.Context;
import android.graphics.Rect;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.animation.LinearInterpolator;
import android.widget.Scroller;
import android.widget.TextView;
public class ScrollTextView extends TextView {
// scrolling feature
private Scroller mSlr;
// milliseconds for a round of scrolling
private int mRndDuration = 250;
// 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);
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;
}
}
/*************************************************************************/
Issues to be fixed:
- Do not know why it would not scroll sometimes if setHorizontallyScrolling is called in constructor;
- The scrolling is not smooth enough. Maybe it's because of the single thread model of Android UI;
Tested on:
Windows XP
Android SDK 1.0 rc2
Android Emulator 1.0
Android Debug Bridge version 1.0.20
Thanks for the info.
ReplyDeleteBut how can i use this class ( within RemoteViews ) to display scrolling text within TextView on home-screen-widget ?
Problem description:
====================
I have created a widget to be displayed on android emulator's home screen to display some long text. In my main.xml layout file i've already set TextView properties like singleLine="true", ellipsize="marquee", focusable="true" etc
----- main.xml layout file -----------------
----------end of main.xml layout file ----
but still when my widget is displayed on home screen text doesnt move/scroll.
-- My WidgetProvider.java file ----------
package com.android.weatherdata;
import android.util.Log;
import android.widget.RemoteViews;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.app.PendingIntent;
public class WeatherWidget extends AppWidgetProvider{
private static final String TAG = "WeatherWidget";
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
{
Log.d("WEATHER-WIDGET", "onUpdate(): ");
final int N = appWidgetIds.length;
// Perform this loop procedure for each App Widget that belongs to this provider
for (int i=0; i<N; i++) {
int appWidgetId = appWidgetIds[i];
// Create an Intent to launch ExampleActivity
Intent intent = new Intent(context, Weather.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
Log.i("WEATHER-WIDGET", " Create and Attach Text-view click handler ");
// Get the layout for the App Widget and attach an on-click listener to the button
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.main);
views.setOnClickPendingIntent(R.id.widget_textview , pendingIntent);
// Tell the AppWidgetManager to perform an update on the current App Widget
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
}
--- End of WidgetProvider file -------------
In my widget only some part of text is displayed as " Latest Weather Infor" but i would like to write the code in such a way that the complete text should scroll till the complete string/text is displayed.
Please suggest whether i need to change my layout file or what logic (code snippet) i can write/use so that i can scroll text smoothly.
Thanks,
Ravi
Never try my ScrollTextView in Widget.
ReplyDeletePlease tar your codes and send to zx.zhangxiong@gmail.com.
I can have a try.
@xiongzh: Did he send you his code? Could you get it to work?
ReplyDeleteI have the same problem....
ReplyDeleteId like to see scroll text on widget but is not so easy.....
Can anyone help me?
Andrea
You can't use this on a widget full stop.
ReplyDeleteThanks for this! It's a great introduction to scrolling.
ReplyDeleteplaease elaborate how to use this code i am not able see the changes please tell me how to add text to the code......
ReplyDeleteI am glad to apprehend the accomplished agreeable of this blog and am actual aflame and blessed to say that the webmaster has done a actual acceptable job actuality to put all the advice agreeable and advice at one place.
ReplyDeleteWonderful tutorial. Was very helpful. However I want to scroll the text from left to right. How can I do that ?
ReplyDeleteA small fix. Into resumeScroll() method should have
ReplyDeleteif (isFirstTime){
duration = 0;
isFirstTime = false;
}
and the isFistTime will be true on constructor.
In this way we don't need to wait so much time at first time that Marquee show up.
Imagine setRndDuration(40000);
How to increase text size?
ReplyDeleteIt would be very helpful if you could demonstrate it with code sample too :)
ReplyDelete