关于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       



0 0