关于Zaker图片启动页面的效果实现


        一直用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的效果,但是基本能用,如果要调就可以调整里面的参数,相关代码如下:

        @Override
	public 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;
    }

       对图片做了适当的处理以后,就可以考虑在动画过程中对位置进行处理以实现图片的滑动效果:

        @Override
	public 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       



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值