web-dev-qa-db-fra.com

android: smoothScrollToPosition () ne fonctionne pas correctement

J'essaie de faire défiler en douceur vers le dernier élément d'une liste après avoir ajouté un élément à l'adaptateur de tableau associé à la vue de liste. Le problème est qu'il défile simplement vers une position aléatoire

arrayadapter.add(item);
//DOES NOT WORK CORRECTLY:
listview.smoothScrollToPosition(arrayadapter.getCount()-1);

//WORKS JUST FINE:
listview.setSelection(arrayadapter.getCount()-1);
48
user1515520

Vous voudrez probablement dire à ListView de publier le défilement lorsque le thread d'interface utilisateur peut le gérer (c'est pourquoi le vôtre ne défile pas correctement). SmoothScroll a besoin de faire beaucoup de travail, au lieu d'aller simplement à une position ignorant la vitesse/le temps/etc. (requis pour une "animation").

Par conséquent, vous devez faire quelque chose comme:

    getListView().post(new Runnable() {
        @Override
        public void run() {
            getListView().smoothScrollToPosition(pos);
        }
    });
56
Martin Marconcini

(Copié de ma réponse: smoothScrollToPositionFromTop () ne fonctionne pas toujours comme il se doit )

Il s'agit d'un bogue connu. Voir https://code.google.com/p/Android/issues/detail?id=36062

Cependant, j'ai implémenté cette solution de contournement qui traite de tous les cas Edge qui peuvent se produire:

Appelez d'abord smothScrollToPositionFromTop(position) puis, lorsque le défilement est terminé, appelez setSelection(position). Ce dernier appel corrige le défilement incomplet en sautant directement à la position souhaitée. Ce faisant, l'utilisateur a toujours l'impression qu'il fait défiler l'animation vers cette position.

J'ai implémenté cette solution de contournement dans deux méthodes d'assistance:

smoothScrollToPosition ()

public static void smoothScrollToPosition(final AbsListView view, final int position) {
    View child = getChildAtPosition(view, position);
    // There's no need to scroll if child is already at top or view is already scrolled to its end
    if ((child != null) && ((child.getTop() == 0) || ((child.getTop() > 0) && !view.canScrollVertically(1)))) {
        return;
    }

    view.setOnScrollListener(new AbsListView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(final AbsListView view, final int scrollState) {
            if (scrollState == SCROLL_STATE_IDLE) {
                view.setOnScrollListener(null);

                // Fix for scrolling bug
                new Handler().post(new Runnable() {
                    @Override
                    public void run() {
                        view.setSelection(position);
                    }
                });
            }
        }

        @Override
        public void onScroll(final AbsListView view, final int firstVisibleItem, final int visibleItemCount,
                                 final int totalItemCount) { }
    });

    // Perform scrolling to position
    new Handler().post(new Runnable() {
        @Override
        public void run() {
            view.smoothScrollToPositionFromTop(position, 0);
        }
    });
}

getChildAtPosition ()

public static View getChildAtPosition(final AdapterView view, final int position) {
    final int index = position - view.getFirstVisiblePosition();
    if ((index >= 0) && (index < view.getChildCount())) {
        return view.getChildAt(index);
    } else {
        return null;
    }
}
12
Lars Blumberg

Vous devez utiliser la méthode setSelection ().

6
TaoZang

La méthode de sélection des ensembles mentionnée par Lars fonctionne, mais l'animation était trop nerveuse pour nous car elle saute tout ce qui reste. Une autre solution consiste à rappeler la méthode à plusieurs reprises jusqu'à ce que la première position visible soit votre index. Il est préférable de le faire rapidement et avec une limite car cela combattra l'utilisateur en faisant défiler la vue autrement.

private  void DeterminedScrollTo(Android.Widget.ListView listView, int index, int attempts = 0) {
    if (listView.FirstVisiblePosition != index && attempts < 10) {
        attempts++;
        listView.SmoothScrollToPositionFromTop (index, 1, 100);
        listView.PostDelayed (() => {
            DeterminedScrollTo (listView, index, attempts);
        }, 100);
    }
}

La solution est en C # via. Xamarin mais devrait se traduire facilement en Java.

3
Daniel Roberts

Utilisez LayoutManager pour lisser le défilement

layoutManager.smoothScrollToPosition(recyclerView, new RecyclerView.State(), position);
2
Siddhesh Shirodkar
final Handler handler = new Handler();
//100ms wait to scroll to item after applying changes
handler.postDelayed(new Runnable() {
@Override
public void run() {
   listView.smoothScrollToPosition(selectedPosition);
}}, 100);
2
VSB

Appelez-vous arrayadapter.notifyDataSetChanged() après avoir appelé arrayadapter.add()? Pour être sûr aussi, smoothScrollToPosition et setSelection sont des méthodes disponibles dans ListView pas arrayadapter comme vous l'avez mentionné ci-dessus.

Dans tous les cas, voyez si cela aide: smoothScrollToPosition après notifyDataSetChanged ne fonctionne pas sur Android

1
Code Poet