android之自定义View和ViewGroup(五)(代码篇,实现类似竖着的ViewPager引导页,竖向引导页)

大家肯定都看到过,很多app我们进入的时候首先是一个引导页左右滑动的那种,一般都是ViewPager实现的,但是有时我们想实现竖着的引导页怎么办呢?以前自己也学习过看过这方面的知识,然后记在了笔记上,不过笔记也是写个大概,为了方便自己回顾,同时也方便大家学习,就写到博客上了(毕竟我说过,要将我电脑上所有的笔记都往博客上移~)。具体的实现效果就是如下图所示:


效果就是上面图片那个了,是不是很像竖着的ViewPager,我们先来想一下思路应该怎么实现吧。。。闭眼,想三分钟,如果是你,你会怎么做?

想好了么~对于新手来说,估计都是懵逼的,我当初刚毕业的时候对这个也是懵逼的~其实说到实现思路,对安卓开发有一定经验的人了应该都大概能想到的:

1.既然是多个图片,那么可能需要的是ViewGroup,因为我们有几个子ImageView,View下不能包含View,所以不能自定义VIew,只能自定义ViewGroup。

2.我们需要滑动图片,而且是手指滑动的同时图片也滑动,并且当我们手指抬起时,需要根据滑动的距离来决定我们是否需要切换到下一个图片。


那么具体实现过程我们接下来一起看看吧:

1.首先我们需要几个属性:

    private int width,height;  //屏幕的宽高,我们需要用于设置图片的大小为一个屏幕的大小
    private int start,end;  //从手指按下到滑动停止,整个过程的起点和终点
    private int last;  //手指最后一次的位置
    private Scroller scroller;  //View滑动的辅助类
2.在构造方法中初始化相关的数据:

    scroller = new Scroller(context);  
    DisplayMetrics dm = new DisplayMetrics();
    WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
    wm.getDefaultDisplay().getMetrics(dm);
    width = dm.widthPixels;
    height = dm.heightPixels;
我们在上述代码中进行了Scroller类的初始化和获取屏幕的宽高并赋值。

3.接下来需要在onMeasure中测量大小,遍历子view然后测量大小:

    /*
    * 重写测量子View的方法
    * */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int count = getChildCount();
        for(int i = 0; i < count; i++){
            View child = getChildAt(i);
            measureChild(child,widthMeasureSpec,heightMeasureSpec);  //测量子View
        }
    }
4.设置子view在父层中的位置:

    /*
    * 重写设定子View的位置的方法
    * */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int count = getChildCount();
        MarginLayoutParams mpl = (MarginLayoutParams) getLayoutParams();
        mpl.height = count * height;   //设置ViewGroup的高度,即子View的高度乘以子View的个数,因为每个子View是充满屏幕的
        setLayoutParams(mpl);
        for(int i = 0; i < count; i++){
            View child = getChildAt(i);
            child.layout(l,i * height,r,((i + 1) * height));  //调用子View的layout方法将位置信息传递过去
        }

    }
5.设置好了子View的位置以及大小后,剩下的就是手势了,触摸事件onTouchEvent:

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        int y = (int) ev.getY();  //手指触碰时在Y方向的位置
        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:   //手指按下时
                last = y;  //手指按下时,最新位置是当前位置y
                start = getScrollY();
                break;
            case MotionEvent.ACTION_MOVE:  //手指滑动时
                if(!scroller.isFinished()){   //如果停止了滚动停止了就终止动画
                    scroller.abortAnimation();
                }
                int yDistance = last - y;  //(滑动差,即上次位置到此次位置,是相对于上次位置)滑动的距离是手指按下的位置 - 当前的位置
                if(getScrollY() < 0){  //总滑动距离(相对于原点)如果小于0,那么滑动距离差就是0
                    yDistance = 0;
                }
                if(getScrollY() > getHeight() - height){   //如果总滑动距离超过能滑动的距离,那么滑动差就是0
                    yDistance = 0;
                }
                scrollBy(0,yDistance);  //执行滑动(跟着手指滑动)
                last = y;
                break;
            case MotionEvent.ACTION_UP:  //手指抬起时
                end = getScrollY();
                int yScrollDistance = end - start;
                if(yScrollDistance > 0){  //大于0说明手指在上滑,比如第一页滑向第二页,向下滚动
                    if(yScrollDistance < height/3){  //滑动距离小于屏幕1/3,复原
                        //复原需要滑动的距离就是手指按下到手指抬起这段距离
                        scroller.startScroll(0,getScrollY(),0, - yScrollDistance);
                    }else {  //大于1/3,滑向下一页,计算思路很简单,滑动的距离相当于先复原再滑动一个屏幕的距离
                        scroller.startScroll(0,getScrollY(),0,-yScrollDistance + height);
                    }
                }else {
                    if(-yScrollDistance < height/3){  //同理如上
                        scroller.startScroll(0,getScrollY(),0,-yScrollDistance);
                    }else {
                        scroller.startScroll(0,getScrollY(),0,-yScrollDistance - height);
                    }
                }
                break;
        }
        postInvalidate();  //刷新
        return true;
    }

6.似乎看着一切都搞定了,但是还差了一个,为了不重复影响scrollto和scrollby别忘了调用computeScroll:
 @Override
    public void computeScroll() {
        super.computeScroll();
        if(scroller.computeScrollOffset()){  //判断是否终止计算
            scrollTo(0,scroller.getCurrY());
            postInvalidate();
        }
    }


其实代码并不多,实现思路也很简单,不过如果想要理解好自定义View,建议大家去看看Scroller和 computeScroll,这两者说着没关系,其实还是有联系的。

so~finish~



发布了33 篇原创文章 · 获赞 49 · 访问量 14万+

猜你喜欢

转载自blog.csdn.net/gsw333/article/details/51943127
今日推荐