ViewPager现在的滚动方式是每个手势一个项目。无论是全屏快速投掷还是慢速拖动,它都以同样的方式对待投掷手势;最后一页只前进一步。
有没有什么项目或者例子可以添加基于速度的投掷,基于现有投掷的速度滚动多个项目(如果它还在进行中),如果投掷手势又宽又快,还可以进一步滚动?
如果没有这样的起点呢?
附言:赏金已经提供。请不要回答画廊或地平线滚动视图的引用
我发现了一个比选中答案更好的实现,当我想停止滚动时,这个ViewPager在触摸时表现更好https://github.com/Benjamin-Dobell/VelocityViewPager
另一个选项是从支持库中复制整个ViewPager
实现源代码,并自定义determiniteragetpage(…)
。它负责在弹出手势时确定滚动到哪个页面。这种方法不是非常方便,但效果很好。请参见下面的实现代码:
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;
}
此处的技术是扩展 ViewPager
并模拟寻呼机在内部将要执行的大部分操作,以及来自“库
”小部件的滚动逻辑。一般的想法是监视甩动(以及速度和伴随的滚动),然后将它们作为假的拖动事件馈送到底层的 ViewPager
。如果你单独这样做,它将无法工作(你仍然只会得到一个页面滚动)。发生这种情况是因为假拖动在滚动将有效的边界上实现上限。您可以模仿扩展的 ViewPager
中的计算并检测何时会发生这种情况,然后只需翻转页面并像往常一样继续。使用假拖动的好处意味着您不必处理与页面对齐或处理 ViewPager
的边缘。
我在动画演示示例中测试了以下代码,可从http://developer.android.com/training/animation/screen-slide.html通过将<code>ScreenSlideActivity</code>中的ViewPager替换为<code>VelocityViewPager</code>(在布局<code>“活动_screen_slide</code>和活动内的字段中)。
/*
* 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
。
我有一个android布局,它有一个,里面有很多元素。在的底部有一个,然后由适配器填充。 我遇到的问题是,android将从中排除,因为已经具有可滚动的功能。我希望与内容一样长,并且主滚动视图是可滚动的。 我怎样才能达到这种行为呢? 下面是我的主要布局: 然后以编程方式将组件添加到具有ID:的linearlayour中。下面是加载到LinearLayout中的一个视图。就是这个给我卷轴带来麻烦的。
感谢您抽出时间阅读此文章。现在我正在制作一个非常基本的基于瓷砖的游戏。地图是大量的16x16分幅,角色图像也是16x16分幅。我的角色有自己的类,它是sprite类的扩展,x和y位置根据平铺位置保存。 要注意,我是相当缺乏经验pyplay。 我的问题是,我计划将角色移动限制在一次一个图块,我不知道如何做到这一点,即使玩家快速按下方向键几十次,(WASD或箭头键)它也只会移动以一定的速度从瓷砖到瓷砖
我一直在研究一个思想,在这个思想中,它保持自动滚动,而不需要用户交互,这一点在使用android API(例如,SmoothScrollTopositionFromTop)时是绝对可行的。 我需要提高流畅度,效率和控制速度
我正在设计一个具有滚动视图的页面,其上方是表格视图(禁用滚动)。为此,我在这个问题中提到了答案 - 使UITableView不可滚动并调整高度以容纳所有单元格,但没有成功。 视图层次结构以及提供的约束- -主视图 -滚动视图< br >固定在主视图的所有边上(0,0,0,0),限制边距 -内容视图 固定到滚动视图(0,0,0,0),与主视图宽度相等,与主视图高度相等(优先级-250) -内容视图中
我有一个水平ScrollView,它有两个元素:CardView和水平RecycerView。所以,当用户水平滚动时,我希望这两个元素滚动。 我想有这样的东西:Goal,橙色的线是CardView,黄色的线是RecycerView。当用户滚动时,两个元素滚动,如下所示:Scrolled Goal。 现在在我的应用程序中,当用户滚动时,只有RecycerView滚动。CardView保持在他的位置。
我有一些子视图的滚动视图。在这个子视图中,我有其他子视图。 我想展示一个固定在这个“嵌套”子视图之一上的popover。 我正确地显示了popover,它被锚定到正确的子视图,但是每当我滚动scrollview时,popover不会随着scrollview移动。每次我水平滚动滚动滚动视图时,我希望我的popover移动并调整其“x”原点。 这是我用来呈现弹出窗口的代码。 有什么帮助吗?谢谢!