J'ai un HorizontalScrollView contenant un LinearLayout. À l'écran, j'ai un bouton qui va ajouter de nouvelles vues à LinearLayout au moment de l'exécution, et j'aimerais que la vue de défilement défile jusqu'à la fin de la liste lorsqu'une nouvelle vue est ajoutée.
J'ai presque le faire fonctionner - sauf qu'il défile toujours une vue avant la dernière. On dirait qu'il défile sans calculer d'abord l'inclusion de la nouvelle vue.
Dans mon application, j'utilise un objet View personnalisé, mais j'ai créé une petite application de test utilisant ImageView et présentant le même symptôme. J'ai essayé diverses choses comme requestLayout () à la fois dans Layout et dans ScrollView, j'ai essayé scrollTo (Integer.MAX_VALUE) et il a fait défiler l'écran vers le bas. :) Suis-je en train de violer un problème de thread d'interface utilisateur?
======
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:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:orientation="vertical"
Android:layout_width="fill_parent"
Android:layout_height="fill_parent">
<HorizontalScrollView
Android:id="@+id/scroller"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:scrollbarSize="50px">
<LinearLayout
Android:id="@+id/list"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:padding="4px"/>
</HorizontalScrollView>
<LinearLayout
Android:layout_width="fill_parent"
Android:layout_height="fill_parent"
Android:layout_gravity="center">
<Button
Android:id="@+id/addButton"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:layout_gravity="center"
Android:paddingLeft="80px"
Android:paddingRight="80px"
Android:paddingTop="40px"
Android:paddingBottom="40px"
Android:text="Add"/>
</LinearLayout>
</LinearLayout>
Je pense qu'il y a un problème de timing. La mise en page n'est pas terminée lorsqu'une vue est ajoutée. Il est demandé et fait peu de temps après. Lorsque vous appelez fullScroll immédiatement après avoir ajouté la vue, la largeur de la représentation linéaire n'a pas eu la possibilité de se développer.
Essayez de remplacer:
s.fullScroll(HorizontalScrollView.FOCUS_RIGHT);
avec:
s.postDelayed(new Runnable() {
public void run() {
s.fullScroll(HorizontalScrollView.FOCUS_RIGHT);
}
}, 100L);
Ce court délai devrait laisser au système suffisamment de temps pour s’installer.
P.S. Il peut suffire de simplement retarder le défilement après l'itération actuelle de la boucle de l'interface utilisateur. Je n'ai pas testé cette théorie, mais si elle est correcte, il suffirait de procéder comme suit:
s.post(new Runnable() {
public void run() {
s.fullScroll(HorizontalScrollView.FOCUS_RIGHT);
}
});
J'aime mieux cela parce qu'introduire un retard arbitraire me semble hacky (même si c'était ma suggestion).
Je conviens avec @monxalo et @ granko87 que la meilleure approche consiste à utiliser un auditeur au lieu de supposer que la mise en page sera complète si on laisse passer un laps de temps arbitraire.
Au cas où, comme moi, vous n’auriez pas besoin d’utiliser une sous-classe HorizontalScrollView personnalisée, vous pouvez simplement ajouter OnLayoutChangeListener pour simplifier les choses:
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);
}
});
Juste une autre suggestion, puisque cette question m'a beaucoup aidé :).
Vous pouvez mettre un auditeur à la fin de la phase de mise en page de la vue, et juste après le fullScroll, vous aurez besoin d'étendre la classe pour cela.
Je ne l'ai fait que parce que je voulais faire défiler une section juste après onCreate () pour éviter le scintillement du point de départ au point de défilement.
Quelque chose comme:
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();
}
}
Utilisez la méthode View smoothScrollTo
postDelayed(new Runnable() {
public void run() {
scrollView.smoothScrollTo(newView.getLeft(), newView.getTop());
}
}, 100L);
Vous devez mettre un exécutable afin de donner le temps au processus de mise en page de positionner la nouvelle vue.
C'est ce que j'ai fait.
//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);
}
});
}
Utilisez le code ci-dessous pour le défilement horizontal { Faites défiler vers la droite }
view.post(new Runnable() {
@Override
public void run() {
((HorizontalScrollView) Iview
.findViewById(R.id.hr_scroll))
.fullScroll(HorizontalScrollView.FOCUS_RIGHT);
}
});
Je pense que la meilleure approche est celle publiée par Monxalo car vous ne pouvez pas savoir combien de temps cela prendra pour que la mise en page soit terminée. J'ai essayé et ça fonctionne parfaitement. La seule différence est que j'ai ajouté une seule ligne à ma vue de défilement horizontal personnalisée:
@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);
}
L'enfant View
ne peut pas être ajouté immédiatement dans une ViewGroup
. Il est donc nécessaire de publier une Runnable
qui sera exécutée après la fin des autres tâches en attente.
Donc, après avoir ajouté View
, utilisez:
horizontalScrollView.post(new Runnable() {
@Override
public void run() {
horizontalScrollView.fullScroll(View.FOCUS_RIGHT);
}
});
Voici mon chemin pour 3 ImageViews dans kotlin:
var p1 = true; var p2 = false; var p3 = false
val x = -55
val handler = Handler()
handler.postDelayed(object : Runnable {
override fun run() {
if (p1){
mainView.picsScrollViewID.smoothScrollTo(mainView.bigPic1ID.x.toInt() + x, 0)
p1 = false
p2 = true
p3 = false
}
else if(p2){
mainView.picsScrollViewID.smoothScrollTo(mainView.bigPic2ID.x.toInt() + x, 0)
p1 = false
p2 = false
p3 = true
}
else if(p3){
mainView.picsScrollViewID.smoothScrollTo(mainView.bigPic3ID.x.toInt() + x, 0)
p1 = true
p2 = false
p3 = false
}
handler.postDelayed(this, 2000)
}
}, 1400)