如何避免TextView在Handler.postDelayed时自动滚动从头开始?
Hey there! Let's dig into why your scrolling TextView resets every time you update the SeekBar with Handler.postDelayed()—and how to fix it.
The core issue here is that updating the SeekBar is likely triggering a layout redraw or state change in your TextView, which resets the default marquee (horizontal scroll) position. Let's walk through practical solutions:
1. Lock Down the TextView's Marquee Configuration
First, make sure your TextView has all the right attributes to maintain its scroll state. Your existing XML was cut off, so here's the complete, stable setup:
<TextView android:id="@+id/titre" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@+id/play" android:textColor="@color/textColor" android:singleLine="true" android:ellipsize="marquee" android:marqueeRepeatLimit="marquee_forever" android:focusable="true" android:focusableInTouchMode="true" android:scrollHorizontally="true" android:freezesText="true" />
The critical lines here:
android:freezesText="true": This tells the TextView to save its current scroll position when it's redrawn, preventing a reset.android:focusable="true"+android:focusableInTouchMode="true": Ensures the TextView retains focus, which the default marquee needs to keep running.
2. Isolate SeekBar Updates From TextView Layout
Double-check your postDelayed logic—make sure you're not accidentally triggering a full layout refresh that affects the TextView. For example:
- Don't call
requestLayout()orinvalidate()on the TextView or its parent view when updating the SeekBar. - If your layout is complex, wrap the TextView in a
FrameLayoutto limit the scope of any redraws caused by the SeekBar.
3. Manually Save and Restore Scroll Position
If the above config doesn't work, you can explicitly track the TextView's scroll offset and reset it after updating the SeekBar:
private int savedScrollOffset = 0; private TextView titreTextView; private Handler updateHandler = new Handler(); private Runnable updateSeekBarTask = new Runnable() { @Override public void run() { // Save current scroll position before updating SeekBar savedScrollOffset = titreTextView.getScrollX(); // Your existing SeekBar update logic seekBar.setProgress(getCurrentMediaProgress()); // Restore the scroll position immediately titreTextView.scrollTo(savedScrollOffset, titreTextView.getScrollY()); // Schedule the next update updateHandler.postDelayed(this, 1000); } };
This acts as a safety net—no matter what triggers a redraw, you force the TextView back to its previous scroll spot.
4. Build a Custom Scrolling TextView (For Full Control)
If the default marquee is too fragile, create a custom TextView with your own scroll logic. This way, layout changes from the SeekBar won't interfere:
public class PersistentScrollTextView extends AppCompatTextView { private Handler scrollHandler = new Handler(); private Runnable scrollRunnable; private int scrollSpeed = 2; // Adjust pixels per frame for speed public PersistentScrollTextView(Context context) { super(context); initScrollLogic(); } public PersistentScrollTextView(Context context, AttributeSet attrs) { super(context, attrs); initScrollLogic(); } private void initScrollLogic() { setSingleLine(true); setEllipsize(null); // Disable default marquee scrollRunnable = new Runnable() { @Override public void run() { scrollBy(scrollSpeed, 0); // Loop back to start when end is reached if (getScrollX() >= getLayout().getLineWidth(0) - getWidth()) { scrollTo(0, 0); } scrollHandler.postDelayed(this, 16); // ~60fps } }; } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); scrollHandler.post(scrollRunnable); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); scrollHandler.removeCallbacks(scrollRunnable); } }
Then use it in your XML like any other TextView—this custom implementation runs independently of system layout changes.
内容的提问来源于stack exchange,提问作者Paul Noiré

