android中过度滚动的视觉指示

Posted

技术标签:

【中文标题】android中过度滚动的视觉指示【英文标题】:visual indication of over scroll in android 【发布时间】:2012-06-02 03:59:49 【问题描述】:

我正在尝试添加一些视觉指示,即在 ViewPager 中没有更多页面位于所需的投掷方向。但是,我正在努力寻找放置相关代码的地方。

我尝试使用以下代码扩展 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;
    

【问题讨论】:

【参考方案1】:

如果您查看 v4 支持库,您会看到 ViewPager 使用了一个名为 EdgeEffectCompat 的类(当您到达 ICS+ 中视图寻呼机的开头或结尾时,它会提供发光效果)如果您查看实现在兼容库中,您将看到它有一个 if 语句来查看构建版本是否为 14+ (ICS)。如果是,那么它最终会使用 ICS 中引入的普通 EdgeEffect 类结束(如果您跟踪足够长的时间)。否则它使用 BaseEdgeEffectImpl ,它基本上没有任何内容。

如果您愿意,您可以制作自己的自定义 ViewPager,使用您自己的 EdgeEffect。您可以查看 android 源代码以了解他们如何实现 EdgeEffect here,您几乎可以复制它(只需确保将 AOSP /res/drawable 目录中的 overscroll_edge 和 overscroll_glow drawables 复制到您自己的项目中因为它们是 android 内部的)或继续创建您自己的版本。

祝你好运。

(顺便说一句,这就是他们如何在 ICS 的启动器菜单中创建看起来很酷的边缘倾斜效果......所以你几乎可以尽情发挥创意;)

【讨论】:

这看起来应该这样做!谢谢。【参考方案2】:

我试图获得与此问题中提出的完全相同的效果。我为此苦苦挣扎,然后我阅读了@wna​​fee 的答案(没有它我无法做到)。

但是我很难实现从答案中听起来很简单的东西。 我在实现它时遇到了很多麻烦,以至于我可能没有正确理解答案,但是由于我没有在兼容库的同一个包中工作,所以存在太多无法访问的 API 问题。

在我尝试了一些方法后(没有一个成功,而且它们非常复杂),我转向了一个稍微不同的方向,现在它就像一个魅力。我用了一些反射,没用过的人不用担心,这真的是反射的基础。

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

为了完成这项任务,您只需遵循我的三部分解决方案即可。 (需要 3-10 分钟)

第一部分:

正如 Wnafee 所说,我刚刚通过复制粘贴 here 的源代码制作了自己的 EdgeEffect 类,

(只要确保复制 overscroll_edge 和 overscroll_glow AOSP /res/drawable 目录中的drawables 到你自己的项目 因为它们是 android 内部的)

我只做了 2 个非常小的改动:

    我声明该类扩展了 EdgeEffectCompat(我将我的类称为 EdgeEffectForEarlyVersions)。 public class EdgeEffectForEarlyVersions extends EdgeEffectCompat。进行此更改的原因是 mLeftEdgemRightEdge 的类型为 EdgeEffectCompat。 在“我的”新类的构造函数的第一行,我添加了对父构造函数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. http://***.com/questions/10773565/visual-indication-of-over-scroll-in-android
     * 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 【参考方案3】:

wnafee 很好地解释了解决方案,但对于我们当中的懒惰者,我在很久以前就做了一个实际的工作实现。

https://github.com/inovex/ViewPager3D

如果您只想过度滚动,请看这里:

https://github.com/inovex/ViewPager3D/issues/1

【讨论】:

【参考方案4】:

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

你可以实现:

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) 
    
);

【讨论】:

当您尝试滚动到 ViewPager 的末尾时,会出现系统标准的发光效果,这与滚动到列表底部的顶部时相同。我想要这样的东西 - 但它应该适用于所有版本(当前的发光是 ICS-only)。您的建议会使 UI 变得笨拙,如果这些确实是唯一的选择,那么我将坚持不做任何指示。 我知道这对我的应用程序来说已经足够了,但如果@ekramus 问,我认为是因为他需要更多的东西。 :)【参考方案5】:

只是为了补充@goBeepit 开发人员的答案,当您创建自己的 edgeffect 类并从 EdgeEffectCompat 扩展时,某些方法需要是布尔值。您可以将这些方法更改为布尔类型,然后在任何情况下都返回 true,这样一切正常

【讨论】:

【参考方案6】:

您可以在片段中重载 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 含义,只是它覆盖了一个不打算被覆盖的方法,而不是使用侦听器,这似乎很危险。【参考方案7】:

我已经基于 Renard 的 ViewPager3D 实现了反弹效果:https://***.com/a/17425468/973379

【讨论】:

【参考方案8】:

通常使用 ViewPager,使用 PagerAdapter(例如 FragmentPagerAdapterFragmentStatePagerAdapter)来让 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 上制作像橡皮筋一样的过度滚动效果,至少在 ICS 之前,ViewPager 中都没有这种效果。

以上是关于android中过度滚动的视觉指示的主要内容,如果未能解决你的问题,请参考以下文章

带有自动滚动指示器的 Android 视图寻呼机

android - 过度绘制布局允许通过 LinearLayout 进行触摸

Android两种方式实现横向滚动图标+指示器

如何在nativescript angular中隐藏ios的水平滚动指示器

ScrollView 的过度滚动页眉/页脚?

删除 Jetpack Compose 中的 LazyColumn 过度滚动效果