android - programacion - ¿Hay alguna forma de desplazar de forma programática una vista de desplazamiento a un texto de edición específico?
manual android studio avanzado (16)
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 revisa quiero desplazarme a una parte específica de la vista. ¿Hay alguna manera de desplazarse por un objeto EditText (o cualquier otro objeto de vista) programáticamente?
Además, sé que esto es posible utilizando coordenadas X e Y, pero quiero evitar hacerlo ya que el formulario puede cambiar de usuario a usuario.
Al examinar el código fuente de Android, puede encontrar que ya hay una función miembro de ScrollView
- scrollToChild(View)
- que hace exactamente lo que se solicita. Desafortunadamente, esta función es por alguna razón oscura marcada como private
. Basado en esa función, he escrito la siguiente función que encuentra el primer ScrollView
sobre la View
especificada como un parámetro y lo desplaza para que se vuelva visible en el 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;
}
}
Creo que he encontrado una solución más elegante y propensa a errores usando
No hay matemática involucrada, y contrariamente a otras soluciones propuestas, manejará desplazarse correctamente hacia arriba y hacia abajo en ambos sentidos.
/**
* 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 postDelayed
en postDelayed
para hacerlo más confiable, en caso de que ScrollView
se cambie 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);
}
Debe hacer que su TextView
solicitud de TextView
:
mTextView.requestFocus();
Desplazamiento vertical, bueno para formularios. La respuesta se basa en el desplazamiento horizontal 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));
}
});
}
En mi caso, eso no es EditText
, eso 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);
}
});
}
En mi opinión, la mejor manera de desplazarse a un rectángulo dado es a través de View.requestRectangleOnScreen(Rect, Boolean)
. Debería llamarlo a la View
que desea desplazarse y pasar un rectángulo local que desea que sea 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);
Esto funciona bien para mi :
targetView.getParent().requestChildFocus(targetView,targetView);
public void RequestChildFocus (Ver hijo, Vista enfocada)
child - El hijo de este ViewParent que quiere enfocarse. Esta vista contendrá la vista enfocada. No es necesariamente la vista la que realmente tiene foco.
enfocado - La vista que es un descendiente del niño que realmente tiene enfoque
Hice un pequeño método de utilidad basado en Answer from WarrenFaith, este código también tiene en cuenta si esa vista ya está visible en la vista de desplazamiento, no es necesario desplazarse.
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());
}
});
}
}
La respuesta de Sherif elKhatib puede mejorarse mucho si desea desplazar la vista al centro de la vista de desplazamiento . Este método reutilizable desplaza suavemente 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 un ScrollView
vertical, cambie la posición y
de las entradas de smoothScroll
. Y use view.getTop()
lugar de view.getLeft()
y view.getBottom()
lugar de v.getRight()
.
:)
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.
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]);
My EditText fue anidado varias capas dentro de mi ScrollView, 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, hice que calculara la distancia desde la parte superior de ScrollView hasta la parte superior de EditText al iterar a través de los padres de 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 variante sería:
scrollView.postDelayed(new Runnable()
{
@Override
public void run()
{
scrollView.smoothScrollTo(0, img_transparent.getTop());
}
}, 2000);
o puede usar el método post()
.
Que: ¿Hay alguna forma de desplazar de manera programática una vista de desplazamiento a un texto de edición específico?
Resp: vista de desplazamiento anidada en la última posición de la vista del reciclador agregados datos de registro.
adapter.notifyDataSetChanged();
nested_scroll.setScrollY(more Detail Recycler.getBottom());
referencia: https://.com/a/6438240/2624806
Siguiendo funcionó mucho mejor.
mObservableScrollView.post(new Runnable() {
public void run() {
mObservableScrollView.fullScroll([View_FOCUS][1]);
}
});
private final void focusOnView(){
your_scrollview.post(new Runnable() {
@Override
public void run() {
your_scrollview.scrollTo(0, your_EditBox.getBottom());
}
});
}