一款安卓日历(二)

接上篇博客一款安卓日历(一),这篇文章主要介绍日历滑动切换的细节。
项目地址:https://github.com/yannecer/NCalendar

月视图和周视图滑动切换,是自定义的MWCalendar继承LinearLayout,实现了NestedScrollingParent接口,消费了RecyclerView的滑动距离,实现整体上滑,在滑动过程中根据条件判断是否显示周视图WeekCalendar,用OverScroller实现滚动。

关于NestedScrollingParentRecyclerView的嵌套滑动,网上已经有比较详细的资料,这里不再复制粘贴,只讲一下实现细节,我主要参考了Hongyang的照片文章Android NestedScrolling机制完全解析 带你玩转嵌套滑动

MWCalendar

MWCalendar是一个继承LinearLayout,并实现NestedScrollingParent的容器,里面包含了一个月视图MonthCalendar和一个RecyclerViewWeekCalendar是在xml加载完后以参数的方式传进去的,目的是为了使MWCalendarWeekCalendar同在一个RelativeLayout中方便处理切换,RecyclerView向上滑动时,让NestedScrollingParent消耗掉RecyclerView的上滑距离,当向上滑动到只剩一个行高的时候,NestedScrollingParent停止滑动,由RecyclerView继续滑动。这里主要说onStartNestedScroll(),onStopNestedScroll(),onNestedPreScroll()onMeasure()这四个方法。

    @Override
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {

        boolean hiddenMonthCalendar = dy > 0 && getScrollY() < rowHeigh * 5;
        boolean showMonthCalendar = dy < 0 && getScrollY() >= 0 && !ViewCompat.canScrollVertically(target, -1);

        if (hiddenMonthCalendar || showMonthCalendar) {
            scrollBy(0, dy);
            consumed[1] = dy;
        }
    }

这个方法在滑动时调用,用来判断月视图和周视图的切换,dy>0向上滑,且滑动距离getScrollY()<rowHeigh * 5(月视图共6个行高)时,需要月视图向上滑,滑动距离为rowHeigh * 5

    @Override
    public void onStopNestedScroll(View target) {

        int scrollY = getScrollY();
        if (scrollY == 0 || scrollY == rowHeigh * 5) {
            return;
        }

        if (STATE == OPEN) {
            if (scrollY > 100) {
                startScroll(scrollY,rowHeigh * 5 - scrollY,300);
            } else {
                startScroll(scrollY, -scrollY, 300);
            }
        }
        if (STATE == CLOSE) {
            if (scrollY < rowHeigh * 5 - 100) {
                startScroll(scrollY, -scrollY, 300);
            } else {
                startScroll(scrollY, rowHeigh * 5 - scrollY, 300);
            }
        }
    }

    -----------

    private void startScroll(int startY,int dy,int duration) {
        mScroller.startScroll(0, startY, 0, dy, duration);
        invalidate();
    }

这个方法是停止滑动手松开的时候调用,在这个方法中根据状态判断是上滚还是下滚,再根据已滑动的距离getScrollY()和总的滑动距离得到需要利用mScroller滚动的距离。

    @Override
    public void computeScroll() {
        int scrollY = getScrollY();
        if (scrollY == 0) {
            STATE = OPEN;
            weekCalendar.setVisibility(INVISIBLE);
        } else if (scrollY == 5 * rowHeigh) {
            STATE = CLOSE;
            weekCalendar.setVisibility(VISIBLE);
        } else {
            int weekRow = monthCalendar.getCurrentMothView().getWeekRow();
            weekCalendar.setVisibility(scrollY >= weekRow  * rowHeigh ? VISIBLE : INVISIBLE);
        }

        if (mScroller.computeScrollOffset()) {
            scrollTo(0, mScroller.getCurrY());
            invalidate();
        }
    }

computeScroll()方法,在执行mScroller.startScroll()后调用,实时调用并绘制界面,所以可以在这个方法中判断周视图和月视图哪个应该显示。

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        rowHeigh = monthCalendar.getRowHeigh();
        ViewGroup.LayoutParams params = recyclerView.getLayoutParams();
        params.height = getMeasuredHeight() - rowHeigh;
    }

NestedScrollingParent中如果不重写onMeasure()方法,会出现上滑的过程中,下面的部分是空白的,原因是整体View测量的时候,测量的结果适应屏幕的大小,在上滑的过程中并没有重新测量,上滑的时候整体View一起整体上滑,View的高度还是那么高,向上滑出一部分,下面的就变成空白了。所以我们需要做的就是在View测量的时候,给RecyclerView加高,这样上滑的时候也是正常显示,这个加高不是随便加的,必须是停止滑动的时候正好能显示RecyclerView的全部数据,这样就可以计算得到,加高的部分是正常的高度减去上面一行的高度,强行把RecyclerView加高已补充上滑出去的那部分视图。

这就是处理滑动切换视图的主要方法和逻辑,完成项目参见:https://github.com/yannecer/NCalendar

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值