44 votes

Android: la vitesse à base de ViewPager de défilement

La façon dont le ViewPager parchemins droit maintenant, c'est par un article par le même geste. Il traite jetant geste de la même façon, peu importe si c'est en plein écran rapide jeter ou de lent glissement; à la fin de la page pour avancer d'une étape seulement.

Est-il des projets ou peut-être des exemples qui permettrait d'ajouter de la vitesse en fonction jetant qui permet de faire défiler plusieurs éléments basé sur la vitesse de l'existant fling (si c'est encore en cours) et des parchemins plus loin si le jetant geste est large et rapide?

Et si il n'y a pas par où commencer avec quelque chose comme ça?

P. S. Le bounty est offert. S'il vous plaît pas de réponses avec des références à la Galerie ou HorizontalScrollView

40voto

Dororo Points 2236

La technique ici est-à-étend ViewPager "et imiter la plupart de ce que le pager sera fait en interne, couplé avec le défilement de la logique de l' Gallery widget. L'idée générale est de surveiller l'aventure (et de la vitesse et de l'accompagnement des parchemins) et puis les nourrir de faux événements de glisser sous-jacents ViewPager. Si vous faites cela seul, il ne fonctionnera pas si vous aurez toujours qu'une seule page de défilement). Cela se produit parce que le faux glisser met en œuvre des plafonds sur les limites que le défilement sera efficace. Vous pouvez imiter les calculs dans l'étendue ViewPager et de détecter quand cela va arriver, puis il suffit de tourner la page et continuer comme d'habitude. L'avantage de l'utilisation de faux glisser signifie que vous n'avez pas à traiter avec l'accrochage des pages ou de la manipulation de la bords de l' ViewPager.

J'ai testé le code suivant sur l'animation des démos exemple, téléchargeable à partir de http://developer.android.com/training/animation/screen-slide.html en remplaçant le ViewPager en ScreenSlideActivity cette VelocityViewPager (à la fois dans la mise en page activity_screen_slide et le terrain au sein de l'Activité).

/*
 * Copyright 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 * 
 * Author: Dororo @ StackOverflow
 * An extended ViewPager which implements multiple page flinging.
 * 
 */

package com.example.android.animationsdemo;

import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.GestureDetector;
import android.widget.Scroller;

public class VelocityViewPager extends ViewPager implements GestureDetector.OnGestureListener {

private GestureDetector mGestureDetector;
private FlingRunnable mFlingRunnable = new FlingRunnable();
private boolean mScrolling = false;

public VelocityViewPager(Context context) {
    super(context);
}

public VelocityViewPager(Context context, AttributeSet attrs) {
    super(context, attrs);
    mGestureDetector = new GestureDetector(context, this);
}

// We have to intercept this touch event else fakeDrag functions won't work as it will
// be in a real drag when we want to initialise the fake drag.
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
    return true;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    // give all the events to the gesture detector. I'm returning true here so the viewpager doesn't
    // get any events at all, I'm sure you could adjust this to make that not true.
    mGestureDetector.onTouchEvent(event);
    return true;
}

@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velX, float velY) {
    mFlingRunnable.startUsingVelocity((int)velX);
    return false;
}

private void trackMotion(float distX) {

    // The following mimics the underlying calculations in ViewPager
    float scrollX = getScrollX() - distX;
    final int width = getWidth();
    final int widthWithMargin = width + this.getPageMargin();
    final float leftBound = Math.max(0, (this.getCurrentItem() - 1) * widthWithMargin);
    final float rightBound = Math.min(this.getCurrentItem() + 1, this.getAdapter().getCount() - 1) * widthWithMargin;

    if (scrollX < leftBound) {
        scrollX = leftBound;
        // Now we know that we've hit the bound, flip the page
        if (this.getCurrentItem() > 0) {
            this.setCurrentItem(this.getCurrentItem() - 1, false);
        }
    } 
    else if (scrollX > rightBound) {
        scrollX = rightBound;
        // Now we know that we've hit the bound, flip the page
        if (this.getCurrentItem() < (this.getAdapter().getCount() - 1) ) {
            this.setCurrentItem(this.getCurrentItem() + 1, false);
        }
    }

    // Do the fake dragging
    if (mScrolling) {
        this.fakeDragBy(distX);
    }
    else {
        this.beginFakeDrag();
        this.fakeDragBy(distX);
        mScrolling = true;
    }

}

private void endFlingMotion() {
    mScrolling = false;
    this.endFakeDrag();
}

// The fling runnable which moves the view pager and tracks decay
private class FlingRunnable implements Runnable {
    private Scroller mScroller; // use this to store the points which will be used to create the scroll
    private int mLastFlingX;

    private FlingRunnable() {
        mScroller = new Scroller(getContext());
    }

    public void startUsingVelocity(int initialVel) {
        if (initialVel == 0) {
            // there is no velocity to fling!
            return;
        }

        removeCallbacks(this); // stop pending flings

        int initialX = initialVel < 0 ? Integer.MAX_VALUE : 0;
        mLastFlingX = initialX;
        // setup the scroller to calulate the new x positions based on the initial velocity. Impose no cap on the min/max x values.
        mScroller.fling(initialX, 0, initialVel, 0, 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE);

        post(this);
    }

    private void endFling() {
        mScroller.forceFinished(true);
        endFlingMotion();
    }

    @Override
    public void run() {

        final Scroller scroller = mScroller;
        boolean animationNotFinished = scroller.computeScrollOffset();
        final int x = scroller.getCurrX();
        int delta = x - mLastFlingX;

        trackMotion(delta); 

        if (animationNotFinished) {
            mLastFlingX = x;
            post(this);
        }
        else {
            endFling();
        }

    }
}

@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distX, float distY) {
    trackMotion(-distX);
    return false;
}

    // Unused Gesture Detector functions below

@Override
public boolean onDown(MotionEvent event) {
    return false;
}

@Override
public void onLongPress(MotionEvent event) {
    // we don't want to do anything on a long press, though you should probably feed this to the page being long-pressed.
}

@Override
public void onShowPress(MotionEvent event) {
    // we don't want to show any visual feedback
}

@Override
public boolean onSingleTapUp(MotionEvent event) {
    // we don't want to snap to the next page on a tap so ignore this
    return false;
}

}

Il y a quelques petits problèmes avec cela, qui peut être résolu facilement, mais je vais laisser à vous, à savoir des choses comme si vous faites défiler (en faisant glisser, ne jetant pas), vous pouvez vous retrouver à mi-chemin entre les pages (que vous aurez envie de s'accrocher sur la ACTION_UP de l'événement). Aussi, les événements tactiles sont en train d'être complètement surchargé pour ce faire, vous aurez besoin de les nourrir événements pertinents de la sous - ViewPager le cas échéant.

4voto

dominus Points 123

Une autre option consiste à copier un ensemble ViewPager de la mise en œuvre du code source de la bibliothèque de prise en charge et de personnaliser determineTargetPage(...) méthode. Il est responsable de la détermination de la page pour faire défiler sur fling geste. Cette approche n'est pas super pratique, mais fonctionne assez bien. Voir la mise en œuvre du code ci-dessous:

private int determineTargetPage(int curPage, float pageOffset, int velocity, int dx) {
    int target;
    if (Math.abs(dx) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity) {
        target = calculateFinalPage(curPage, velocity);
    } else {
        final float truncator = curPage >= mCurItem ? 0.4f : 0.6f;
        target = (int) (curPage + pageOffset + truncator);
    }
    if (mItems.size() > 0) {
        final ItemInfo first = mItems.get(0);
        final ItemInfo last = mItems.get(mItems.size() - 1);

        // Only let the user target pages we have items for
        target = Math.max(first.position, Math.min(target, last.position));
    }
    return target;
}

private int calculateFinalPage(int curPage, int velocity) {
    float distance = Math.abs(velocity) * MAX_SETTLE_DURATION / 1000f;
    float normalDistance = (float) Math.sqrt(distance / 2) * 25;
    int step = (int) - Math.signum(velocity);
    int width = getClientWidth();
    int page = curPage;
    for (int i = curPage; i >= 0 && i < mAdapter.getCount(); i += step) {
        float pageWidth = mAdapter.getPageWidth(i);
        float remainingDistance = normalDistance - pageWidth * width;
        if (remainingDistance >= 0) {
            normalDistance = remainingDistance;
        } else {
            page = i;
            break;
        }
    }
    return page;
}

1voto

Leonidos Points 6450

ViewPager est classe à partir de la bibliothèque de prise en charge. Télécharger le support de bibliothèque de code source et le modifier sur 10 lignes de code dans la méthode onTouchEvent pour ajouter des fonctionnalités désirées.

J'ai utiliser la modification de soutien de la bibliothèque dans mes projets depuis environ un an, parce que parfois j'ai besoin de modifier plusieurs lignes de code pour faire un peu de changement ou d'ajouter de nouvelles méthode et je ne veux pas copier les composants de code source. J'utilise une version modifiée de fragments et viewpager.

Mais il y a un problème, vous obtiendrez: une fois dans environ 6 locations vous devez fusionner personnalisé bibliothèque de prise en charge avec une nouvelle version officielle si vous avez besoin de nouvelles fonctionnalités. Et être prudent avec les modifications, vous ne voulez pas casser le soutien de la bibliothèque de classes de compatibilité.

-1voto

hsafarya Points 124

Vous pouvez remplacer le ScrollView ou HorizontalScrollView classe, et d'ajouter que le comportement. Il y a beaucoup de bugs dans la Galerie, et comme je me souviens, c'est obsolète depuis l'api de niveau 14.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X