HorizontalScrollView: desplazamiento automático para finalizar cuando se agregan nuevas Vistas.

Tengo un HorizontalScrollView que contiene LinearLayout. En la pantalla, tengo un botón que agregará nuevas vistas a LinearLayout en tiempo de ejecución, y me gustaría que la vista de desplazamiento se desplace hasta el final de la lista cuando se agrega una nueva vista.

Casi lo tengo funcionando, excepto que siempre se desplaza una vista antes de la última vista. Parece que se desplaza sin haber calculado antes la inclusión de la nueva vista.

En mi aplicación, estoy usando un objeto View personalizado, pero hice una pequeña aplicación de prueba que usa ImageView y tiene el mismo síntoma. Probé varias cosas como requestLayout () tanto en el diseño como en ScrollView, probé scrollTo (Integer.MAX_VALUE) y se desplazó al netherverso 🙂 ¿Estoy violando un problema de subproceso de UI o algo así?

  • Almiar

======

public class Main extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button b = (Button) findViewById(R.id.addButton); b.setOnClickListener(new AddListener()); add(); } private void add() { LinearLayout l = (LinearLayout) findViewById(R.id.list); HorizontalScrollView s = (HorizontalScrollView) findViewById(R.id.scroller); ImageView i = new ImageView(getApplicationContext()); i.setImageResource(android.R.drawable.btn_star_big_on); l.addView(i); s.fullScroll(HorizontalScrollView.FOCUS_RIGHT); } private class AddListener implements View.OnClickListener { @Override public void onClick(View v) { add(); } } } 

Layout XML:

        

Creo que hay un problema de tiempo. El diseño no se hace cuando se agrega una vista. Se solicita y se realiza poco tiempo después. Cuando llama a fullScroll inmediatamente después de agregar la vista, el ancho de la distribución lineal no ha tenido la oportunidad de expandirse.

Intenta reemplazar:

 s.fullScroll(HorizontalScrollView.FOCUS_RIGHT); 

con:

 s.postDelayed(new Runnable() { public void run() { s.fullScroll(HorizontalScrollView.FOCUS_RIGHT); } }, 100L); 

La breve demora debe darle al sistema suficiente tiempo para establecerse.

PD: podría ser suficiente simplemente retrasar el desplazamiento hasta después de la iteración actual del ciclo UI. No he probado esta teoría, pero si es correcta, sería suficiente hacer lo siguiente:

 s.post(new Runnable() { public void run() { s.fullScroll(HorizontalScrollView.FOCUS_RIGHT); } }); 

Me gusta esto mejor porque introducir una demora arbitraria me parece una broma (aunque fue mi sugerencia).

Solo otra sugerencia, ya que esta pregunta me ayudó mucho :).

Puedes poner un oyente cuando la vista haya terminado su fase de diseño, y justo después de hacer el desplazamiento completo, aunque necesitarás extender la clase para eso.

Solo hice esto porque quería desplazarme a una sección justo después de onCreate () para evitar el parpadeo desde el punto de inicio al punto de desplazamiento.

Algo como:

 public class PagerView extends HorizontalScrollView { private OnLayoutListener mListener; ///... private interface OnLayoutListener { void onLayout(); } public void fullScrollOnLayout(final int direction) { mListener = new OnLayoutListener() { @Override public void onLayout() { fullScroll(direction) mListener = null; } }; } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); if(mListener != null) mListener.onLayout(); } } 

Estoy de acuerdo con @monxalo y @ granko87 en que el mejor enfoque es con un oyente en lugar de suponer que el diseño estará completo si dejamos transcurrir algún tiempo arbitrario.

En caso de que, como yo, no necesite usar una subclase HorizontalScrollView personalizada, puede agregar OnLayoutChangeListener para mantener las cosas simples:

 mTagsScroller.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { @Override public void onLayoutChange(View view, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { mTagsScroller.removeOnLayoutChangeListener(this); mTagsScroller.fullScroll(View.FOCUS_RIGHT); } }); 

Use el método View smoothScrollTo

 postDelayed(new Runnable() { public void run() { scrollView.smoothScrollTo(newView.getLeft(), newView.getTop()); } }, 100L); 

Debe colocar un ejecutable para que le dé tiempo al proceso de disposición para colocar la nueva Vista

Esto es lo que hice.

 //Observe for a layout change ViewTreeObserver viewTreeObserver = yourLayout.getViewTreeObserver(); if (viewTreeObserver.isAlive()) { viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { yourHorizontalScrollView.fullScroll(HorizontalScrollView.FOCUS_RIGHT); } }); } 

Use el código a continuación para scrollview horizontal { Scroll to right }

 view.post(new Runnable() { @Override public void run() { ((HorizontalScrollView) Iview .findViewById(R.id.hr_scroll)) .fullScroll(HorizontalScrollView.FOCUS_RIGHT); } }); 

Creo que el mejor enfoque es el publicado por monxalo porque no se puede saber cuánto tiempo llevará hasta que se complete el diseño. Lo intenté y funciona perfectamente. La única diferencia es que agregué solo una fila a mi vista de desplazamiento horizontal personalizada:

 @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); this.fullScroll(HorizontalScrollView.FOCUS_RIGHT); }