You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在onTouchListener中调用TextView的setText()导致UI卡顿

Fixing UI Jank When Calling TextView.setText() in onTouchEvent

Hey there, let's break down why your touch event handling is causing UI stutter when updating a TextView, and walk through practical fixes to smooth things out.

The Root Cause

Your onTouchEvent runs on the main UI thread, and setText() isn't just a simple string swap—it triggers a chain of expensive UI work: measuring the text's size, relayouting the TextView, and scheduling a full view redraw. When you call this in ACTION_MOVE (which fires dozens of times per second as the user drags), you're flooding the UI thread with redundant tasks. This blocks critical operations like processing touch input or rendering frames, leading to that annoying jank.

Practical Fixes

1. Throttle Updates (Cut Down on Unnecessary Calls)

Instead of updating the TextView every single ACTION_MOVE event, only refresh when the value changes significantly or after a small time interval. This reduces UI workload without making the update feel laggy.

Here's how to adjust your code:

// Add these class-level variables
private float mLastProgress = 0;
private long mLastUpdateTime = 0;
private static final long MIN_UPDATE_INTERVAL = 60; // ~16fps, matches typical screen refresh

@Override 
public boolean onTouchEvent(MotionEvent e) { 
    float center = mScrubberX + mScrubberWidth / 2; 
    float handleLeft = center - HANDLE_WIDTH; 
    float handleRight = center + HANDLE_WIDTH; 

    switch (e.getAction()) { 
        case MotionEvent.ACTION_DOWN: 
            if (e.getX() > handleLeft && e.getX() < handleRight) { 
                mActionDown = true; 
                mActionDownX = e.getX() - mScrubberX; 
            } 
            break; 

        case MotionEvent.ACTION_MOVE: 
            if (mActionDown) {
                float newLeft = e.getX() - mActionDownX; 
                // Calculate your progress value based on the new position (adjust to your logic)
                float currentProgress = calculateProgressFromPosition(newLeft);

                long now = System.currentTimeMillis();
                // Only update if progress changed enough OR enough time has passed
                if (Math.abs(currentProgress - mLastProgress) > 1 || now - mLastUpdateTime > MIN_UPDATE_INTERVAL) {
                    mYourTextView.setText(String.valueOf((int) currentProgress));
                    mLastProgress = currentProgress;
                    mLastUpdateTime = now;
                }
            }
            break;

        // Don't forget to reset state when touch ends
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
            mActionDown = false;
            break;
    }
    return true;
}

// Helper method to map position to your desired progress value
private float calculateProgressFromPosition(float leftPosition) {
    // Example: convert position to 0-100 progress range
    float totalRange = getWidth() - mScrubberWidth;
    return (leftPosition / totalRange) * 100;
}

2. Use Choreographer for Frame-Aligned Updates

If you need ultra-smooth updates, use Choreographer to schedule the text update for the next available screen frame. This ensures the UI update doesn't block touch event processing itself.

// Add these class-level variables
private Choreographer mChoreographer;
private float mPendingProgress;
private boolean mIsUpdateScheduled = false;

// Initialize in your constructor/onCreate
public YourView(Context context) {
    super(context);
    mChoreographer = Choreographer.getInstance();
}

@Override 
public boolean onTouchEvent(MotionEvent e) { 
    // ... (keep your ACTION_DOWN logic)

    case MotionEvent.ACTION_MOVE: 
        if (mActionDown) {
            float newLeft = e.getX() - mActionDownX; 
            mPendingProgress = calculateProgressFromPosition(newLeft);

            // Only schedule one update at a time to avoid duplicates
            if (!mIsUpdateScheduled) {
                mIsUpdateScheduled = true;
                mChoreographer.postFrameCallback(new Choreographer.FrameCallback() {
                    @Override
                    public void doFrame(long frameTimeNanos) {
                        mYourTextView.setText(String.valueOf((int) mPendingProgress));
                        mIsUpdateScheduled = false;
                    }
                });
            }
        }
        break;

    // ... (keep ACTION_UP/CANCEL logic)
}

Why These Work

  • Throttling reduces the number of setText() calls, so the UI thread isn't overwhelmed with repeated layout and redraw tasks.
  • Choreographer aligns the update with the screen's refresh cycle, ensuring the text update happens at the optimal time without blocking touch input.

内容的提问来源于stack exchange,提问作者Siddharth Yadav

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:27:13