1. Project introduction
1. Background and meaning
In many information, news and enterprise display Android applications,Scroll text playback(also known as marquee effect, bulletin board effect) is a very common UI interaction method, used to continuously display announcements, news titles, prompt information, etc. In scenes such as film and television recommendation apps, subway and bus inquiries, stock market conditions, text scrolling can not only save screen space, but also attract users' attention and make information transmission more intense. Through native Android technology, this project implements a set of high-performance, highly customizable text scrolling playback controls that support multiple scrolling directions and animation curves from scratch to meet various complex needs.
2. Functional Requirements
Text content settings: One or more texts can be dynamically set;
Scroll mode:supportlevel、verticalTwo rolling directions;
Scrolling method:supportcyclePlay andSingle timePlay, supportRound tripandSeamless connection;
Speed and interval: Customizable scroll speed and the stay interval between two scrolls;
Animation curves:built-inLinear、accelerate、slow downEqual interpolation;
Touch interaction: Supports user touch sliding pause and manual drag;
Resource release: Activity/Fragment correctly releases animation and Handler when destroyed to prevent memory leakage;
Customizable styles: Text size, color, font, background, etc. can be dynamically configured through XML attributes or code;
high performance: Maintain a smooth 60FPS in long list and multi-instance scenarios.
3. Technical selection
language:Java
Minimum SDK:API 21(Android 5.0)
-
Core Components:
TextView
Or customView
Attribute animation (
ObjectAnimator
)ValueAnimator
+()
(Advanced Program)Handler
+Runnable
(Basic Plan)Scroller
/OverScroller
(Smooth scrolling)
Layout container: Usually used
FrameLayout
、RelativeLayout
、ConstraintLayout
Hosting custom controlsDevelopment Tools: Android Studio latest stable version
2. Detailed explanation of relevant knowledge
1. Android Custom View Basics
onMeasure(): Measure the width and height of the control;
onSizeChanged(): Size change callback, initialize the drawing area;
onDraw(Canvas): Draw text and background;
Custom properties:pass
res/values/
Definition, can be used in XML;Hardware acceleration: Ensure that the animation is smooth and turn off hardware acceleration for text shadow drawing if necessary.
2. Attribute animation and interpolation
(view, "translationX", start, end)
;(start, end)
,existaddUpdateListener
Update location in ;Commonly used interpolation:
LinearInterpolator
、AccelerateInterpolator
、DecelerateInterpolator
、AccelerateDecelerateInterpolator
;Custom interpolation: Implementation
TimeInterpolator
。
3. Handler and Runnable
Suitable for circular light scheduling;
postDelayed()
Control the scroll interval;Activity / Fragment When destroying
removeCallbacks()
Prevent memory leaks.
4. Scroller / OverScroller
Achieve smooth physical scrolling effect;
()
orfling()
;exist
computeScroll()
, call()
andscrollTo(x, y)
;Suitable for scenes where gesture drag and inertial scrolling are required.
5. TextView and ()
For simple scenarios, you can move directly
TextView
;For higher performance and custom effects, you can
()
middle()
, and pass()
Implement scrolling.
Ideas for project implementation
-
Determine the implementation plan
Plan 1 (Basic): Use a single in the layout
TextView
,passObjectAnimator
orTranslateAnimation
moveTextView
oftranslationX/Y
。Scheme 2 (custom View):Inheritance
View
,existonDraw()
Draw text in and control the offset of text drawing position to achieve more flexible animation and style control.
-
Basic Process
initialization: Read XML attributes or get text content, font, color, speed and other configurations through setters;
Measurement and layout:exist
onMeasure()
Calculate the text width/height and determine the View size;Start the animation:exist
onAttachedToWindow()
orstartScroll()
, start the scrolling animation;Scroll control:use
ValueAnimator
orObjectAnimator
Continuously update the offset of text;Loop and interval: The monitoring animation ends (
AnimatorListener
), in the callbackpostDelayed()
Start again to achieve interval playback;Resource release:exist
onDetachedFromWindow()
Cancel all animations with Handler calls.
-
Multi-directional and multi-mode
Roll horizontally: The initial offset is
viewWidth
, the end point is-textWidth
;Vertical scrolling: The initial offset is
viewHeight
, the end point is-textHeight
;Round trip mode:set up
repeatMode =
;Seamless connection: Use two lines of text to alternately scroll, one line rolls out, and one line follows.
-
Touch Pause and Drag
Rewrite in a custom view
onTouchEvent()
,existACTION_DOWN
hourpause()
Animation,ACTION_MOVE
Adjust the offset whenACTION_UP
hourresume()
orfling()
。
4. Complete integrated version code
4.1
<!-- res/values/ --> <resources> <declare-styleable name="MarqueeTextView"> <attr name="mtv_text" format="string" /> <attr name="mtv_textColor" format="color" /> <attr name="mtv_textSize" format="dimension" /> <attr name="mtv_speed" format="float" /> <attr name="mtv_direction"> <flag name="horizontal" value="0" /> <flag name="vertical" value="1" /> </attr> <attr name="mtv_repeatDelay" format="integer" /> <attr name="mtv_repeatMode"> <enum name="restart" value="1" /> <enum name="reverse" value="2" /> </attr> <attr name="mtvInterpolator" format="reference" /> <attr name="mtv_loop" format="boolean" /> </declare-styleable> </resources>
4.2 Layout Files
<!-- res/layout/activity_main.xml --> <FrameLayout xmlns:andro xmlns:app="/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="16dp"> < android: android:layout_width="match_parent" android:layout_height="wrap_content" app:mtv_text="Welcome to Android text scrolling control" app:mtv_textColor="#FF5722" app:mtv_textSize="18sp" app:mtv_speed="100" app:mtv_direction="horizontal" app:mtv_repeatDelay="500" app:mtv_repeatMode="restart" app:mtvInterpolator="@android:anim/linear_interpolator" app:mtv_loop="true"/> </FrameLayout>
4.3 Custom controls:
package ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; public class MarqueeTextView extends View { // =========== Configurable properties ============ private String text; private int textColor; private float textSize; private float speed; // px/s private int direction; // 0: horizontal, 1: vertical private long repeatDelay; // ms private int repeatMode; // or REVERSE private boolean loop; // Whether to loop private TimeInterpolator interpolator; // ============ Draw related ============= private Paint paint; private float textWidth, textHeight; private float offset; // Current scroll offset // ============ Animation ============== private ObjectAnimator animator; public MarqueeTextView(Context context) { this(context, null); } public MarqueeTextView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MarqueeTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initAttributes(context, attrs); initPaint(); } private void initAttributes(Context context, AttributeSet attrs) { TypedArray a = (attrs, ); text = (.MarqueeTextView_mtv_text); textColor = (.MarqueeTextView_mtv_textColor, 0xFF000000); textSize = (.MarqueeTextView_mtv_textSize, 16 * getResources().getDisplayMetrics().scaledDensity); speed = (.MarqueeTextView_mtv_speed, 50f); direction = (.MarqueeTextView_mtv_direction, 0); repeatDelay = (.MarqueeTextView_mtv_repeatDelay, 500); repeatMode = (.MarqueeTextView_mtv_repeatMode, ); loop = (.MarqueeTextView_mtv_loop, true); int interpRes = (.MarqueeTextView_mtvInterpolator, ); interpolator = (context, interpRes); (); if ((text)) text = ""; } private void initPaint() { paint = new Paint(Paint.ANTI_ALIAS_FLAG); (textColor); (textSize); (); // Calculate text size textWidth = (text); fm = (); textHeight = - ; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int desiredW = (int) (direction == 0 ? getSuggestedMinimumWidth() : textWidth + getPaddingLeft() + getPaddingRight()); int desiredH = (int) (direction == 1 ? getSuggestedMinimumHeight() : textHeight + getPaddingTop() + getPaddingBottom()); int width = resolveSize(desiredW, widthMeasureSpec); int height = resolveSize(desiredH, heightMeasureSpec); setMeasuredDimension(width, height); } @Override protected void onAttachedToWindow() { (); startScroll(); } @Override protected void onDetachedFromWindow() { (); if (animator != null) (); } private void startScroll() { if (animator != null && ()) return; float start, end, distance; if (direction == 0) { // Horizontal scrolling: start from the outside on the right and end from the outside on the left start = getWidth(); end = -textWidth; distance = start - end; } else { // Vertical scrolling: start from outside the bottom and end from outside the top start = getHeight(); end = -textHeight; distance = start - end; } long duration = (long) (distance / speed * 1000); animator = (this, "offset", start, end); (interpolator); (duration); (loop ? : 0); (repeatMode); (repeatDelay); (new () { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); (); } public void setOffset(float value) { = value; invalidate(); } public float getOffset() { return offset; } @Override protected void onDraw(Canvas canvas) { (canvas); if (direction == 0) { // level float y = getPaddingTop() - ().top; (text, offset, y, paint); } else { // Vertical float x = getPaddingLeft(); (text, x, offset - ().top, paint); } } // ==== More APIs can be added: pause(), resume(), setText(), setSpeed(), etc. ====}
5. Code interpretation
-
Custom properties
exist
The text content, color, size, speed, direction, interval, loop mode, interpolation and other attributes are defined;
Pass in the control constructor
TypedArray
Read and initialize.
-
Measurement logic
onMeasure()
Determine the expected width and height of the control according to the scrolling direction;For horizontal scrolling, the width is determined by the parent container and the height is determined by the text height plus the inner margin;
For vertical scrolling and vice versa.
-
Drawing logic
onDraw()
, according to the currentoffset
Draw text;use
()
and()
Calculate the width and height of the text and baseline.
-
Animation logic
startScroll()
In , calculate the distance and duration from the start position to the end position;use
ObjectAnimator
rightoffset
Attributes are used to animation;Set the interpolation, number of cycles, cycle mode and delay;
exist
onDetachedFromWindow()
Cancel the animation to prevent leakage.
-
Scalability
Exposed
setText()
、setSpeed()
、pause()
、resume()
etc.Listen to user touch, supports sliding pause and manual drag;
Connect to RecyclerView and ListView to realize multiple marquee *s in the list.
6. Project Summary and Expansion
-
Project gains
Deeply master the measurement, drawing and attribute animation of custom Views;
Learn to manage animation life cycle gracefully in custom controls;
The core algorithm for mastering the effect of marquee *s: offset calculation and duration conversion;
Learn how to achieve high configurability through XML properties.
-
Performance optimization
Ensure that hardware is accelerated to avoid text drawing lags;
For extra long text or multiple columns, you can use
StaticLayout
Segmented cache;Combined
Choreographer
Accurately control the frame rate;
-
Advanced Development
Touch control: Drag and pause, manually fast forward and rewind;
Multi-line horse-drawn: Supports scrolling multiple lines of text at the same time, or background gradient;
Dynamic data sources: Combined with the network or database, update scrolling content in real time;
Jetpack Compose implementation:based on
Canvas
and()
Compose plan;
The above is the detailed content of the sample code for Android to implement text scrolling effect. For more information about Android text scrolling, please follow my other related articles!