一款安卓日历(二)

来源:互联网 发布:java 多线程 编辑:程序博客网 时间:2024/06/15 23:29

接上篇博客一款安卓日历(一),这篇文章主要介绍日历滑动切换的细节。
项目地址: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

原创粉丝点击