android - programacion - Detener ScrollView del desplazamiento automático a un EditText
manual de programacion android pdf (20)
Parece ser un problema común sin una gran solución que he encontrado. El objetivo es evitar que un ScrollView
desplace automáticamente a un EditText
(o cualquier vista) que tenga el foco.
Tiene un montón de vistas ( Button
s, TextView
s, etc.) en un ScrollView
, una de las cuales es un EditText
. Al hacer clic en decir un botón dentro de ScrollView
, ScrollView
desplaza hacia abajo hasta EditText
(no está en la pantalla). Esto no se desea, ya que hay otros elementos que no desea que se desplacen de la pantalla.
Ahora puedo evitar que esto suceda cuando la pantalla se muestra por primera vez al tener otros elementos enfocables en el ScrollView
. Sin embargo, el problema general todavía existe. El usuario se desplaza hacia abajo manualmente hasta EditText
, ingresa algunos números, luego se desplaza hacia arriba ( EditText
fuera de la pantalla ahora), hace clic en un botón en el ScrollView
y adivina qué? El ScrollView
desplaza hacia abajo a ese maldito EditText
.
Estoy pensando en extender el ScrollView
y anular algunos de los métodos que existen como findFocusableViewInBounds
, pero tengo la sensación de que me estaré metiendo en más problemas.
Por favor ayuda si puedes.
He jugado un poco con cosas como tener un EditText
altura 0 en la parte superior de mi ScrollView
, agregar las propiedades del siguiente elemento enfocable a los otros elementos en el ScrollView
, etc. Supongo que un "truco" podría ser para que el EditText
pierda el foco. Cuando el teclado virtual o manual se oculta o algo así.
A menudo tengo este problema cuando mis aplicaciones manejan el cambio de orientación.
En ese caso utilizo el siguiente tipo de código:
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// to avoid the scrollview to scroll to this element automatically
mEditTextSearch.setFocusable(false);
// Get the saved scroll position
final int scrolly = savedInstanceState.getInt("scrolly");
mScrollView.post(new Runnable() {
@Override
public void run() {
mScrollView.scrollTo(0, scrolly);
// Restore the initial state of the EditText
mEditTextSearch.setFocusable(true);
mEditTextSearch.setFocusableInTouchMode(true);
mEditTextSearch.setClickable(true);
}
});
...
}
Añadiendo 2 parámetros en:
android:focusable="true"
android:focusableInTouchMode="true"
En qué diseño principal está allí.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/layMain"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/background"
android:focusable="true"
android:focusableInTouchMode="true">
Por este EditText
no se enfocará automáticamente.
Aquí esta lo que hice
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" style="@style/measurementTableRowStyle"
android:focusable="true" android:layout_height="fill_parent">
<requestFocus></requestFocus>
<LinearLayout android:id="@+id/linearLayout1"
android:orientation="horizontal" android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView android:id="@+id/desc_text" android:text="Value : "
style="@style/attributeNameTextStyle" android:layout_width="wrap_content"
android:focusable="true" android:layout_height="wrap_content">
<requestFocus></requestFocus>
</TextView>
<TextView style="@style/attributeValueStyle" android:id="@+id/value_text"
android:text="TextView" android:layout_width="wrap_content"
android:layout_height="wrap_content"></TextView>
</LinearLayout>
La razón es que, en tales casos, debe hacer que todas las demás vistas puedan enfocarse dentro de la vista de desplazamiento mediante un android:focusable="true"
explícito android:focusable="true"
y luego <requestFocus></requestFocus>
. Esto debería funcionar siempre IMO
Cree un ScrollView personalizado (cree una clase y haga que extienda HorizontalScrollView) y haga un configurador de obtención para el desplazamiento. Luego, anule computeScrollDeltaToGetChildRectOnScreen.
Cómo funciona: cada vez que Android tiene un texto de edición o algo enfocado que está fuera de la pantalla, llama al método computeScrollDeltaToGetChildRectOnScreen para ponerlo a la vista. Si lo reemplaza y devuelve 0 cuando está desactivado, no se desplazará ...
Así que tendrás una vista de desplazamiento personalizada como esta:
public class TrackableHorizontalScrollView extends HorizontalScrollView {
// true if we can scroll (not locked)
// false if we cannot scroll (locked)
private boolean mScrollable = true;
public TrackableHorizontalScrollView(Context context) {
super(context);
}
public TrackableHorizontalScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TrackableHorizontalScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setScrollingEnabled(boolean enabled) {
mScrollable = enabled;
}
public boolean isScrollable() {
return mScrollable;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
// if we can scroll pass the event to the superclass
if (mScrollable) return super.onTouchEvent(ev);
// only continue to handle the touch event if scrolling enabled
return mScrollable; // mScrollable is always false at this point
default:
return super.onTouchEvent(ev);
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// Don''t do anything with intercepted touch events if
// we are not scrollable
if (!mScrollable) return false;
else return super.onInterceptTouchEvent(ev);
}
@Override
public void scrollTo(int x, int y){
if (!mScrollable) return;
super.scrollTo(x, y);
}
//Custom smooth scroll method since norm is final and cannot be overridden
public final void smooothScrollToIfEnabled(int x, int y){
if (!mScrollable) return;
smoothScrollTo(x, y);
}
@Override
protected int computeScrollDeltaToGetChildRectOnScreen(android.graphics.Rect rect){
if (!mScrollable) return 0;
return super.computeScrollDeltaToGetChildRectOnScreen(rect);
}
}
Puedes usar esto dentro de tu XML de esta manera:
<com.your.package.ui.widget.TrackableHorizontalScrollView
android:id="@+id/wi_et_credit_scroller"
android:layout_toRightOf="@id/wi_et_credit_iv"
android:layout_width="fill_parent"
android:scrollbars="none"
android:layout_height="wrap_content"
android:paddingRight="5dp"
android:layout_gravity="center_vertical">
<!--Whatever you have inside the scrollview-->
</com.your.package.ui.widget.TrackableHorizontalScrollView>
Después de luchar con ese problema durante bastante tiempo, he encontrado una solución que parece funcionar sin ser demasiado fea. En primer lugar, asegúrese de que cualquier grupo de ViewGroup
(directamente) contenga su EditText
tiene la EditText
descendantFocusability
establecida en "Antes de los descendientes", el conjunto focusable
en "verdadero" y el modo de EditText
en "verdadero". Este no será el ScrollView
sí, sino el diseño en el que tiene sus distintas vistas. A continuación, agregue un onTouchListener
a su ScrollView
que elimine el foco del EditText
cada vez que se toque, de esta manera:
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;
}
});
Dime si eso no lo arregla. Lo que debería suceder es que el diseño se centre en lugar del texto de EditText
, por lo que no debería ocurrir ningún desplazamiento.
El problema no está en el código java, sino en el código manifiesto.
En su AndroidManifest.xml agregue un atributo a la Actividad:
<activity android:name=".MyActivity" android:windowSoftInputMode="adjustPan"> </activity>
Estaba teniendo un problema similar y finalmente lo puse a trabajar. Mi vista de desplazamiento contiene una serie de botones personalizados, seguidos de un texto de EditText
(que normalmente tiene el foco, pero no quiero que pierda el foco). Cada vez que se hacía clic en los botones, la vista de desplazamiento se desplazaba automáticamente al EditText
enfocado. La public boolean requestChildRectangleOnScreen(final View child, final Rect rectangle, final boolean immediate)
y siempre devolviendo false
(comportamiento predeterminado de un ViewGroup
de ViewGroup
) hizo el truco. Espero que te ayude con tu situación también.
Hice un proyecto de prueba para experimentar con las distintas soluciones si alguien quiere jugar con él. https://github.com/marchold/EditText-ErrorPopup-Scroll-View
La mejor solución es agregar opciones de enfoque para el niño de su vista de desplazamiento:
android:descendantFocusability="beforeDescendants"
android:focusable="true"
android:focusableInTouchMode="true"
Entonces su archivo xml se verá como:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginRight="50dp"
android:descendantFocusability="beforeDescendants"
android:focusable="true"
android:focusableInTouchMode="true"
android:orientation="vertical" >
<EditText
android:id="@+id/editText_one"
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="TestApp 1" />
<EditText
android:id="@+id/editText_two"
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="TestApp 2" />
<EditText
android:id="@+id/editText_three"
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="TestApp 3" />
</LinearLayout>
</ScrollView>
La respuesta de thomas88wp, https://.com/a/6486348/528746 funcionó para mí. Pero tuve dos problemas: 1. Al desplazarme, quería ocultar el teclado.
2. Tenía muchas vistas de EditText y no quería escribirlas para cada una de ellas.
(Obtuve Actividad () ya que estoy escribiendo esto dentro de un Fragmento y no una actividad)
ScrollView scroll = (ScrollView)view.findViewById(R.id.layout_scroll);
scroll.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// Check if the view with focus is EditText
if (getActivity().getCurrentFocus() instanceof EditText)
{
EditText ed = (EditText)getActivity().getCurrentFocus();
if (ed.hasFocus()) {
// Hide the keyboard
InputMethodManager inputManager = (InputMethodManager)
getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
inputManager.hideSoftInputFromWindow(getActivity().getCurrentFocus().getWindowToken(),
InputMethodManager.HIDE_NOT_ALWAYS);
// Clear the focus
ed.clearFocus();
}
}
return false;
}
});
Mi solución a este error más horrible, (vale la pena señalar que esto es pre API11 solo cuando modificaron el método de lanzamiento para que no fuera estúpido).
El viejo método de lanzamiento encuentra el siguiente enfoque al que llegará ... lo que no es realmente tan útil. Las otras versiones de esta clase no funcionan realmente, ya que dejan de funcionar el enfoque cuando el usuario realmente cruza el formulario desde el teclado.
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;
}
}
Mi solución está a continuación, para rastrear el código fuente y anular alguna función para detener el desplazamiento automático por elemento centrado.
Puede verificar si focusedView
es TextView o si su hijo es TextView, utilizando focusedView.findViewById(R.id.textview_id_you_defined) != null
focusedView instanceof TextView == true
o focusedView instanceof TextView == true
.
public class StopAutoFocusScrollView extends ScrollView {
private View focusedView;
private ScrollMonitorListener listener;
public interface ScrollMonitorListener {
public boolean enableScroll(View view);
}
public StopAutoFocusScrollView(Context context) {
super(context);
}
public StopAutoFocusScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public StopAutoFocusScrollView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
public void setScrollMonitorListener(ScrollMonitorListener listener) {
this.listener = listener;
}
@Override
public void requestChildFocus(View child, View focused) {
focusedView = focused
super.requestChildFocus(child, focused);
}
//flow : requestChildFocus -> scrollToChild -> scrollBy
//Therefore, you can give listener to determine you want scroll to or not
@Override
public void scrollBy(int x, int y) {
if (listener == null || listener.enableScroll(focusedView)) {
super.scrollBy(x, y);
}
}
}
Otra versión del código de thomas88wp:
ScrollView scroll = (ScrollView)getActivity().findViewById(R.id.scrollView_addNewBill);
scroll.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View arg0, MotionEvent arg1) {
View focussedView = getCurrentFocus();
if( focussedView != null ) focussedView.clearFocus();
return false;
}
});
Para mí, no funcionó anular ScrollView onTouch. Tampoco funcionó android: descendantFocusability = "beforeDescendants" android: focusableInTouchMode = "true" Esta y otras soluciones mencionadas solo funcionaron por primera vez, solo cuando EditText no está seleccionado, pero una vez que lo seleccionas, scrollview se vuelve a enrollar automáticamente.
Como ya había escrito un código para ocultar un teclado al tocar otras vistas, solo agregué dos líneas de código y funcionó como un campeón:
public static void setupUI(final Activity activity, final View view) {
//view is the parent view in your layout
OnTouchListener mTouchListener = new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
try {
View vFocused = null;
vFocused = activity.getCurrentFocus();
if (vFocused != null) {
hideSoftKeyboard(activity, v);
if (vFocused instanceof EditText) {
vFocused.clearFocus();//this is the trick to avoid ScrollView autoscroll
}
}
} catch (Exception e) {
}
return false;
}
};
// Set up touch listener for non-text box views to hide keyboard.
if (!(view instanceof EditText) && !(view instanceof ViewGroup)) {
view.setOnTouchListener(mTouchListener);
}
// If a layout container, iterate over children and seed recursion.
if (view instanceof ViewGroup) {
view.setOnTouchListener(mTouchListener);
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
View innerView = ((ViewGroup) view).getChildAt(i);
setupUI(activity, innerView);
}
}
}
public static void hideSoftKeyboard(Context context, View v) {
InputMethodManager inputMethodManager = (InputMethodManager) context
.getSystemService(Activity.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
También agregó esto en la vista raíz:
android:descendantFocusability="beforeDescendants" android:focusableInTouchMode="true"
Tal vez no sea realmente una buena solución, pero está funcionando.
Podemos escribir un ScrollView personalizado y anular el método onScrollChanged , borrar el enfoque de la vista enfocada y, opcionalmente, ocultar el teclado.
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
View v = getFocusedChild();
if (v != null) {
InputMethodManager imm = (InputMethodManager) getContext()
.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
v.clearFocus();
}
super.onScrollChanged(l, t, oldl, oldt);
}
Prueba este :)
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;
}
}
Sólo este código funciona para mí:
public static void preventScrollViewFromScrollingToEdiText(ScrollView view) {
view.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
view.setFocusable(true);
view.setFocusableInTouchMode(true);
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
v.requestFocusFromTouch();
return false;
}
});
}
Todos los créditos van a esta respuesta original .
Solo crea una vista vacía en la parte superior de linearlayout
<View android:layout_width="fill_parent" android:id="@+id/focus_view" android:layout_height="0dp" android:focusable="true" android:focusableInTouchMode="true"><requestFocus/></View>
Una sola línea resuelve el problema
Tuve una objeción ligeramente diferente a esta deficiente exasperación. Cada vez que pulsaba uno de varios RadioButtons debajo de EditTexts, la posición de desplazamiento saltaba para adaptarse a lo que Android determinó que era el EditText visible y enfocado.
Todos los intentos de conservar la posición de desplazamiento actual deseada a través de un Runnable
que emitió ScrollView.scrollTo(x,y)
fueron IGNORADOS debidamente por Android.
Comparto mi solución con la esperanza de que pueda ahorrarle a otra persona 8 (ocho) horas perdidas.
/* 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;
}
});
Yo tuve el mismo problema. Hay un truco que estoy usando para lidiar con este problema:
public void onClick(View v) {
button.requestFocusFromTouch(); //prevents from loosing focus and scrolling view down
....
}