华为P8日历的截图

目前仅实现动画,内容没有填充,效果如下图

布局分析

承载日历网格的是一个ViewPager,每天的日历活动是一个ListView,需要实现的效果是,在listview区域向上滑动,viewpager也跟着滑动,滑动后viewpaer只留下一行,listview滑动到这一行的下面。

难点在于拦截listview的滑动事件。

实现分析

使用CalendarContainer(继承自FrameLayout)作为ViewPager和listView的父空间,在CalendarContainer的onInterceptTouchEvent中判断拦截listview的滑动事件,并处理滑动动画。

CalendarContainer类文件;

package com.example.test;import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.FrameLayout;/*** @author whuthm* @date 2015-6-12*/
public class CalendarContainer extends FrameLayout {/** 停止滚动 */public static final int SCROLL_STATE_IDLE = 0;/** 手指在屏幕上正在滚动 */public static final int SCROLL_STATE_TOUCH_SCROLL = 1;/** 手指离开屏幕滚动 */public static final int SCROLL_STATE_FLING = 2;private int mScrollState = SCROLL_STATE_IDLE;private VelocityTracker mVelocityTracker;private CalendarListView mListView;private CalendarViewPager mViewPager;private int mRowHeight = 150;private int mRowCount = 5;private int mSelecteRow = 2;private float mLastMotionY;private boolean mIsOnSlidablyArea = false;private int mTouchSlop;/** view是否展开 */private boolean mExpanded = true;public CalendarContainer(Context context) {this(context, null);}public CalendarContainer(Context context, AttributeSet attrs) {this(context, attrs, 0);}public CalendarContainer(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);final ViewConfiguration configuration = ViewConfiguration.get(getContext());mTouchSlop = configuration.getScaledTouchSlop();mViewPager = new CalendarViewPager(context, attrs);// mViewPager.setBackgroundColor(Color.GREEN);mViewPager.setBackgroundResource(R.drawable.ic_launcher);LayoutParams pagerLp = new LayoutParams(0, 0);pagerLp.x = 0;pagerLp.y = 0;addView(mViewPager, pagerLp);mListView = new CalendarListView(context, attrs, defStyle);mListView.setBackgroundColor(Color.BLUE);LayoutParams listLp = new LayoutParams(0, 0);listLp.x = 0;listLp.y = mRowCount * mRowHeight;addView(mListView, listLp);}private void acquireVelocityTrackerAndAddMovement(MotionEvent ev) {if (mVelocityTracker == null) {mVelocityTracker = VelocityTracker.obtain();}mVelocityTracker.addMovement(ev);}private void releaseVelocityTracker() {if (mVelocityTracker != null) {mVelocityTracker.recycle();mVelocityTracker = null;}}protected void determineScrollingStart(MotionEvent ev) {final float y = ev.getY();final float deltaY = y - mLastMotionY;final int yDiff = (int) Math.abs(deltaY);boolean xScrolled = yDiff >= mTouchSlop;if (xScrolled&& ((deltaY < 0 && mExpanded) || (deltaY > 0 && !mExpanded))) {mScrollState = SCROLL_STATE_TOUCH_SCROLL;mLastMotionY = y;}}/** 滑动动画结束时,属性改变*/private void end() {mExpanded = !mExpanded;mScrollState = SCROLL_STATE_IDLE;}/** 取消后,直接重置*/private void cancel() {mScrollState = SCROLL_STATE_IDLE;LayoutParams listLp = (LayoutParams) mListView.getLayoutParams();listLp.y = mExpanded ? mRowCount * mRowHeight : mRowHeight;LayoutParams pagerLp = (LayoutParams) mViewPager.getLayoutParams();pagerLp.y = mExpanded ? 0 : -mSelecteRow * mRowHeight;requestLayout();}/** 跟随手指滑动动画*/private void move(int distance) {boolean needRequesLayout = false;LayoutParams listLp = (LayoutParams) mListView.getLayoutParams();final int listTop = mRowHeight;final int listBottom = mRowCount * mRowHeight;int listTargetY = listLp.y + distance;if (listTargetY >= listTop && listTargetY <= listBottom) {listLp.y = listTargetY;listLp.x = 0;needRequesLayout = true;}LayoutParams pagerLp = (LayoutParams) mViewPager.getLayoutParams();final int pagerTop = -mSelecteRow * mRowHeight;final int pagerBottom = 0;int pagerTargetY = pagerLp.y + distance;if (pagerTargetY >= pagerTop && pagerTargetY <= pagerBottom) {pagerLp.y = pagerTargetY;pagerLp.x = 0;needRequesLayout = true;}if (needRequesLayout) {requestLayout();}}/** 手指松开后,滑动动画,目前只支持属性动画*/@SuppressLint("NewApi")private void fling() {mScrollState = SCROLL_STATE_FLING;final LayoutParams listLp = (LayoutParams) mListView.getLayoutParams();final int listTop = mRowHeight;final int listBottom = mRowCount * mRowHeight;if (listLp.y > listTop && listLp.y < listBottom) {final LayoutParams pagerLp = (LayoutParams) mViewPager.getLayoutParams();final int pagerTop = -mSelecteRow * mRowHeight;final int pagerBottom = 0;int listTargetY = mExpanded ? listTop : listBottom;int pagerTargetY = mExpanded ? pagerTop : pagerBottom;ValueAnimator listAnimator = ValueAnimator.ofInt(listLp.y,listTargetY);listAnimator.addUpdateListener(new AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animator) {listLp.y = (Integer) animator.getAnimatedValue();requestLayout();}});ValueAnimator pagerAnimator = ValueAnimator.ofInt(pagerLp.y,pagerTargetY);pagerAnimator.addUpdateListener(new AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animator) {pagerLp.y = (Integer) animator.getAnimatedValue();requestLayout();}});AnimatorSet set = new AnimatorSet();set.playTogether(listAnimator, pagerAnimator);set.addListener(new AnimatorListener() {@Overridepublic void onAnimationStart(Animator animator) {}@Overridepublic void onAnimationRepeat(Animator animator) {}@Overridepublic void onAnimationEnd(Animator animator) {end();}@Overridepublic void onAnimationCancel(Animator animator) {cancel();}});set.setDuration(400);set.start();} else {end();}}//处理滑动动画@SuppressLint("ClickableViewAccessibility")@Overridepublic boolean onTouchEvent(MotionEvent ev) {Log.e("whuthm2222", "onTouchEvent action = " + ev.getAction());if (mScrollState == SCROLL_STATE_FLING) {return true;}if (getChildCount() <= 0) {mScrollState = SCROLL_STATE_IDLE;return super.onTouchEvent(ev);}acquireVelocityTrackerAndAddMovement(ev);final int action = ev.getAction();final float y = ev.getY();switch (action) {case MotionEvent.ACTION_DOWN:mLastMotionY = y;break;case MotionEvent.ACTION_MOVE:if (mScrollState == SCROLL_STATE_TOUCH_SCROLL) {float deltaY = y - mLastMotionY;if (Math.abs(deltaY) >= 1.0f) {move((int) deltaY);mLastMotionY = y;}}break;case MotionEvent.ACTION_UP:if (mScrollState == SCROLL_STATE_TOUCH_SCROLL) {fling();}releaseVelocityTracker();break;case MotionEvent.ACTION_CANCEL:cancel();releaseVelocityTracker();break;}return true;}/** 拦截listview的滑动事件*/@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {Log.e("whuthm", "onInterceptTouchEvent action = " + ev.getAction()+ "  " + mScrollState);//当在滑动中时,直接拦截if (mScrollState == SCROLL_STATE_TOUCH_SCROLL|| mScrollState == SCROLL_STATE_FLING) {return true;}if (getChildCount() <= 0) {return super.onInterceptTouchEvent(ev);}//当listview的第一项不可见时不拦截if (!mListView.isTop()) {return super.onInterceptTouchEvent(ev);}acquireVelocityTrackerAndAddMovement(ev);final int action = ev.getAction();final float x = ev.getX();final float y = ev.getY();//当滑动区域不在listview中时,不 拦截if (action != MotionEvent.ACTION_DOWN && !mIsOnSlidablyArea) {return super.onInterceptTouchEvent(ev);}switch (action) {case MotionEvent.ACTION_DOWN:mIsOnSlidablyArea = isOnSlidablyArea(x, y);mScrollState = SCROLL_STATE_IDLE;mLastMotionY = y;break;case MotionEvent.ACTION_MOVE:determineScrollingStart(ev);break;case MotionEvent.ACTION_UP:end();releaseVelocityTracker();break;case MotionEvent.ACTION_CANCEL:cancel();releaseVelocityTracker();break;}return mScrollState == SCROLL_STATE_TOUCH_SCROLL|| mScrollState == SCROLL_STATE_FLING;}/** 计算可滑动区域,list在父控件中的可见区域 */private boolean isOnSlidablyArea(float x, float y) {int left = Math.max(0, mListView.getLeft());int top = Math.max(0, mListView.getTop());int right = Math.min(getWidth(), mListView.getRight());int bottom = Math.min(getHeight(), mListView.getBottom());return x >= left && x <= right && y >= top && y <= bottom;}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// super.onMeasure(widthMeasureSpec, heightMeasureSpec);int widthSize = MeasureSpec.getSize(widthMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);if (mListView.getVisibility() != View.GONE) {LayoutParams listLp = (LayoutParams) mListView.getLayoutParams();listLp.width = widthSize;listLp.height = heightSize - mRowHeight;mListView.measure(MeasureSpec.makeMeasureSpec(listLp.width,MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(listLp.height, MeasureSpec.EXACTLY));}if (mViewPager.getVisibility() != View.GONE) {LayoutParams pagerLp = (LayoutParams) mViewPager.getLayoutParams();pagerLp.width = widthSize;pagerLp.height = mRowHeight * mRowCount;mViewPager.measure(MeasureSpec.makeMeasureSpec(pagerLp.width,MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(pagerLp.height, MeasureSpec.EXACTLY));}setMeasuredDimension(widthSize, heightSize);}@Overrideprotected void onLayout(boolean changed, int left, int top, int right,int bottom) {// super.onLayout(changed, left, top, right, bottom);if (mListView.getVisibility() != View.GONE) {LayoutParams listLp = (LayoutParams) mListView.getLayoutParams();mListView.layout(listLp.x, listLp.y, listLp.x + listLp.width,listLp.y + listLp.height);}if (mViewPager.getVisibility() != View.GONE) {LayoutParams pagerLp = (LayoutParams) mViewPager.getLayoutParams();mViewPager.layout(pagerLp.x, pagerLp.y, pagerLp.x + pagerLp.width,pagerLp.y + pagerLp.height);}}class LayoutParams extends FrameLayout.LayoutParams {int x;int y;public LayoutParams(int width, int height) {super(width, height);}}}

CalendarListView类文件

package com.example.test;import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;/*** @author whuthm* @date 2015-6-12*/
public class CalendarListView extends ListView {public CalendarListView(Context context) {super(context);}public CalendarListView(Context context, AttributeSet attrs) {super(context, attrs);}public CalendarListView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);setAdapter(new SimpleAdapter());}/** 判断第一项是否可见*/boolean isTop() {return getFirstVisiblePosition() == 0;}class SimpleAdapter extends BaseAdapter {String[] datas;public SimpleAdapter() {datas = new String[100];for (int i = 0; i < 100; i++) {datas[i] = "test " + i;}}@Overridepublic int getCount() {return datas.length;}@Overridepublic Object getItem(int position) {return datas[position];}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {if (convertView == null) {convertView = new TextView(getContext());}((TextView) convertView).setText(datas[position]);return convertView;}}}

数据为填充,后续完善。

android 日历动画的实现相关推荐

  1. android 日历动画效果,Android仿 MIUI日历

    前言 这个日历是一个仿MIUI交互的日历,尽可能实现MIUI日历的交互设计,加入了一些自定义属性,如设置默认视图,设置一周的第一天是周日还是周一等.这个日历是在之前我写的那个日历基础上改的,里面的关于 ...

  2. android 日历翻页动画,Android开源库合集:轻松实现Android动态,炫目:日历效果...

    前言: 了解过那种动态,炫目的日历效果吗?你知道是怎么 操作的嘛?是否想过,用UI就可以实现,对,也许你说的对,不过UI只是都是动态效果的一部分.那么今天用Annroid开源库,来告诉你android ...

  3. Android日历视图

    In this tutorial, we'll be discussing the Calendar Widget using the CalendarView class in our Androi ...

  4. android 自定义loading,Android自定义动画-StarLoadingView

    今天来分享第二个自定义loading的动画,起了个名字叫 蹦跶的星星 ,还是老规矩先介绍,后上图. 实现效果在最后,GIF有点大,手机流量慎重. 介绍 首先声明做这个动画的初衷是为了学习和分享,所以从 ...

  5. android 三维动画效果,9款令人惊叹的HTML5 3D动画应用

    原标题:9款令人惊叹的HTML5 3D动画应用 之前我们已经向大家分享了很多HTML5动画应用了,大部分都非常炫酷,也有一小部分是很实用的.今天我们要向各位HTML5动画爱好者介绍更多的HTML5 3 ...

  6. 【学习笔记】Android视图动画学习

    2019独角兽企业重金招聘Python工程师标准>>> 1.Android View动画框架 Animation框架定义了透明度.旋转.缩放和位移几种常见的动画. 实现原理:每次绘制 ...

  7. android矢量动画 充电,android矢量动画

    android矢量动画! 直接来个例子就明白了!(这里我把与动画无关的属性都用-表示) 首先你要有个矢量图 比如这个矢量图xml文件叫"vector1",文件在res\drawab ...

  8. Android视图动画集合AndoridViewAnimations

    Android视图动画集合AndoridViewAnimations Android视图动画是针对视图对象的动画效果,包括对象平移.旋转.缩放和渐变四种类型.通过组合这四种类型,可以创建出无数种动画效 ...

  9. 日历控件的android代码,Android日历控件PickTime代码实例

    Android日历控件PickTime代码实例 发布时间:2020-10-03 16:05:51 来源:脚本之家 阅读:86 作者:手撕高达的村长 最近做项目,需要设置用户的生日,所以做这样一个功能. ...

最新文章

  1. 打造高效的工作环境 – SHELL 篇
  2. 弹出对话框拖拽JavaScript实现
  3. An example of parsing xml file using Scala
  4. execjs执行js出现window对象未定义时的解决_10个常见的JS语言错误总汇
  5. web api 权限控制
  6. NVIDIA ECCV18论文:超像素采样网络助力语义分割与光流估计(代码将开源)
  7. 科学计算机怎么编程游戏,官泄 可编程科学计算器开发游戏
  8. 最大功率点跟踪_华北电力大学颜湘武团队特稿:基于变功率点跟踪和超级电容器储能协调控制的双馈风电机组一次调频策略...
  9. 介绍全新的 JSX 转换
  10. git与github远程连接代码库使用笔记
  11. 利用canvas打造一个炫酷的粒子背景
  12. ❤️字节跳动8年测试经验,彻夜无眠整理的40道自动化测试面试题(附精准答案),爆肝2W字❤️
  13. 无缘无故,Oralce使用normal模式登录用户失败
  14. 计算识别率的matlab代码,基于MATLAB神经网络图像识别的高识别率代码
  15. 基于声网 视频通话SDK 的opencv 人脸检测
  16. 哈夫曼编码树的经典题目
  17. 来认识一下Ning!
  18. 分子对接教程 | (5) 配体小分子的预处理
  19. 环保绿色植树节主题班会课件PPT模板
  20. relate to与associate with的区别

热门文章

  1. python为什么是蛇的天敌_蛇的天敌有哪些:蛇的天敌排名
  2. mysql配置报错thread_MySQL错误Forcing close of thread的两种解决方法
  3. 墨者靶场-SQL手工注入漏洞测试(MySQL数据库-字符型)
  4. 揭密win7pe制作全过程
  5. Win11怎么分区硬盘?
  6. 音乐 美术 劳技 计算机教研组工作总结,综合教研组教学工作总结
  7. 三个月速成Java--一些小建议和感概
  8. 2023年江苏专转本志愿填报辅导(22上岸南工程学长辅导手册)
  9. 计算机硕士论文导师评语,硕士论文指导教师评语
  10. 一键加速去不掉加锁的_vivo手机一键加速的时候,怎样可以不把当前的应用清除?...