Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to distinguish end-user scroll of an android ScrollView from programatic scrolling?

Background: I have a ScrollView / TextView pair that receives an intermittent stream of text from an external source. It automatically scrolls to the bottom on each update.

I'd like for the user to be able to break out of that auto scroll down mode by manually scrolling to somewhere, however I am unclear how to distinguish the manual scroll from the programatic scroll I am doing myself.

My UI update runs on a timer to buffer redraws:

private Handler outputUpdater = new Handler ();
private static String outputBuffer = "";
private static boolean outputHasChanged = false;
private static final Object lock = new Object ();

private Runnable outputUpdaterTask = new Runnable () {
    public void run () {
        synchronized (lock) {

            // if the output has changed, update the TextView
            if (outputHasChanged) {
                TextView tv = (TextView) findViewById (R.id.textView);
                tv.setText (outputBuffer);
            }

            // if the output has changed, or the scroll hasn't reached the bottom yet
            // then keep scrolling down
            if (outputHasChanged || !scrollAtBottom ()) {
                ScrollView sv = (ScrollView) findViewById (R.id.scrollView);
                sv.fullScroll (View.FOCUS_DOWN);
            }

            outputHasChanged = false;
        }

        outputUpdater.postDelayed (this, 100);
    }
};

scrollAtBottom gets it's value from a onScrollChanged handler.

This all works fine. It's necessary to call fullScroll even if there was no text update because a single call to fullScroll doesn't always go to the bottom if there are TextView updates going on, or the virtual keypad visibility changed, etc.

I'd like that if the user manually scrolls, that I could know to make a decision to stop calling fullScroll.

Unfortunately It's seems that not enough simply to treat any transition from "at the bottom, automatic mode" to "not at the bottom" as a cue to switch to manual mode because various UI changes seem to move the scroll view off the bottom also (e.g. virtual keypad showing).

Question Restated: How can I distinguish the user initiated scroll from the programatic scroll?

like image 329
idij Avatar asked Apr 19 '13 17:04

idij


1 Answers

Have you tried using a boolean with onTouchEvent, something similar to:

boolean userIntercept = false;
@Override
public boolean onTouchEvent(MotionEvent me) {
    int action = me.getAction();
    if (action == MotionEvent.ACTION_MOVE) {
            userIntercept = true;
    }
    return super.onTouchEvent(me);
}

then in your outputUpdaterTask:

// if the output has changed, or the scroll hasn't reached the bottom yet
// then keep scrolling down
if (outputHasChanged || !scrollAtBottom () && !userIntercept) {
    ScrollView sv = (ScrollView) findViewById (R.id.scrollView);
    sv.fullScroll (View.FOCUS_DOWN);
}

You would just need to determine a way in which you can return the userIntercept to false, whichever best fits your application.

like image 129
jnthnjns Avatar answered Nov 19 '22 01:11

jnthnjns