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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With