Seems to be a common problem without a great solution that I have found. Goal is to stop a ScrollView
from auto-scrolling to an EditText
(or any vi
My fix to this most horrific bug, (worth noting that this is pre API11 only where they modified the fling method not to be stupid).
The old fling method finds the next focus that it will get to.. which isn't really that helpful. Other versions of this class don't really work as they stop focus working when the user genuinely traverses the form from the keyboard.
public class NonFocusingScrollView extends ScrollView {
private boolean mBlockRequestFocusOnFling = false;
public NonFocusingScrollView(Context context) {
super(context);
}
public NonFocusingScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NonFocusingScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public ArrayList<View> getFocusables(int direction) {
if(mBlockRequestFocusOnFling)
return new ArrayList<View>();
return super.getFocusables(direction);
}
@Override
public void requestChildFocus(View child, View focused) {
if(!mBlockRequestFocusOnFling)
super.requestChildFocus(child, focused);
}
@Override
public void fling(int velocityY) {
mBlockRequestFocusOnFling = true;
super.fling(velocityY);
mBlockRequestFocusOnFling = false;
}
}
Try this one :)
public class CustomHorizontalScrollView extends HorizontalScrollView {
public CustomHorizontalScrollView(Context context) {
super(context);
}
public CustomHorizontalScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomHorizontalScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
return super.onTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return super.onInterceptTouchEvent(ev);
}
@Override
public void scrollTo(int x, int y) {
super.scrollTo(x, y);
}
//Custom smooth scroll method since norm is final and cannot be overridden
public final void smooothScrollToIfEnabled(int x, int y) {
smoothScrollTo(x, y);
}
@Override
protected int computeScrollDeltaToGetChildRectOnScreen(android.graphics.Rect rect) {
/* if (getContext() != null && getContext() instanceof Activity) {
Activity activity = (Activity) getContext();
if (!activity.isFinishing()) {
View view = activity.getCurrentFocus();
if (view != null) {
if (view instanceof EditText) {
return 0;
}
}
}
}
return super.computeScrollDeltaToGetChildRectOnScreen(rect);
*/
return 0;
}
}
After struggling with that problem for quite some time, I've found a solution that seems to work without being too ugly. First, make sure that whatever ViewGroup
(directly) contains your EditText
has descendantFocusability
set to "Before Descendants," focusable
set to "true" and focusableInTouchMode
set to "true." This will not be the ScrollView
itself, but the layout inside where you have your various views. Next add an onTouchListener
to your ScrollView
that removes focus from the EditText
whenever it is touched, like so:
ScrollView scroll = (ScrollView)findViewById(R.id.scrollView1);
scroll.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (myEditText.hasFocus()) {
myEditText.clearFocus();
}
return false;
}
});
Tell me if that doesn't fix it. What should happen is that the Layout gets focus instead of the EditText
, so no scrolling should happen.
I had a slightly different objection to this infuriating deficiency. Whenever I tapped one of a number of RadioButtons below the EditTexts, the scroll position jumped to accommodate what Android determined to be the visible and focused EditText.
All attempts to retain the current desired scroll position via a Runnable
that issued ScrollView.scrollTo(x,y)
were dutifully IGNORED by Android!
I share my solution in the hope that it may save someone else 8 (eight) wasted hours.
/* This interesting little 'hack' prevents unwanted scroll 'jump' occurring when
user touches a RadioButton for example
[ Causes focus to change - but maybe this is a lesser evil! ] */
mScrollView.setOnTouchListener(new View.OnTouchListener() {
@Override public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() != MotionEvent.ACTION_UP)
return false;
mScrollView.clearFocus();
return false;
}
});
I solved this problem adding the descendantFocusability
attribute to the ScrollView's containing LinearLayout, with the value blocksDescendants
.
For example:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:descendantFocusability="blocksDescendants" >
I was having a similar problem and finally got it to work. My scroll view contains a series of customized buttons, followed by an EditText
(which normally has focus, but I don't want it to be losing focus). Any time the buttons were clicked, the scroll view auto-scrolled to the focused EditText
. Overriding public boolean requestChildRectangleOnScreen(final View child, final Rect rectangle, final boolean immediate)
and always returning false
(default behavior of a ViewGroup
) did the trick. Hope it helps with your situation too.