纵享丝滑滑动切换的周月日历,水滴效果,丰富自定义日历样式,仿小米日历(ViewDragHelper实现)(1)

@Override
public int getViewVerticalDragRange(View child) {
return super.getViewVerticalDragRange(child);
}

@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
}

@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
}
});

  • tryCaptureView():如果返回true,则说明可以捕获该view,我们可以在这里设置捕获的条件
  • clampViewPositionHorizontal ()``clampViewPositionVertical(): 分别对child水平和竖直方向移动的边界进行控制,例如限制周月移动的距离可以在这里做处理
  • onViewPositionChanged() : 当child的位置发生移动时候会回调这个方法
  • onViewReleased():手指释放时候的回调
  • getViewHorizontalDragRange()``getViewVerticalDragRange():返回child横向或者纵向移动的范围,大于0才能捕获。

更多的可以参考鸿洋的Android ViewDragHelper完全解析 自定义ViewGroup神器

如何实现

既然选择ViewDragHelper要实现周月联动呢,我们来理一理要实现的效果,在月视图的时候,能够把下面的recyclerView上移拖到到周视图的高度,上移过程如果超过一定距离就默认滚动到周视图。 在周视图的的时候又能把recyclerView下移拖动到月视图的高度位置,下移过程如果超过一定距离就默认滚动到月视图。

整体分析

整个页面是由顶部的周名字的View、周模式的MaterialCalendarView、月模式的MaterialCalendarView和最下面的recyclerView组成 需要注意的是MaterialCalendarView 这个库原来是有周名字还有顶部显示日期的, 需要注意的是这里稍微做了下修改把这些给隐藏掉了,具体可以看MaterialCalendarView.setTopbarVisible()。并且做了下修改增加了获得单行的高度方法MaterialCalendarView.getItemHeight() ,即为周模式时显示的高度。

具体实现

  • 拖动前处理 整个页面只有recyclerView ,月模式下如果向上拖动时候如果recyclerView不是滚动到了顶部的话那么就不允许拖动,相关代码

@Override
public boolean tryCaptureView(View child, int pointerId) {
return !mDragHelper.continueSettling(true)
&&child == mRecyclerView && !animatStart
&& isAtTop(mRecyclerView) &&
!ViewCompat.canScrollVertically(mRecyclerView, -1);
}

  • 限制recyclerView移动的高度在周模式和月模式之间

@Override
public int clampViewPositionVertical(View child, int top, int dy) {
//决定竖直方向上能移动的距离为 finalWeekModeHeight到finalMonthModeHeight
int topBound = finalWeekModeHeight;
int bottomBound = finalMonthModeHeight;
int newTop = Math.min(Math.max(top, topBound), bottomBound);
return newTop;
}

  • onMeasure获得初始的一些数据值,包括周模式的高度,月模式的高度,最大移动的距离,单行的高度

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
calendarItemHight = mCalendarViewMonth.getItemHeight();
calendarWeekHight = calendarItemHight;
if (defaultStopHeight == 0) {
defaultStopHeight = getCurrentItemPosition(CalendarDay.today()) * calendarItemHight;
}
calendarMonthHight = mCalendarViewMonth.getMeasuredHeight();
weekViewHight = mTopWeekView.getMeasuredHeight();
finalMonthModeHeight = weekViewHight + calendarMonthHight;
finalWeekModeHeight = calendarItemHight + weekViewHight;
maxOffset = calendarMonthHight - calendarItemHight;
}

  • 然后在onlayout()把布局里的View绘制到对应的位置上面

  • 最大移动的距离defaultStopHeight在选中日期时候就会通过 getCurrentItemPosition()计算出它点击所在的行数再调用setStopItemPosition()就可以得到要停止下来的高度,

  • 接下来说下最关键的地方 既然是周月联动我们发现在拖动recyclerView视图的时候我们会不停回调onViewPositionChanged()这个方法,我们在这个方法里面就可以根据recyclerView移动的距离来移动对应的月视图,

//滑动处理
private void HandlerOffset(View changedView, int left, int top, int dx, int dy) {
//获取日历相对手指移动的相对距离 dy向上移动小于0
transY = transY + dy;
if (transY > 0) {
transY = 0;
}
if (transY < -calendarMonthHight - calendarItemHight) {
transY = -calendarMonthHight - calendarItemHight;
}

float abstransY = Math.abs(transY);
if (dy < 0) {
//如果上滑动,并且滑向动的绝对值距离在超过calendarHight-defaultStopHeight
// 并且小于可以滑动的距离calendarHight-calendarItemHight之间的话
if (abstransY >= (calendarMonthHight - defaultStopHeight) && abstransY < calendarMonthHight - calendarItemHight) {
if (!animatStart) {
mCalendarViewMonth.setTranslationY(getOffset((int) mCalendarViewMonth.getTranslationY() + dy, calendarItemHight - defaultStopHeight));
}
}
}
if (dy > 0) {
if (abstransY < maxOffset
&& currentMode.equals(Mode.WEEK)) {
mCalendarViewWeek.setVisibility(INVISIBLE);
}
if (abstransY < maxOffset) {
mCalendarViewMonth.setTranslationY(getOffset((int) mCalendarViewMonth.getTranslationY() + dy, 0));
}

}

}

月视图的移动我们是通过setTranslationY来移动的,为了防止滑动时候过快通过getOffset()限制一下它滑动的最大距离。

@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
int moveY = finalMonthModeHeight - mRecyclerView.getTop();
//周模式距离滑动为一行的高度,超过就滑动到周位置
int weekdistance = calendarItemHight;
//最大滑动距离
int maxDistance = calendarMonthHight;
if (currentMode == Mode.MONTH) {
//如果滑动距离超过当前选中项和最大滑动距离之间的距离
if (moveY > weekdistance && moveY < maxDistance) {
//变为周模式
setMode(Mode.WEEK);
} else if (moveY <= weekdistance) {
//变为月模式
setMode(Mode.MONTH);
}
} else {
//周模式下距离顶部选中日期的距离小于最大滑动距离-10的话就让它变为月模式
if (moveY > maxOffset - 10) {
//变为周模式
setMode(Mode.WEEK);
} else if (moveY <= maxOffset - 10) {
//变为月模式
setMode(Mode.MONTH);
}
}
}

最后

这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

相信它会给大家带来很多收获:

img

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

  • 无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,这四个字就是我的建议!!!
  • 我希望每一个努力生活的IT工程师,都会得到自己想要的,因为我们很辛苦,我们应得的。

当我们在抱怨环境,抱怨怀才不遇的时候,没有别的原因,一定是你做的还不够好!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
活的IT工程师,都会得到自己想要的,因为我们很辛苦,我们应得的。

当我们在抱怨环境,抱怨怀才不遇的时候,没有别的原因,一定是你做的还不够好!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 9
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值