Android 那些事 – 欢迎页特效 (上推–推动门效果)

Android 那些事 – 欢迎页特效 (上推–推动门效果)

项目中开发欢迎页,本来是想做一个带有上推门效果的,后来设计说不要了,用了上下滑动的ViewPager,反正demo写出来了,顺便总结下。

在Ata发了,发现csdn也可以用markdown了,顺便粘过来好了,排版都省了。

进入正题

之前在csdn中看到过一个帖子Android特效开发(仿zaker用手向上推动的效果(推动门效果))

帖子中介绍了通过Interpolator来实现弹跳效果。参考之后拿来改造了下,可以实现多个欢迎页的效果。

效果图如下所示【因为之前要求gif不能超过1M,所以删了一些帧,压缩了大小,也没法还原了】:

Android 那些事 – 欢迎页特效 (上推–推动门效果)_第1张图片

原文中有一个bug,上推如果距离比较大(不超过半个屏幕),当前的View是会“砸”回到底部,但是如果是慢慢拖动,慢慢放下,就会在底部留下几个像素的空白,就好像设置了MarginBottom一样,如果快速滑动有可能空白更大如下图:

Android 那些事 – 欢迎页特效 (上推–推动门效果)_第2张图片

测试发现是在计算Touch坐标时忘记了一个场景,假定手指在(40,50)处向上拨动屏幕,移动的最后位置很有可能按着屏幕(40,20)位置上。也就是mDelY的取值问题,当<0的时候可以正常,也就是说只要回到(40,50)坐标之上就可以正常回到屏幕底部,如果一旦回到(40,50)坐标之下就不行了。理论上触摸滑动向上X个单位,向下回来的时候也应该是X个单位,但是在Log中测试发现不一定,有可能向上滑动X个单位,实际返回时滑动Y个单位【这里我也是不解,感觉是UI线程没有来得及处理吧】,总之是有偏差的。

public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            mLastDownY = (int) event.getY();
            return true;
        case MotionEvent.ACTION_MOVE:
            mCurryY = (int) event.getY();
            mDelY = mCurryY - mLastDownY;
            if (mDelY < 0) {//上滑有效,把当前View向上滑动
                scrollTo(0, -mDelY); 
            }
            break;

解决的方法也就一句代码:在处理UP事件时,添加一个分支,即当捕获到UP事件的时候,再执行一下 startBounceAnim(this.getScrollY(), -this.getScrollY(), 450);也就是当松开手指的时候,强制view回到屏幕底部。

然后添加多个页面,同时可以回到上一个页面功能。

在欢迎页面中,手指向下滑动大约1/3时,把上一个页面调出来,修改ACTION_MOVE事件代码:其中getScrollY()==0目的是为了 保证只有当前View在屏幕底部时向下滑动才会出现上一个页面 。如果刚好先向上滑动,再向下滑动也会触发次操作,为了避免冲突,需要判断此时并没有向上滑动过。

case MotionEvent.ACTION_MOVE:
        mCurryY = (int) event.getY();
        mDelY = mCurryY - mLastDownY;
        if (mDelY < 0) {//上滑有效,把当前View向上滑动
            scrollTo(0, -mDelY); 
        }else{//如果向下画的距离超过了1/3,把上一个页面弄出来
            if(this.getScrollY()==0 && mDelY>mScreenHeigh / 3){
                mActivity.showView(false);
            }
        }

computeScroll方法中添加一个关闭当前页面的分支。

public void computeScroll() {
    if (mScroller.computeScrollOffset()) {
        scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
        // 不要忘记更新界面
        postInvalidate();
    } else {
        if (mCloseFlag) {//关闭此页面,
            this.setVisibility(View.GONE);
            mActivity.showView(true);//进入到新的view
        }
    }
}

showView(boolean flag)是Activity中的方法,根据flag标记来判断当前应该下一个页面还是上一个页面,因为所有的View都在Activity中管理。其中mCurrentIndex表示当前页面中为第几个View,mWelcomView为类型为PullDoorView的数组。

public void showView(boolean flag) {
    if (flag) {// 打开下一个页面
        if (mCurrentIndex == 0) {
            ++mCurrentIndex;
        }
        if (mCurrentIndex == mViewSize - 1) {// 已经是最后一个或者第一个,则只需要开启动画即可
            return;
        } else {// 先关闭当前动画,然后再开启新页面动画
            ++mCurrentIndex;
        }
    } else {// 打开上一个页面
        if (mCurrentIndex == 0) {// 已经是第一个,不能再回退了
            return;
        }//如果上一页还有view,则显示上一页的内容,把上一页的view“砸”下来
        mWelcomView[--mCurrentIndex].setVisibility(View.VISIBLE);
        mWelcomView[mCurrentIndex].mCloseFlag = false;// 不需要关闭
        mWelcomView[mCurrentIndex].startBounceAnim( mWelcomView[mCurrentIndex].getScrollY(), -mWelcomView[mCurrentIndex].getScrollY(), 1000);
        }
    }

代码最后贴在了github上。

还是有很多不足,比如说在PullDoorView中调用了Activity中的方法,view做了与自身无关的一些操作,还没想到好的解决方法;当返回上一个页面时,是直接通过执行返回动画,比较生硬;上滑的时候是根据距离计算是否显示下一个页面,如果快速滑动经常无法触发显示下一个页面的动作,应该再加上一个根据滑动的速度来判断等。

你可能感兴趣的:(Android)