Android:基于速度的 ViewPager 滚动

Android: velocity-based ViewPager scrolling(Android:基于速度的 ViewPager 滚动)

本文介绍了Android:基于速度的 ViewPager 滚动的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

ViewPager 现在滚动的方式是每个手势一个项目.无论是全屏快闪还是慢拖,对挥动手势的处理方式都是一样的;在最后一页只前进了一步.

The way the ViewPager scrolls right now is by one item per gesture. It treats flinging gesture the same way no matter if it's full screen fast fling or slow dragging; at the end page advances one step only.

是否有任何项目或示例可能会添加基于速度的投掷,根据现有投掷的速度滚动多个项目(如果仍在进行中)并在投掷手势又宽又快时进一步滚动?

Is there any projects perhaps or examples that would add velocity-based flinging that scrolls multiple items based on velocity of the existing fling (if it still in progress) and scrolls further if the flinging gesture is wide and fast?

如果没有从哪里开始这样的事情?

And if there's none where to start with something like this?

附:提供赏金.请不要参考 Gallery 或 Horizo​​ntalScrollView 的答案

P.S. The bounty is offered. Please no answers with references to Gallery or HorizontalScrollView

推荐答案

这里的技术是扩展 ViewPager 并模仿分页器在内部执行的大部分操作,再加上 <代码>图库小部件.总体思路是监控弹跳(以及速度和伴随的滚动),然后将它们作为虚假的拖动事件提供给底层的 ViewPager.如果您单独执行此操作,则将无法正常工作(您仍然只能获得一页滚动).发生这种情况是因为假拖动在滚动有效的边界上实现了上限.您可以在扩展的 ViewPager 中模拟计算并检测何时会发生这种情况,然后只需翻页并照常继续.使用假拖动的好处意味着您不必处理捕捉到页面或处理 ViewPager 的边缘.

The technique here is to extends ViewPager and mimic most of what the pager will be doing internally, coupled with scrolling logic from the Gallery widget. The general idea is to monitor the fling (and velocity and accompanying scrolls) and then feed them in as fake drag events to the underlying ViewPager. If you do this alone, it won't work though (you'll still get only one page scroll). This happens because the fake drag implements caps on the bounds that the scroll will be effective. You can mimic the calculations in the extended ViewPager and detect when this will happen, then just flip the page and continue as usual. The benefit of using fake drag means you don't have to deal with snapping to pages or handling the edges of the ViewPager.

我在动画演示示例中测试了以下代码,可从 http://developer.android 下载.com/training/animation/screen-slide.html 通过用这个 VelocityViewPager 替换 ScreenSlideActivity 中的 ViewPager(都在布局 activity_screen_slide 和 Activity 中的字段).

I tested the following code on the animation demos example, downloadable from http://developer.android.com/training/animation/screen-slide.html by replacing the ViewPager in ScreenSlideActivity with this VelocityViewPager (both in the layout activity_screen_slide and the field within the Activity).

/*
 * 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;
}

}

这有一些小问题,可以很容易地解决,但我会留给你,比如如果你滚动(拖动,而不是扔)你可能会在页面之间结束(你会想要捕捉 ACTION_UP 事件).此外,为了做到这一点,触摸事件被完全覆盖,因此您需要在适当的情况下将相关事件提供给底层 ViewPager.

There are a few minor issues with this, which can be resolved easily but I will leave up to you, namely things like if you scroll (dragging, not flinging) you can end up half way between pages (you'll want to snap on the ACTION_UP event). Also, touch events are being completely overridden in order to do this, so you will need to feed relevant events to the underlying ViewPager where appropriate.

这篇关于Android:基于速度的 ViewPager 滚动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:Android:基于速度的 ViewPager 滚动

基础教程推荐