Launcher3 翻页动画详解与修改
2016-03-25 15:14
495 查看
Launcher3 翻页动画详解与修改
一直对android桌面的翻页效果比较感兴趣,这两天有空所以去研究了下它的实现原理。Launcher3 的页面分为两块:工作区(workspace)和主菜单(APPS_CUSTOMIZE),这两块基本功能差不多,都是继承于PagedView,工作区的翻页效果是普通的平滑过渡,没什么特殊效果。主菜单页则采用了类似于扑克牌的层叠效果,本文重点研究其实现,最后顺便修改成其它效果。
1,原理
开始一直以为是由动画效果生成的,结果走了不少弯路,后来发现是Scroller滚动时不断利用重载dispatchDraw绘制子页生成的一个动画效果。关键代码如下:@Override protected void dispatchDraw(Canvas canvas) { int halfScreenSize = getViewportWidth() / 2; // 270 // mOverScrollX is equal to getScrollX() when we're within the normal // scroll range. // Otherwise it is equal to the scaled overscroll position. int screenCenter = mOverScrollX + halfScreenSize; // 当位置为变化或强制滑动时 if (screenCenter != mLastScreenCenter || mForceScreenScrolled) { // set mForceScreenScrolled before calling screenScrolled so that // screenScrolled can // set it for the next frame mForceScreenScrolled = false; //绘制翻页效果的关键位置,下面将重点展开详解。 screenScrolled(screenCenter); mLastScreenCenter = screenCenter; } // Find out which screens are visible; as an optimization we only call // draw on them final int pageCount = getChildCount(); if (pageCount > 0) { getVisiblePages(mTempVisiblePagesRange); final int leftScreen = mTempVisiblePagesRange[0]; final int rightScreen = mTempVisiblePagesRange[1]; if (leftScreen != -1 && rightScreen != -1) { final long drawingTime = getDrawingTime(); // Clip to the bounds canvas.save(); canvas.clipRect(getScrollX(), getScrollY(), getScrollX() + getRight() - getLeft(), getScrollY() + getBottom() - getTop()); // Draw all the children, leaving the drag view for last //绘制所有子页,拖动页最后绘制 for (int i = pageCount - 1; i >= 0; i--) { final View v = getPageAt(i); if (v == mDragView) continue; if (mForceDrawAllChildrenNextFrame || (leftScreen <= i && i <= rightScreen && shouldDrawChild(v))) { drawChild(canvas, v, drawingTime); } // Draw the drag view on top (if there is one) if (mDragView != null) { drawChild(canvas, mDragView, drawingTime); } mForceDrawAllChildrenNextFrame = false; canvas.restore(); } } } }
当Scroll位置有变化时,就进入screenScrolled这个类,对子页进行缩放,改变透明度,转换角度,移动等,不断变化就组合成了我们看到的层叠效果。都是view自带的一些类,没有涉及复杂的动画效果。
// launcher3\AppsCustomizePagedView.java // In apps customize, we have a scrolling effect which emulates pulling cards off of a stack. @Override protected void screenScrolled(int screenCenter) { // 右到左布局,一般中东一些语言才采用,这里为false final boolean isRtl = isLayoutRtl(); super.screenScrolled(screenCenter); // 循环处理每页 for (int i = 0; i < getChildCount(); i++) { View v = getPageAt(i); if (v != null) { // 获取滑动的进度 float scrollProgress = getScrollProgress(screenCenter, v, i); float interpolatedProgress; float translationX; float maxScrollProgress = Math.max(0, scrollProgress); float minScrollProgress = Math.min(0, scrollProgress); if (isRtl) { translationX = maxScrollProgress * v.getMeasuredWidth(); interpolatedProgress = mZInterpolator.getInterpolation(Math.abs(maxScrollProgress)); } else { //子页X轴偏移 translationX = minScrollProgress * v.getMeasuredWidth(); //插值器,值是一条向上的抛物线,为了缩放过度更加自然 interpolatedProgress = mZInterpolator.getInterpol 4000 ation(Math.abs(minScrollProgress)); } float scale = (1 - interpolatedProgress) + interpolatedProgress * TRANSITION_SCALE_FACTOR; float alpha; if (isRtl && (scrollProgress > 0)) { alpha = mAlphaInterpolator.getInterpolation(1 - Math.abs(maxScrollProgress)); } else if (!isRtl && (scrollProgress < 0)) { alpha = mAlphaInterpolator.getInterpolation(1 - Math.abs(scrollProgress)); } else { // On large screens we need to fade the page as it nears its leftmost position alpha = mLeftScreenAlphaInterpolator.getInterpolation(1 - scrollProgress); } v.setCameraDistance(mDensity * CAMERA_DISTANCE); int pageWidth = v.getMeasuredWidth(); int pageHeight = v.getMeasuredHeight(); if (PERFORM_OVERSCROLL_ROTATION) { float xPivot = isRtl ? 1f - TRANSITION_PIVOT : TRANSITION_PIVOT; boolean isOverscrollingFirstPage = isRtl ? scrollProgress > 0 : scrollProgress < 0; boolean isOverscrollingLastPage = isRtl ? scrollProgress < 0 : scrollProgress > 0; //在第一页向右滑时 if (i == 0 && isOverscrollingFirstPage) { // Overscroll to the left v.setPivotX(xPivot * pageWidth); v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress); scale = 1.0f; alpha = 1.0f; // On the first page, we don't want the page to have any lateral motion translationX = 0; //最后一页向左滑时 } else if (i == getChildCount() - 1 && isOverscrollingLastPage) { // Overscroll to the right v.setPivotX((1 - xPivot) * pageWidth); v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress); scale = 1.0f; alpha = 1.0f; // On the last page, we don't want the page to have any lateral motion. translationX = 0; } else { v.setPivotY(pageHeight / 2.0f); v.setPivotX(pageWidth / 2.0f); v.setRotationY(0f); } } v.setTranslationX(translationX); //X轴偏移 v.setScaleX(scale); //缩放 v.setScaleY(scale); v.setAlpha(alpha); //透明度 // If the view has 0 alpha, we set it to be invisible so as to prevent // it from accepting touches if (alpha == 0) { v.setVisibility(INVISIBLE); } else if (v.getVisibility() != VISIBLE) { v.setVisibility(VISIBLE); } } } enableHwLayersOnVisiblePages(); }
2,修改
基本上就是这样,没什么要过多解释的,试一下效果就比较清楚。下面将他们稍微改动下,变成翻转效果。第三方的Launcher有很多翻页效果,大家可以多尝试。看起来挺高端的,其实也是很简单的效果叠加的。还有,现在很多Launcher都是循环滑动的,具体怎样改为循环的,有机会再开一贴。// In apps customize, we have a scrolling effect which emulates pulling cards off of a stack. // http://blog.csdn.net/heymi_csdn @Override protected void screenScrolled(int screenCenter) { final boolean isRtl = isLayoutRtl(); super.screenScrolled(screenCenter); for (int i = 0; i < getChildCount(); i++) { View v = getPageAt(i); if (v != null) { float scrollProgress = getScrollProgress(screenCenter, v, i); float interpolatedProgress; float translationX; float maxScrollProgress = Math.max(0, scrollProgress); float minScrollProgress = scrollProgress;//Math.min(0, scrollProgress); if (isRtl) { translationX = maxScrollProgress * v.getMeasuredWidth(); interpolatedProgress = mZInterpolator.getInterpolation(Math.abs(maxScrollProgress)); } else { translationX = minScrollProgress * v.getMeasuredWidth(); interpolatedProgress = mZInterpolator.getInterpolation(Math.abs(minScrollProgress)); } float scale = (1 - interpolatedProgress) + interpolatedProgress * TRANSITION_SCALE_FACTOR; float alpha; alpha = mAlphaInterpolator.getInterpolation(1 - Math.abs(scrollProgress)); v.setCameraDistance(mDensity * CAMERA_DISTANCE); int pageWidth = v.getMeasuredWidth(); int pageHeight = v.getMeasuredHeight(); if (PERFORM_OVERSCROLL_ROTATION) { float xPivot = isRtl ? 1f - TRANSITION_PIVOT : TRANSITION_PIVOT; boolean isOverscrollingFirstPage = isRtl ? scrollProgress > 0 : scrollProgress < 0; boolean isOverscrollingLastPage = isRtl ? scrollProgress < 0 : scrollProgress > 0; if (i == 0 && isOverscrollingFirstPage) { // Overscroll to the left v.setPivotX(xPivot * pageWidth); //设置中心点 v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress); //Y轴旋转 // On the first page, we don't want the page to have any lateral motion translationX = 0; } else if (i == getChildCount() - 1 && isOverscrollingLastPage) { // Overscroll to the right v.setPivotX((1 - xPivot) * pageWidth); v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress); // On the last page, we don't want the page to have any lateral motion. translationX = 0; } else { v.setPivotY(pageHeight / 2.0f); v.setPivotX(pageWidth / 2.0f); v.setTranslationX(translationX);//X轴偏移方向 v.setRotationY(180 * scrollProgress); v.setAlpha(alpha); } } // If the view has 0 alpha, we set it to be invisible so as to prevent // it from accepting touches if (alpha == 0 || Math.abs(translationX) > pageWidth/2) { v.setVisibility(INVISIBLE); } else if (v.getVisibility() != VISIBLE) { v.setVisibility(VISIBLE); } } } enableHwLayersOnVisiblePages(); }
注:本文基于的是android4.4 MTK定制过的代码,和谷歌原生有一点差异。下次有空做一个demo。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories