继承ViewGroup实现Scroll滑动效果

继承ViewGroup实现Scroll滑动效果

extends ViewGroup需要重写onMeasure和onLayout方法

onMeasure方法是去测量ViewGroup需要的大小以及包含的子View需要的大小。

执行完上面的方法后,再执行onLayout方法去设置子View的摆放位置。

实现Scroll滑动效果需要去检测滑动速率,即要知道每个单位时间滑动了多少像素值,根据这个像素值去判断Scroll滑动到下一页还是上一页。

Android为我们提供了VelocityTracker这个类检测速率

使用mVelocityTracker = VelocityTracker.obtain();来初始化

使用mVelocityTracker.addMovement(event);将touch事件添加进去检测。注意每个touch事件都要添加进去

使用mVelocityTracker.computeCurrentVelocity(1000);计算每个单位时间内滑动了多少像素,这里传入的是1000ms即1s。

然后使用float pxsec = mVelocityTracker.getXVelocity();获取到x轴滑动的像素值,必须在执行了上面方法只会再调用。

最后需要mVelocityTracker.recycle();mVelocityTracker = null;回收掉这个对象。

完整代码是:

public class MyScrollLayout extends ViewGroup{

    private int curScreen;
    private int defaultScreen = 0;
    private Scroller mScroller;
    private float mLastMotionX = 0;

    private VelocityTracker mVelocityTracker;

    public MyScrollLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

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

    public MyScrollLayout(Context context) {
        super(context);
        init(context);
    }

    private void init(Context context){
        curScreen = defaultScreen;
        mScroller = new Scroller(context);
    }
    @Override
    public void computeScroll() {
        if(mScroller.computeScrollOffset()){
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
        }
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub
        int action = event.getAction();
        float x = event.getX();
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            if(mVelocityTracker==null){
                mVelocityTracker = VelocityTracker.obtain();
                mVelocityTracker.addMovement(event);
            }
            if(!mScroller.isFinished()){
                mScroller.abortAnimation();
            }
            mLastMotionX = event.getX();
            break;
        case MotionEvent.ACTION_MOVE:
            float delt = mLastMotionX-x;
            if(isCanMove((int)delt)){
            if(mVelocityTracker!=null){
                mVelocityTracker.addMovement(event);
            }
            mLastMotionX = x;
            scrollBy((int)delt, 0);
            }
            break;
        case MotionEvent.ACTION_UP:
            if(mVelocityTracker!=null){
                mVelocityTracker.addMovement(event);
                mVelocityTracker.computeCurrentVelocity(1000);
                float pxsec = mVelocityTracker.getXVelocity();
                if(pxsec>600 && curScreen >0){
                    snapToScreen(curScreen-1);
                }else if(pxsec<-600 && curScreen<getChildCount()-1){
                    snapToScreen(curScreen+1);
                }else{
                    //主要是用来获取该滑动到哪个界面,最终调用的是invalid调用draw方法然后draw调用computeScroll方法,然后使用scroller对象
                    snapToDestination();
                }

                mVelocityTracker.recycle();
                mVelocityTracker = null;
            }
            break;

        default:
            break;
        }

        return true;
    }
    private void snapToScreen(int screen){
        int whichscreen = Math.max(0, Math.min(screen, getChildCount()-1));
        if(getScrollX()!=(whichscreen*getWidth())){
            final int delat = whichscreen*getWidth() - getScrollX();
            mScroller.startScroll(getScrollX(), 0, delat, 0, Math.abs(delat)*2);
            curScreen = whichscreen;
            invalidate();
        }
    }
    private void snapToDestination(){
        int screen = (getScrollX()+getWidth()/2)/getWidth();
        snapToScreen(screen);
    }
    private boolean isCanMove(int delat){
        /*if(getScrollX()<0 && delat<0){
            return false;
        }*/
        if(getScrollX()>=(getChildCount()-1)*getWidth() && delat>0){
            return false;
        }
        return true;
    }
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if(changed){
        int totalHeight = 0;
        int totalwidth = 0;
        int childCount = getChildCount();
        for(int i=0; i<childCount; i++){
            View childView = getChildAt(i);
            int childwidth = childView.getMeasuredWidth();
            int childheight = childView.getMeasuredHeight();
            childView.layout(totalwidth, t, totalwidth+childwidth, b);
            totalHeight += childheight;
            totalwidth += childwidth;
        }
        }
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // TODO Auto-generated method stub
        measureChildren(widthMeasureSpec, heightMeasureSpec);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
    }

很多人会以为ViewGroup的滑动是Scroller的功劳,其实不然,Scroller在这里扮演的角色我认为更像是一个用来计算x和y轴单位时间移动像素的工具类而已,仅此而已没有特别的能力。

真正在这里实现Scroll滑动效果的是ViewGroup里的scrollto和scrollby方法,scrollto是滑动到,scrollby是滑动了。

Scroller.startScroll(getScrollX(), 0, delat, 0, Math.abs(delat)*2);

Scroller这个类的startScroll方法传入了五个参数,分别对应,x轴起滑的偏移像素,y轴起滑的偏移像素,x轴滑动像素,y轴滑动像素,滑动过程需要的时间。

看源码如果不传时间参数的方法有个默认的时间250ms。

scroller.startScroll之后需要调用invalidate方法,然后调用ViewGroup的draw方法,然后调用computeScroll方法,在computeScroll方法里面调用Scroller.computeScrollOffset()方法去判断有没有计算完,没计算完返回true,然后scrollto方法,再postInvalidate();方法重新执行computeScroll方法。

记录下看法,如果有不对的地方求批评指正,谢谢!

时间: 2024-10-14 04:04:07

继承ViewGroup实现Scroll滑动效果的相关文章

android自定义控件系列教程----继承ViewGroup实现带阻力效果的可回弹的SrollView

前沿分析: 我为什么要想实现一个这样的回弹呢?因为android都没有支持回弹效果,只有个oversroll的回弹效果,其他的时候都是edgeeffect效果,当我们在哪个地方需要这样的回弹效果我们就直接把我们的控件往这个SrollVIew里面一扔就可以了.其他的都不用管. 主要用到的类讲解: Scroller,主要来辅助我们记录动画和滑动的类,VelocityTracker用来计算滑动阀值就是快速滑动的辅助类,用到的辅助类就这两个,其他的就是测量和布局还有事件的编写了. 效果图 里面的按钮是我

Android Scroll分析——滑动效果产生

相对于在Android2.x版本上出现的长按.点击事件的效果,不得不说,滑动操作具有更好的用户体验.因此,从Android 4.X版本开始,出现了更多滑动操作的效果.越来越多第三方应用模仿这样的效果,来改善自己的应用.本文,我们刨根问底来探寻滑动效果的产生. 滑动效果如何产生 滑动一个View,其本质就是移动一个View.便是改变它的坐标位置,它的原理与动画产生的效果原理应该是如出一辙的.其最终本质都是改变其坐标.所以,我们要实现View滑动的效果,就只需要监听用户的触摸事件,动态改变View的

Android用TabLayout实现类似网易选项卡动态滑动效果

此前我们用HorizontalScrollView也实现了类似网易选项卡动态滑动效果,详见 Android选项卡动态滑动效果这篇文章 这里我们用TabLayout来实现这一效果.TabLayout是Android Design Support Library库中的控件. Google在2015的IO大会上,给我们带来了更加详细的Material Design设计规范,同时,也给我们带来了全新的Android Design Support Library,在这个support库里面,Google给

十六、Android 滑动效果汇总

Android 滑动效果入门篇(一)-- ViewFlipper Android 滑动效果入门篇(二)-- Gallery Android 滑动效果基础篇(三)-- Gallery仿图像集浏览 Android 滑动效果基础篇(四)-- Gallery + GridView Android 滑动效果进阶篇(五)-- 3D旋转 Android 滑动效果进阶篇(六)-- 倒影效果 ViewFilpper 是Android官方提供的一个View容器类,继承于ViewAnimator类,用于实现页面切换,

android仿系统Launcher界面,实现分屏,左右滑动效果(ViewSwitcher)

ViewSwitcher代表了视图切换组件, 本身继承了FrameLayout ,可以将多个View叠在一起 ,每次只显示一个组件.当程序控制从一个View切换到另个View时,ViewSwitcher 支持指定动画效果. 为了给ViewSwitcher 添加多个组件, 一般通过ViewSwitcher 的setFactory 方法为止设置ViewFactory ,并由ViewFactory为之创建View 即可. 下面通过一个实例来介绍 ViewSwitcher的用法.(仿Android系统L

如何实现微信5.0的滑动效果

作为初学者,虽然深知不可一步登天也应当脚踏实地,但是总会有一些奇思异想想要去实现.在实现第一个app的时候我们遇到的另一个头疼的问题便是如何实现像微信5.0版本过后的滑动效果,查阅官方文档以及借鉴网上大神的经验之后我们也顺利得到了一点思路.虽然我另外一个伙伴已经实现了此功能,但我总觉得有些许的冗杂,经过研究学习之后现在已经得到了优化,详见下文. 1.android.support.v4 google提供了Android Support Library package 系列的包来保证来高版本sdk

ViewPager+Fragmet 实现3D滑动效果

ViewPager+Fragmet 实现3D滑动效果,下面给出程序运行截图: 本项目的目录截图如下: 核心代码如:fragments包里的碎片fragment,和util包里面的对ViewPager的一些定义. dome源码下载地址:(http://download.csdn.net/detail/qq_30000411/9582958) 本dome是基于as2.0下写的项目,下载后导入运行有可能会出现sdk版本问题,希望你能自习该版本bug,as的项目的apk路径 ViewPage+Fragm

[学习总结]3、Android---Scroller类(左右滑动效果常用的类)

参考资料:http://blog.csdn.net/vipzjyno1/article/details/24592591 非常感谢这个兄弟! 在android学习中,动作交互是软件中重要的一部分,其中的Scroller就是提供了拖动效果的类,在网上,比如说一些Launcher实现滑屏都可以通过这个类去实现.. 先来看一下demo的效果:  下载地址 学习Scroller这个类之前你需要学习一下下面的知识: 1.Scroller的2个方法scrollTo()和scrollBy()不了解的点这里 s

基于Android小说阅读器滑动效果的一种实现

看过小说都知道小说阅读器翻页有好多种效果,比如仿真翻页,滑动翻页,等等.由于某种原因,突然想写一个简单点的滑动翻页效果.在这里写出来也没有什么意图,希望大家可以根据这个效果举一反三,写出其他的效果.图就不上了. 下面是代码:大家理解onTouch事件即可 package com.example.testscroll.view; import android.content.Context; import android.util.AttributeSet; import android.view