安卓中超出滚动的可视化指示

10

我试图添加一些视觉提示,以表明ViewPager中所需的fling方向没有更多页面了。然而,我很难找到一个适合放置相关代码的位置。

我尝试使用以下代码扩展ViewPager类,但是Toast不显示(ev.getOrientation()总是返回0)。 我还尝试过使用历史点来实现相同的功能,但ev.getHistorySize()也返回0。

我漏掉了什么?

示例代码:

public class CustomViewPager extends ViewPager {

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

    public CustomViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * @see android.support.v4.view.ViewPager#onTouchEvent(android.view.MotionEvent)
     */
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        boolean result = super.onTouchEvent(ev);

        switch (ev.getAction() & MotionEventCompat.ACTION_MASK) {
            case MotionEvent.ACTION_MOVE:
                if (ev.getOrientation() > 0) {
                    Toast.makeText(getContext(), "left", 0).show();
                }
        }

        return result;
    }
}
8个回答

19
如果您查看v4支持库,您会发现ViewPager使用了一个名为EdgeEffectCompat的类(在ICS+中,当您到达视图页面的开头或结尾时,它提供了发光效果)。如果您查看compat库中的实现,您会看到它有一个if语句来检查构建版本是否为14+(ICS)或不是。如果是,那么它最终会(如果您跟踪足够长的时间)使用在ICS中引入的普通EdgeEffect类。否则,它将使用基本的BaseEdgeEffectImpl,其中基本上什么都没有。
如果您想的话,可以制作自己的自定义ViewPager,并使用自己的EdgeEffect。您可以查看Android源代码以了解他们如何实现EdgeEffect here,这样您就可以复制(确保将AOSP / res / drawable目录中的overscroll_edgeoverscroll_glow可绘制对象复制到您自己的项目中,因为它们是Android内部的),或者继续创建自己的版本。
祝好运。
(顺便说一下,在ICS的启动器菜单中创建酷炫的边缘倾斜效果的方法就是这样...所以您可以尽情发挥创意;)

这看起来应该可以做到!谢谢。 - Aaron Dufour

7

我试图达到与此问题中所要求的完全相同的效果。我遇到了困难,然后我阅读了@wnafee的答案(没有它我做不到)。

但是,我努力去实现答案中听起来很简单的东西。我在实现时遇到了很多麻烦,可能是因为我没有正确理解答案,但由于我没有在兼容性库的相同包中工作,所以还存在着许多不可访问API的问题。

在尝试了一些方法之后(其中没有一个成功,而且它们都非常复杂),我走了稍微不同的方向,现在它像魅力般地工作。我使用了一些反射,对于从未使用过反射的人,请不用担心,这真的是反射的基础知识。

我不确定它是否是最好的解决方案,但它适合我,所以如果你想使用它,你可以欢迎使用。请阅读Wnafee的示例,因为它解释了我所做的一些事情。

为了完成这个任务,你只需要按照我的三个部分解决方案即可。(将花费3-10分钟)

第一部分:

如Wnafee所说,我只是通过从这里复制源代码来创建了自己的EdgeEffect类,

(只需确保在AOSP /res/drawable目录中复制overscroll_edge和overscroll_glow可绘制项以供您自己的项目使用,因为它们是Android的内部项)

我只做了两个非常小的更改:

  1. 我声明该类扩展了EdgeEffectCompat(我将我的类称为EdgeEffectForEarlyVersions)。 public class EdgeEffectForEarlyVersions extends EdgeEffectCompat。更改这个的原因是mLeftEdge和mRightEdge的类型是EdgeEffectCompat。
  2. 在“我的”新类的构造函数的第一行,我添加了调用父级构造函数super(context);。由于EdgeEffectCompat没有默认构造函数,因此必须显式调用构造函数。

第二部分:

除此之外,我还编写了另一个函数。该函数的目的是,在早期版本(ICS之前),我们想要使用刚刚复制的EdgeEffectForEarlyVersions。为了达到这个目的,我使用了反射。

这就是该函数:

private static void changeEdgeEffectCompactOnEarlyVersions(ViewPager viewPager, Context context)
{
    /* In case that the version is earlier than 14 there is only empty implementation for the edge effect, therefore we change it.
     * for more information look on the following links:
     * 1. https://dev59.com/mGgv5IYBdhLWcg3wNeDS
     * 2. http://grepcode.com/file/repo1.maven.org/maven2/com.google.android/support-v4/r7/android/support/v4/view/ViewPager.java#ViewPager.0mLeftEdge
     * 3. http://grepcode.com/file/repo1.maven.org/maven2/com.google.android/support-v4/r7/android/support/v4/widget/EdgeEffectCompat.java#EdgeEffectCompat
     */

    if (Build.VERSION.SDK_INT < 14)
    {
        try
        {
            Class<ViewPager> viewPagerClass = ViewPager.class;
            //Get the left edge field, since it is private we used getDeclaredField and not getDeclared 
            Field leftEdge = viewPagerClass.getDeclaredField("mLeftEdge");
            leftEdge.setAccessible(true);
            //Get the right edge field, since it is private we used getDeclaredField and not getDeclared
            Field rightEdge = viewPagerClass.getDeclaredField("mRightEdge");
            rightEdge.setAccessible(true);

            EdgeEffectForEarlyVersions leftEdgeEffect = new EdgeEffectForEarlyVersions(context);
            EdgeEffectForEarlyVersions rightEdgeEffect = new EdgeEffectForEarlyVersions(context);

            //Set the mLeftEdge memeber of viewPager not to be the default one, but to be "our" edgeEffect  
            leftEdge.set(viewPager, leftEdgeEffect);
            //Set the mRightEdge memeber of viewPager not to be the default one, but to be "our" edgeEffect
            rightEdge.set(viewPager, rightEdgeEffect);              
        }
        catch (Exception ex)
        {
            Log.e("refelection", ex.getMessage());
        }

    }
}

第三部分

现在,你只需要在获取ViewPager实例后调用该函数即可,不需要做其他操作。

希望这篇文章能对某些人有所帮助。


这在 API <= 8 上无法工作。(在最新的 rev 11 中)使用 v4 兼容库。因为它使用 BaseViewCompatImpl,当要求超滚动模式时,它总是返回 OVER_SCROLL_NEVER。在 onDraw() 中,它跳过了超滚动的绘制... - Nick

3

1

仅为补充@goBeepit开发人员的答案,当您创建自己的Edgeffect类并从EdgeEffectCompat扩展时,一些方法需要是布尔类型。您可以将这些方法更改为布尔类型,并使它们在任何情况下返回true,这样一切都能正常工作。


1

您有很多选项,可以显示Toast、显示对话框、使TextView或图像出现在您的UI上等等。或者因为您知道ViewPager中的View项数量,您可以在位置0和/或n + 1添加不同的View,并使其反弹到实际包含数据的最后一个View。

您可以实现:

viewPager.setOnPageChangeListener(new OnPageChangeListener() {
    public void onPageSelected(int position) {
        //TODO If position is the 0 or n item, add a view at 0 or at n+1 to indicate there is no more pages with data.
    }

    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        // TODO Show a Toast, View or do anything you want when position = your first/last  item;
    }

    public void onPageScrollStateChanged(int state) {
    }
});

2
当您尝试滚动ViewPager的末尾时,会出现系统标准的发光效果,这与滚动列表顶部或底部时相同。我想要类似的效果,但它应该适用于所有版本(当前的发光效果仅适用于ICS)。您提供的建议会导致用户界面笨拙,如果那些是唯一的选项,那么我将坚持没有指示。 - Aaron Dufour
我知道并且对我的应用来说足够了,但如果@ekramus有所询问,我想他可能需要更多的东西。 :) - Oscar Salguero

0

0
你可以在Fragment中重载setUserVisibleHint(boolean)函数。伪代码:
    void setUserVisibleHint(boolean isVisibleToUser) {
        // If this fragment is becoming visible
        if (isVisibleToUser == true) {
             // Check if it is the last fragment in the viewpager
             if (indexOfThis == getActivity().indexOfLast) {
                // Display right limit reached
                Toast(..., "No more Frags to right",...)
                }
             // Check if it is the first fragment in the viewpager
             else if (indexOfThis == getActivity().indexOfFirst) {
                // Display Left Limit reached
                Toast(..., "No more Frags to left",...)
             }
        }
    }

我没有用过这个函数来实现这个目的,但是我用它来做其他事情,而且它确实能够正常触发。希望这可以帮到你...


这与Oscar的答案具有相同的UI影响,只是它覆盖了一个不应该被覆盖的方法,而不是使用监听器,这似乎很危险。 - Aaron Dufour

-1
通常使用ViewPager时,我们会使用PagerAdapter,例如FragmentPagerAdapter或FragmentStatePagerAdapter来填充ViewPager的内容(您的内容将成为视图)。
现在,当您使用PagerAdapter时,您有一个名为getCount()的方法http://developer.android.com/reference/android/support/v4/view/PagerAdapter.html#getCount%28%29,它将给出内容的大小。
既然您知道了大小,您可以轻松地使用if控制语句显示消息。
尝试这段代码:http://developer.android.com/reference/android/support/v4/view/ViewPager.html 注意:我认为您不需要自定义ViewPager。您还需要了解ViewPager的片段。查看ApiDemos中的示例。这是一个很好的资源。

那不是他所问的。他想要具体知道如何实现类似于iOS上的橡皮筋效果的overscroll-effect,而这在ViewPager中至少在ICS之前是缺失的。 - Fabian Zeindl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接