关于Zaker图片启动页面的效果实现
来源:互联网 发布:加油站经营数据分析 编辑:程序博客网 时间:2024/05/16 12:14
一直用Zaker看新闻,觉得Zaker的设计非常简洁明了。之前一直对Zaker的图片启动页面很喜欢,每次给的图都很漂亮,而且动画的效果也很流畅舒服,所以就找了时间静下心来好好的研究了一番。
最初想了解的时候,在网上搜了一个实现,网上例子只是实现了zaker图片页面的效果,但是实际应该与zaker有很大区别。区别的主要原因是这样的:
1、上面的例子在Layout的子类中用了其他的View,比如ImageView,如果Zaker的实现跟此例相似的话,那么在手机上打开“开发者选项”中的“显示边界布局”,那么就应该能够看到一些子View的边界,但是实际上在zaker的页面上是看不见的,及时是左下角的下载按钮,同样是没有边界的,所以通过这点,我认为Zaker的页面上用到的所有“组件”都不是使用系统的组件,同时这些功能图应该是直接绘制到一个全页面的View上面的。
2、在上面的例子中,动画的部分,我们会发觉例子的动画和zaker的动画有一些差别,特别在顺滑度上,所以我觉得zaker的实现是不同的。
声明:不是认为网上的例子不好,通过这个的例子,我才发现了和zaker实际的不同的地方,然后才考虑换一张方式去实现。感谢例子的作者给我的启发(例子地址附在最下方)。
按照对于Zaker的页面的观察,得到上面两点后,就考虑实际的实现了。首先,已经发现Zaker的实现是通过直接将元素绘制到页面上,那么我们可以创建一个View的子类,用来绘制在这个View中的所有元素,包括左上角的Zaker标志,背景图,左下角的loading图片和下载图片。其次就是动画部分,我们会发现,在实际操作Zaker页面的时候,不同给的操作方式时,显示的动画是有区别的,共有三个不同的动画部分:1、页面划上去部分后释放,自然下落的动画;2、单次点击时的弹跳动画;3、向上快速滑动的时候收起的动画。对于这三个动画,应该是采用不同的加速器,同时,在动画执行过程中,页面上的元素的透明度也是不一样的,鉴于此,我不适用系统自带的动画系统,采用了另外一个开源的动画框架:NineOldAndroid来实现,地址为http://nineoldandroids.com/
首先,我们下载了一张zaker的壁纸图片,发现那张壁纸是960*960的,而在显示的时候,只显示的是中间部分,说明zaker在处理图片的时候,对图片做过矩阵变换,所以我们的View中有Matrix对象来处理图片的矩阵变换。其次,点击图片时,自己发现,会有至少三种动画类型:1、向上快速滑动的收起,2、拖动然后释放,图片自然下落,3、单次点击的操作,那么中和一下,那么我们需要有三种动画类型,然后至少有三个不同的插值器Interpolator。
一,定义动画类型:
/** * Cover动画类型:默认类型. */private static final int ANIMATOR_TYPE_DEFAULT = 0;/** * Cover动画类型:向上收起. */private static final int ANIMATOR_TYPE_FOLD_UP = 1;/** * Cover动画类型:普通的向下掉落. */private static final int ANIMATOR_TYPE_NORMAL_FALL = 2;/** * Cover动画类型:单击. */private static final int ANIMATOR_TYPE_SINGLE_TOUCH = 3;二,根据动画类型,设置不同的插值器的动画文件:
private void createCoverAnimator() {if (mCoverValueAnimator == null) {float[] defaultPosition = new float[2];defaultPosition[0] = mMoveDistance;defaultPosition[1] = 0.0F;mCoverValueAnimator = ValueAnimator.ofFloat(defaultPosition).setDuration(1000L);mCoverValueAnimator.addListener(this);mCoverValueAnimator.addUpdateListener(this);}switch (mCoverAnimatorType) {case ANIMATOR_TYPE_FOLD_UP:// 向上收起的动画float[] positionsUp = new float[2];positionsUp[0] = mMoveDistance;positionsUp[1] = -getHeight();mCoverValueAnimator.setFloatValues(positionsUp);mCoverValueAnimator.setDuration(5000L);mCoverValueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());break;case ANIMATOR_TYPE_NORMAL_FALL:// 普通下落的动画,采用自定义的弹性加速器float[] positionsFall = new float[2];positionsFall[0] = mMoveDistance;positionsFall[1] = 0.0F;mCoverValueAnimator.setFloatValues(positionsFall);mCoverValueAnimator.setDuration(1000L);mCoverValueAnimator.setInterpolator(new CoverInterpolator());break;case ANIMATOR_TYPE_SINGLE_TOUCH:// 单次点击的动画,后续弹起的高度是前一次的一半,最多三次弹起。float maxHeight = -getHeight() / 15;float[] positionsSingle = new float[7];positionsSingle[0] = 0.0F;positionsSingle[1] = maxHeight;positionsSingle[2] = 0.0F;positionsSingle[3] = (maxHeight / 2.0F);positionsSingle[4] = 0.0F;positionsSingle[5] = (maxHeight / 4.0F);positionsSingle[6] = 0.0F;mCoverValueAnimator.setFloatValues(positionsSingle);mCoverValueAnimator.setDuration(1000L);mCoverValueAnimator.setInterpolator(new LinearInterpolator());default:break;}}其中的:
private ValueAnimator mCoverValueAnimator;// 移动的距离.private float mMoveDistance;上面代码中用到了一个自定义的CoverInterplator,这个类是参考BounceInterpolator做了一个修改,不过没有达到zaker的效果,但是基本能用,如果要调就可以调整里面的参数,相关代码如下:
@Overridepublic float getInterpolation(float input) {float value;if (input < 0.35) {value = (float) (8 * input * input);} else if (input < 0.74) {value = (float) (0.8 + 7.56 * (input - 0.55D) * (input - 0.55));} else if (input < 0.9) {value = (float) (0.94 + 7.56 * (input - 0.812) * (input - 0.82));} else {value = (float) (0.98 + 7.56 * (input - 0.95) * (input - 0.95));}return value;}
这样就定义完了动画文件,那么接下来就是上面提到的对图片的矩阵变换了。
PaintFlagsDrawFilter paintFlagsDFilter;private Drawable mCoverDrawable;private Matrix mCoverMatrix;private int mWidth;private int mHeight; @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mWidth = w; mHeight = h; paintFlagsDFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); // 背景图片 mCoverDrawable = getResources().getDrawable(R.drawable.cover_drawable); mCoverMatrix = getCoverMatrix(mCoverDrawable); mCoverAnimatorType = ANIMATOR_TYPE_DEFAULT; } /** * 获取图片显示时要做的矩阵变换。 * * @param drawable * @return */ private Matrix getCoverMatrix(Drawable drawable) { Matrix matrix; int intrinsicWidth; int intrinsicHeight; float scale; float transX = 0.0F; float transY = 0.0F; matrix = new Matrix(); if (drawable != null) { // 图片的两个属性一样 intrinsicWidth = drawable.getIntrinsicWidth(); intrinsicHeight = drawable.getIntrinsicHeight(); // 高<=宽 if (intrinsicWidth * mHeight > intrinsicHeight * mWidth) { scale = (float) mHeight / (float) intrinsicHeight; transX = 0.5F * (mWidth - scale * intrinsicWidth); transY = 0.0F; } else { scale = (float) mWidth / (float) intrinsicWidth; transY = 0.5F * (mHeight - scale * intrinsicHeight); if (intrinsicWidth <= 0) { drawable.setBounds(0, 0, mWidth, mHeight); } } drawable.setBounds(0, 0, intrinsicWidth, intrinsicHeight); matrix.setScale(scale, scale); matrix.postTranslate((int) (transX + 0.5F), (int) (transY + 0.5F)); } return matrix; }
对图片做了适当的处理以后,就可以考虑在动画过程中对位置进行处理以实现图片的滑动效果:
@Overridepublic void onAnimationUpdate(ValueAnimator animation) {if (animation == mCoverValueAnimator) {mMoveDistance = ((Float) mCoverValueAnimator.getAnimatedValue()).floatValue();System.out.println("CoverView distance = " + mMoveDistance);invalidate();}} @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); canvas.setDrawFilter(paintFlagsDFilter); canvas.translate(0.0f, mMoveDistance); canvas.save(); // 裁剪画布 canvas.clipRect(0, 0, mWidth, mHeight); if (mCoverDrawable != null) { // 绘制旧的壁纸 canvas.save(); if (mCoverMatrix != null) { canvas.concat(mCoverMatrix); } mCoverDrawable.draw(canvas); } canvas.restore(); } @Override public boolean onTouchEvent(MotionEvent event) { if ((ANIMATOR_TYPE_FOLD_UP == mCoverAnimatorType) || (-1 == mCoverAnimatorType)) { return true; } float x = event.getX(); float y = event.getY(); if (velocityTracker == null) { velocityTracker = VelocityTracker.obtain(); } velocityTracker.addMovement(event); int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: if (mCoverValueAnimator != null) { mCoverValueAnimator.cancel(); mCoverValueAnimator = null; } mDownX = x; mDownY = y; mLastEventY = y; break; case MotionEvent.ACTION_MOVE: float moverDistance = y - mLastEventY; if (moverDistance + mMoveDistance <= 0.0F) {// 标明发生了向上移动 mMoveDistance += moverDistance;// 移动的绝对距离,包括上下总和,带符号 } mLastEventY = y; invalidate(); break; case MotionEvent.ACTION_UP: velocityTracker.computeCurrentVelocity(1000); if (velocityTracker.getYVelocity() < -800.0F) { if (mCoverAnimatorType != ANIMATOR_TYPE_FOLD_UP) { mCoverAnimatorType = ANIMATOR_TYPE_FOLD_UP; } if (velocityTracker != null) { velocityTracker.clear(); velocityTracker.recycle(); velocityTracker = null; } } else { float absX = Math.abs(x - mDownX); float absY = Math.abs(y - mLastEventY); if ((absX >= 40.0F) || (absY >= 40.0F) || (mMoveDistance > 0) || (mMoveDistance < -40)) { if (mMoveDistance >= -getHeight() / 3) { mCoverAnimatorType = ANIMATOR_TYPE_NORMAL_FALL; } else { mCoverAnimatorType = ANIMATOR_TYPE_FOLD_UP; } } else { mCoverAnimatorType = ANIMATOR_TYPE_SINGLE_TOUCH; } } startCoverAnimation(); break; case MotionEvent.ACTION_CANCEL: break; default: break; } return true; }上面只列出了一些重要的处理代码,其他代码请参考项目源码。
上面代码基本实现了zaker相似的拖动、点击、快速滑动的效果,在一些细节和弹性的地方还是有一些差距。不过,整体思路还是比较明确的。整个例子中,对于其他软件的观察还是花了很长时间,如果单从表面现象其实很难知道对方是怎么实现的,只有自己尝试去做了,抓住关键的地方才是最好的。
最后附上源码:http://pan.baidu.com/s/1o6NXzvk
上面参考的例子地址:http://blog.csdn.net/manymore13/article/details/12219687
- 关于Zaker图片启动页面的效果实现
- zaker splash页面实现
- 仿zaker最新版本引导界面的视图联动效果(修改viewpager实现)
- 关于图片透明效果的实现
- 页面实现图片滚动效果
- Android实现Zaker的加载对话框
- [转载]Android实现Zaker的加载对话框
- 仿zaker最新版本引导界面的视图联动效果
- 通过设置图片实现app启动广告页的效果
- Gallery实现ViewPager的页面切换效果、以及实现图片画廊效果
- Android特效开发(仿zaker用手向上推动的效果(推动门效果))
- Android 仿zaker用手向上推动的效果(推动门效果
- Android特效开发(仿zaker用手向上推动的效果(推动门效果))
- Android特效开发(仿zaker用手向上推动的效果(推动门效果))
- Android特效开发(仿zaker用手向上推动的效果(推动门效果))
- Android特效开发(仿zaker用手向上推动的效果(推动门效果))
- Android特效开发(仿zaker用手向上推动的效果(推动门效果))
- Android特效开发(仿zaker用手向上推动的效果(推动门效果))
- java实现的拦截器
- 最好的GIT教程
- XML的SelectNodes使用方法以及XPath .
- 将标准输出重定向到一个文件的同时并在屏幕上显示
- 博客
- 关于Zaker图片启动页面的效果实现
- java动态代理(JDK和cglib)
- jaxb的优点和用法
- MAVEN三大特性,很实用
- InvocationHandler中invoke()方法的调用问题
- WS_CLIP特性 文章
- HttpServer的简单运用
- linux下which、whereis、locate、find 命令的区别
- 深入了解JVM—内存区域