¿Hay alguna forma de desplazar programáticamente una vista de desplazamiento a un texto de edición específico?

StackOverflow https://stackoverflow.com/questions/6831671

Pregunta

Tengo una actividad muy larga con una vista de desplazamiento. Es un formulario con varios campos que el usuario debe completar. Tengo una casilla de verificación a la mitad de mi formulario, y cuando el usuario lo verifica, quiero desplazarme a una parte específica de la vista. ¿Hay alguna forma de desplazarse a un objeto EditText (o cualquier otro objeto de vista) mediante programación?

Además, sé que esto es posible usando coords X e Y, pero quiero evitar hacerlo, ya que el formulario puede cambiar de usuario a usuario.

¿Fue útil?

Solución

private final void focusOnView(){
        your_scrollview.post(new Runnable() {
            @Override
            public void run() {
                your_scrollview.scrollTo(0, your_EditBox.getBottom());
            }
        });
    }

Otros consejos

La respuesta de Sherif Elkhatib puede mejorarse enormemente, si desea Desplácese la vista al centro de la vista de desplazamiento. Este método reutilizable se desplaza suave la vista al centro visible de un horizontalscrollview.

private final void focusOnView(final HorizontalScrollView scroll, final View view) {
    new Handler().post(new Runnable() {
        @Override
        public void run() {
            int vLeft = view.getLeft();
            int vRight = view.getRight();
            int sWidth = scroll.getWidth();
            scroll.smoothScrollTo(((vLeft + vRight - sWidth) / 2), 0);
        }
    });
}

Para una vertical ScrollView cambiar el x y y posición de las entradas de smoothScroll. Y use view.getTop() en vez de view.getLeft() y view.getBottom() en vez de v.getRight().

:)

Esto funciona bien para mi :

  targetView.getParent().requestChildFocus(targetView,targetView);

Public void requestchildfocus (ver niño, ver enfocado)

niño - El hijo de esta vista que quiere enfoque. Esta vista contendrá la vista enfocada. No es necesariamente la opinión que realmente tiene enfoque.

concentrado - La opinión que es un descendiente del niño que en realidad tiene enfoque

En mi opinión, la mejor manera de desplazarse a un rectángulo dado es a través de View.requestRectangleOnScreen(Rect, Boolean). Deberías llamarlo en un View Desea desplazarse y pasar un rectángulo local en el que desea ser visible en la pantalla. El segundo parámetro debe ser false para desplazamiento suave y true para desplazamiento inmediato.

final Rect rect = new Rect(0, 0, view.getWidth(), view.getHeight());
view.requestRectangleOnScreen(rect, false);

Hice un pequeño método de utilidad basado en la respuesta de Warrenfaith, este código también tiene en cuenta si esa vista ya es visible en el ScrollView, sin necesidad de desplazamiento.

public static void scrollToView(final ScrollView scrollView, final View view) {

    // View needs a focus
    view.requestFocus();

    // Determine if scroll needs to happen
    final Rect scrollBounds = new Rect();
    scrollView.getHitRect(scrollBounds);
    if (!view.getLocalVisibleRect(scrollBounds)) {
        new Handler().post(new Runnable() {
            @Override
            public void run() {
                scrollView.smoothScrollTo(0, view.getBottom());
            }
        });
    } 
}

Deberías hacer tu TextView Enfoque de solicitud:

    mTextView.requestFocus();

Mi edittext estaba anidado varias capas dentro de mi View Scroll, que no es la vista raíz del diseño. Debido a que GetTop () y GetBottom () parecían informar las coordenadas dentro de su vista que contiene, tuve que calcular la distancia desde la parte superior de la vista de desplazamiento hasta la parte superior del edittext iterando a través de los padres del Edittext.

// Scroll the view so that the touched editText is near the top of the scroll view
new Thread(new Runnable()
{
    @Override
    public
    void run ()
    {
        // Make it feel like a two step process
        Utils.sleep(333);

        // Determine where to set the scroll-to to by measuring the distance from the top of the scroll view
        // to the control to focus on by summing the "top" position of each view in the hierarchy.
        int yDistanceToControlsView = 0;
        View parentView = (View) m_editTextControl.getParent();
        while (true)
        {
            if (parentView.equals(scrollView))
            {
                break;
            }
            yDistanceToControlsView += parentView.getTop();
            parentView = (View) parentView.getParent();
        }

        // Compute the final position value for the top and bottom of the control in the scroll view.
        final int topInScrollView = yDistanceToControlsView + m_editTextControl.getTop();
        final int bottomInScrollView = yDistanceToControlsView + m_editTextControl.getBottom();

        // Post the scroll action to happen on the scrollView with the UI thread.
        scrollView.post(new Runnable()
        {
            @Override
            public void run()
            {
                int height =m_editTextControl.getHeight();
                scrollView.smoothScrollTo(0, ((topInScrollView + bottomInScrollView) / 2) - height);
                m_editTextControl.requestFocus();
            }
        });
    }
}).start();

Otra varición sería:

scrollView.postDelayed(new Runnable()
{
    @Override
    public void run()
    {
        scrollView.smoothScrollTo(0, img_transparent.getTop());
    }
}, 2000);

o puedes usar el post() método.

Sé que esto puede ser demasiado tarde para una mejor respuesta, pero una solución perfecta deseada debe ser un sistema como Positioner. Quiero decir, cuando el sistema hace un posicionamiento para un campo de editor, coloca el campo hasta el teclado, por lo que a medida que las reglas UI/UX es perfecta.

Lo que hace el siguiente código es el posicionamiento de Android Way sin problemas. En primer lugar, mantenemos el punto de desplazamiento actual como punto de referencia. La segunda cosa es encontrar el mejor punto de desplazamiento de posicionamiento para un editor, para hacer esto, desplazamos a la parte superior, y luego solicitamos a los campos del editor que hagan que el componente ScrollView haga el mejor posicionamiento. ¡Gatcha! Hemos aprendido la mejor posición. Ahora, lo que haremos es desplazarse sin problemas desde el punto anterior hasta el punto que hemos encontrado recientemente. Si desea, puede omitir el desplazamiento suave usando desplazarse en vez de SmoothScrollto solamente.

NOTA: El principal contenedor ScrollView es un campo miembro llamado ScrollViewSignup, porque mi ejemplo fue una pantalla de registro, como puede resolver mucho.

view.setOnFocusChangeListener(new View.OnFocusChangeListener() {
        @Override
        public void onFocusChange(final View view, boolean b) {
            if (b) {
                scrollViewSignup.post(new Runnable() {
                    @Override
                    public void run() {
                        int scrollY = scrollViewSignup.getScrollY();
                        scrollViewSignup.scrollTo(0, 0);
                        final Rect rect = new Rect(0, 0, view.getWidth(), view.getHeight());
                        view.requestRectangleOnScreen(rect, true);

                        int new_scrollY = scrollViewSignup.getScrollY();
                        scrollViewSignup.scrollTo(0, scrollY);
                        scrollViewSignup.smoothScrollTo(0, new_scrollY);
                    }
                });
            }
        }
    });

Si quieres usar este bloque para todos Editar texto instancias e integrarlo rápidamente con su código de pantalla. Simplemente puede hacer un Traverser como a continuación. Para hacer esto, he hecho que el principal Onfocuschangelistener sea un campo miembro llamado focuschangelistenertoscrolleditor, y llámalo durante la información como se muestra a continuación.

traverseEditTextChildren(scrollViewSignup, focusChangeListenerToScrollEditor);

Y la implementación del método es la siguiente.

private void traverseEditTextChildren(ViewGroup viewGroup, View.OnFocusChangeListener focusChangeListenerToScrollEditor) {
    int childCount = viewGroup.getChildCount();
    for (int i = 0; i < childCount; i++) {
        View view = viewGroup.getChildAt(i);
        if (view instanceof EditText)
        {
            ((EditText) view).setOnFocusChangeListener(focusChangeListenerToScrollEditor);
        }
        else if (view instanceof ViewGroup)
        {
            traverseEditTextChildren((ViewGroup) view, focusChangeListenerToScrollEditor);
        }
    }
}

Entonces, lo que hemos hecho aquí es hacer que todos los niños de EditText Instance llamen al oyente en Focus.

Para llegar a esta solución, lo revisé todas las soluciones aquí y generé una nueva solución para un mejor resultado UI/UX.

Many thanks to all other answers inspiring me much.

referencia : https://stackoverflow.com/a/6438240/2624806

A continuación funcionó mucho mejor.

mObservableScrollView.post(new Runnable() {
            public void run() { 
                mObservableScrollView.fullScroll([View_FOCUS][1]); 
            }
        });

Las respuestas anteriores funcionarán bien si ScrollView es el padre directo de ChildView. Si su visión infantil se está envolviendo en otro grupo de vista en el ScrollView, causará un comportamiento inesperado porque la vista.gettop () obtiene la posición en relación con su padre. En tal caso, debe implementar esto:

public static void scrollToInvalidInputView(ScrollView scrollView, View view) {
    int vTop = view.getTop();

    while (!(view.getParent() instanceof ScrollView)) {
        view = (View) view.getParent();
        vTop += view.getTop();
    }

    final int scrollPosition = vTop;

    new Handler().post(() -> scrollView.smoothScrollTo(0, scrollPosition));
}

Creo que he encontrado una solución más elegante y menos propensa a errores usando

ScrollView.RequestChildrEdrectangeScreen

No hay matemáticas involucradas y, al contrario de otras soluciones propuestas, se encargará de desplazarse correctamente tanto hacia arriba como hacia abajo.

/**
 * Will scroll the {@code scrollView} to make {@code viewToScroll} visible
 * 
 * @param scrollView parent of {@code scrollableContent}
 * @param scrollableContent a child of {@code scrollView} whitch holds the scrollable content (fills the viewport).
 * @param viewToScroll a child of {@code scrollableContent} to whitch will scroll the the {@code scrollView}
 */
void scrollToView(ScrollView scrollView, ViewGroup scrollableContent, View viewToScroll) {
    Rect viewToScrollRect = new Rect(); //coordinates to scroll to
    viewToScroll.getHitRect(viewToScrollRect); //fills viewToScrollRect with coordinates of viewToScroll relative to its parent (LinearLayout) 
    scrollView.requestChildRectangleOnScreen(scrollableContent, viewToScrollRect, false); //ScrollView will make sure, the given viewToScrollRect is visible
}

Es una buena idea envolverlo en postDelayed para hacerlo más confiable, en caso de que el ScrollView se está cambiando en este momento

/**
 * Will scroll the {@code scrollView} to make {@code viewToScroll} visible
 * 
 * @param scrollView parent of {@code scrollableContent}
 * @param scrollableContent a child of {@code scrollView} whitch holds the scrollable content (fills the viewport).
 * @param viewToScroll a child of {@code scrollableContent} to whitch will scroll the the {@code scrollView}
 */
private void scrollToView(final ScrollView scrollView, final ViewGroup scrollableContent, final View viewToScroll) {
    long delay = 100; //delay to let finish with possible modifications to ScrollView
    scrollView.postDelayed(new Runnable() {
        public void run() {
            Rect viewToScrollRect = new Rect(); //coordinates to scroll to
            viewToScroll.getHitRect(viewToScrollRect); //fills viewToScrollRect with coordinates of viewToScroll relative to its parent (LinearLayout) 
            scrollView.requestChildRectangleOnScreen(scrollableContent, viewToScrollRect, false); //ScrollView will make sure, the given viewToScrollRect is visible
        }
    }, delay);
}

Mi solución es:

            int[] spinnerLocation = {0,0};
            spinner.getLocationOnScreen(spinnerLocation);

            int[] scrollLocation = {0, 0};
            scrollView.getLocationInWindow(scrollLocation);

            int y = scrollView.getScrollY();

            scrollView.smoothScrollTo(0, y + spinnerLocation[1] - scrollLocation[1]);

Al examinar el código fuente de Android, puede encontrar que ya hay una función miembro de ScrollViewscrollToChild(View) - Eso hace exactamente lo que se solicita. Desafortunadamente, esta función es por una razón oscura marcada private. Basado en esa función que he escrito siguiente función que encuentra la primera ScrollView sobre el View especificado como un parámetro y se desplaza para que se vuelva visible dentro del ScrollView:

 private void make_visible(View view)
 {
  int vt = view.getTop();
  int vb = view.getBottom();

  View v = view;

  for(;;)
     {
      ViewParent vp = v.getParent();

      if(vp == null || !(vp instanceof ViewGroup))
         break;

      ViewGroup parent = (ViewGroup)vp;

      if(parent instanceof ScrollView)
        {
         ScrollView sv = (ScrollView)parent;

         // Code based on ScrollView.computeScrollDeltaToGetChildRectOnScreen(Rect rect) (Android v5.1.1):

         int height = sv.getHeight();
         int screenTop = sv.getScrollY();
         int screenBottom = screenTop + height;

         int fadingEdge = sv.getVerticalFadingEdgeLength();

         // leave room for top fading edge as long as rect isn't at very top
         if(vt > 0)
            screenTop += fadingEdge;

         // leave room for bottom fading edge as long as rect isn't at very bottom
         if(vb < sv.getChildAt(0).getHeight())
            screenBottom -= fadingEdge;

         int scrollYDelta = 0;

         if(vb > screenBottom && vt > screenTop) 
           {
            // need to move down to get it in view: move down just enough so
            // that the entire rectangle is in view (or at least the first
            // screen size chunk).

            if(vb-vt > height) // just enough to get screen size chunk on
               scrollYDelta += (vt - screenTop);
            else              // get entire rect at bottom of screen
               scrollYDelta += (vb - screenBottom);

             // make sure we aren't scrolling beyond the end of our content
            int bottom = sv.getChildAt(0).getBottom();
            int distanceToBottom = bottom - screenBottom;
            scrollYDelta = Math.min(scrollYDelta, distanceToBottom);
           }
         else if(vt < screenTop && vb < screenBottom) 
           {
            // need to move up to get it in view: move up just enough so that
            // entire rectangle is in view (or at least the first screen
            // size chunk of it).

            if(vb-vt > height)    // screen size chunk
               scrollYDelta -= (screenBottom - vb);
            else                  // entire rect at top
               scrollYDelta -= (screenTop - vt);

            // make sure we aren't scrolling any further than the top our content
            scrollYDelta = Math.max(scrollYDelta, -sv.getScrollY());
           }

         sv.smoothScrollBy(0, scrollYDelta);
         break;
        }

      // Transform coordinates to parent:
      int dy = parent.getTop()-parent.getScrollY();
      vt += dy;
      vb += dy;

      v = parent;
     }
 }

En mi caso, eso no es EditText, ese es googleMap. Y funciona con éxito así.

    private final void focusCenterOnView(final ScrollView scroll, final View view) {
    new Handler().post(new Runnable() {
        @Override
        public void run() {
            int centreX=(int) (view.getX() + view.getWidth()  / 2);
            int centreY= (int) (view.getY() + view.getHeight() / 2);
            scrollView.smoothScrollBy(centreX, centreY);
        }
    });
}

Que: ¿Hay alguna forma de desplazar programáticamente una vista de desplazamiento a un edittext específico?

Respuesta: Vista de desplazamiento anidado en RecyclerView Última posición agregó datos de registro.

adapter.notifyDataSetChanged();
nested_scroll.setScrollY(more Detail Recycler.getBottom());

¿Hay alguna forma de desplazar programáticamente una vista de desplazamiento a un texto de edición específico?

Lo siguiente es lo que estoy usando:

int amountToScroll = viewToShow.getBottom() - scrollView.getHeight() + ((LinearLayout.LayoutParams) viewToShow.getLayoutParams()).bottomMargin;
// Check to see if scrolling is necessary to show the view
if (amountToScroll > 0){
    scrollView.smoothScrollTo(0, amountToScroll);
}

Esto obtiene la cantidad de desplazamiento necesaria para mostrar la parte inferior de la vista, incluido cualquier margen en la parte inferior de esa vista.

Rollo vertical, bueno para formas. La respuesta se basa en el pergamino horizontal de Ahmadalibaloch.

private final void focusOnView(final HorizontalScrollView scroll, final View view) {
    new Handler().post(new Runnable() {
        @Override
        public void run() {
            int top = view.getTop();
            int bottom = view.getBottom();
            int sHeight = scroll.getHeight();
            scroll.smoothScrollTo(0, ((top + bottom - sHeight) / 2));
        }
    });
}

Puedes usar ObjectAnimator como esto:

ObjectAnimator.ofInt(yourScrollView, "scrollY", yourView.getTop()).setDuration(1500).start();

Si ScrlMain es su NestedScrollView, use lo siguiente,

scrlMain.post(new Runnable() {
                    @Override
                    public void run() {
                        scrlMain.fullScroll(View.FOCUS_UP);

                    }
                });
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top