web-dev-qa-db-fra.com

Détection du sens de défilement dans l'adaptateur (haut/bas)

J'essaie de reproduire l'application Google Plus dans mon projet, car elle semble être la référence actuelle.

L’effet listview lors du défilement est vraiment agréable et j’aimerais faire quelque chose de similaire.

J'ai commencé avec LayoutAnimationController http://Android-er.blogspot.be/2009/10/listview-and-listactivity-layout.html

LayoutAnimationController controller 
   = AnimationUtils.loadLayoutAnimation(
     this, R.anim.list_layout_controller);
  getListView().setLayoutAnimation(controller);

et cela semble mauvais, car tous les éléments ne sont pas animés:

J'ai donc fini par utiliser le getView de l'adaptateur et utiliser ceci:

        AnimationSet set = new AnimationSet(true);

        Animation animation = new AlphaAnimation(0.0f, 1.0f);
        animation.setDuration(800);
        set.addAnimation(animation);

        animation = new TranslateAnimation(
            Animation.RELATIVE_TO_SELF, 0.0f,Animation.RELATIVE_TO_SELF, 0.0f,
            Animation.RELATIVE_TO_SELF, 1.0f,Animation.RELATIVE_TO_SELF, 0.0f
        );
        animation.setDuration(600);
        set.addAnimation(animation);

        row.startAnimation(set);

Le résultat est génial et j'en suis vraiment content!

Malheureusement, cela ne fonctionne que lorsque je fais défiler de haut en bas de la liste!

Je veux le faire fonctionner en faisant défiler de l'autre côté, je dois changer un peu le TranslateAnimation.

Alors ma question, y at-il un moyen de détecter si je fais défiler vers le haut ou vers le bas dans mon adaptateur?

16
Waza_Be

Assignez un OnScrollListener à votre ListView. Créez un drapeau qui indique si l'utilisateur fait défiler vers le haut ou vers le bas. Définissez une valeur appropriée pour le drapeau en vérifiant si la position actuelle de l'élément visible premier est égale ou supérieure à la position précédente visible de premier. Mettez cette vérification à l'intérieur de onScrollStateChanged().

Exemple de code:

private int mLastFirstVisibleItem;
private boolean mIsScrollingUp;

public void onScrollStateChanged(AbsListView view, int scrollState) {
    final ListView lw = getListView();

    if (view.getId() == lw.getId()) {
        final int currentFirstVisibleItem = lw.getFirstVisiblePosition();

        if (currentFirstVisibleItem > mLastFirstVisibleItem) {
            mIsScrollingUp = false;
        } else if (currentFirstVisibleItem < mLastFirstVisibleItem) {
            mIsScrollingUp = true;
        }

        mLastFirstVisibleItem = currentFirstVisibleItem;
    } 
}

Vérifiez si mIsScrollingUp est vrai ou faux dans getView() et attribuez les animations en conséquence.

22
Wroclai

J'ai fini par faire ceci:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    Log.i("",position+" - "+lastposition);

    if (position >= lastposition)
        animation = new TranslateAnimation(Animation.RELATIVE_TO_SELF,
                0.0f, Animation.RELATIVE_TO_SELF, 0.0f,
                Animation.RELATIVE_TO_SELF, 1.0f,
                Animation.RELATIVE_TO_SELF, 0.0f);
    else
        animation = new TranslateAnimation(Animation.RELATIVE_TO_SELF,
                0.0f, Animation.RELATIVE_TO_SELF, 0.0f,
                Animation.RELATIVE_TO_SELF, -1.0f,
                Animation.RELATIVE_TO_SELF, 0.0f);

    animation.setDuration(600);
    set.addAnimation(animation);

    row.startAnimation(set);

    lastposition = position;

}
18
Waza_Be

Solution plus complexe (travailler avec la hauteur des éléments longs dans listview)

  1. Créer une liste personnalisée

    public class ScrollDetectingListView extends ListView {
        public ScrollDetectingListView(Context context) {
            super(context);
        }
    
        public ScrollDetectingListView(Context context, AttributeSet attrs) {
            super(context,attrs);
        } 
    
        public ScrollDetectingListView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
    
        //we need this protected method for scroll detection
        public int getVerticalScrollOffset() {
            return computeVerticalScrollOffset();
        }
    }
    
  2. Ignorer onScroll

        listView.setOnScrollListener(new AbsListView.OnScrollListener() {
    
        private int mInitialScroll = 0;
    
        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
    
        }
    
        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            int scrolledOffset = listView.getVerticalScrollOffset();
            if (scrolledOffset!=mInitialScroll) {
                //if scroll position changed
                boolean scrollUp = (scrolledOffset - mInitialScroll) < 0;
                mInitialScroll = scrolledOffset;
            }
        }
        });
    
5
recoilme

La réponse acceptée ne "détecte" pas vraiment le défilement vers le haut ou le bas. Cela ne fonctionnera pas si l'élément visible actuel est vraiment énorme. Utiliser onTouchListener est la voie à suivre.

Voici l'extrait de code que j'ai utilisé:

listView.setOnTouchListener(new View.OnTouchListener() {
    float initialY, finalY;
    boolean isScrollingUp;

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        int action = MotionEventCompat.getActionMasked(event);

        switch(action) {
            case (MotionEvent.ACTION_DOWN):
                initialY = event.getY();
            case (MotionEvent.ACTION_UP):
                finalY = event.getY();

                if (initialY < finalY) {
                    Log.d(TAG, "Scrolling up");
                    isScrollingUp = true;
                } else if (initialY > finalY) {
                    Log.d(TAG, "Scrolling down");
                    isScrollingUp = false;
                }
            default:
        }

        if (isScrollingUp) {
            // do animation for scrolling up
        } else {
            // do animation for scrolling down
        }

        return false; // has to be false, or it will freeze the listView
    }
});
5
maverick97

Essaye ça . J'espère que ça t'aide . Logique de @Gal Rom Answer.

lv.setOnScrollListener(new OnScrollListener() {
        private int mLastFirstVisibleItem;

        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {

        }

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

            if(mLastFirstVisibleItem<firstVisibleItem)
            {
                Log.i("SCROLLING DOWN","TRUE");
            }
            if(mLastFirstVisibleItem>firstVisibleItem)
            {
                Log.i("SCROLLING UP","TRUE");
            }
            mLastFirstVisibleItem=firstVisibleItem;

        }
    });
3
IntelliJ Amiya

Voici mon approche: Vous obtenez un retour plus immédiat sur le nombre de fois que vous avez fait défiler: OnScroll, vous pouvez simplement obtenir la position de tête du premier élément de votre liste. Il est assez fiable d’obtenir immédiatement des informations sur la position réelle du défilement. 

listView.getChildAt(0).getTop()
2
Nelson Ramirez
list.setOnScrollListener(new OnScrollListener() {
        int last_item;
        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {

        }

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem,
                int visibleItemCount, int totalItemCount) {
            if(last_item<firstVisibleItem+visibleItemCount-1){
                System.out.println("List is scrolling upwards");
            }
            else if(last_item>firstVisibleItem+visibleItemCount-1){
                System.out.println("List is scrolling downwards");
            }
             last_item = firstVisibleItem+visibleItemCount-1;
        }
    });

En fonction de la position du dernier élément visible, je décide si Listview est en train de monter ou de descendre.

0
Karthik Rk

Solution générale qui ne repose pas sur des positions de vues/etc. Il suffit de vérifier le décalage de défilement vertical et de le comparer au décalage de défilement précédent. Si la nouvelle valeur est supérieure à l'ancienne, l'utilisateur fait défiler l'écran vers le bas, et inversement.

// [START check vertical scroll direction]
int oldScrollOffset = 0;

listView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
        @Override
        public void onScrollChange(View view, int i, int i1, int i2, int i3) {

            Boolean scrollDirectionDown;

            int newScrollOffset = listView.computeVerticalScrollOffset();

            if (newScrollOffset > oldScrollOffset) {
                scrollDirectionDown = true;
            } else {
                scrollDirectionDown = false;
            }

            oldScrollOffset = newScrollOffset;

            if (scrollDirectionDown) {
                // Update accordingly for scrolling down
                Log.d(TAG, "scrolling down");
            } else {
                // Update accordingly for scrolling up
                Log.d(TAG, "scrolling up");
            }

    });
// [END check vertical scroll direction]
0
Damon

J'ai utilisé cette solution beaucoup plus simple: 

public class ScrollDetectingListView extends ListView

...

setOnScrollListener( new OnScrollListener() 
{

    private int mInitialScroll = 0;

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) 
    {
        int scrolledOffset = computeVerticalScrollOffset();

        boolean scrollUp = scrolledOffset > mInitialScroll;
        mInitialScroll = scrolledOffset;
    }


    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {


    }

}
0
Vaiden