Android Folding View(摺疊視圖、控件)

  版本號:1.0html

日期:2014.4.21
版權:© 2014 kince 轉載註明出處

很是早以前看過有人求助如下這個效果是怎樣實現的,android


  也就是側滑菜單的一個摺疊效果,事實上關於這個效果的實現,谷歌的一名project師已經完畢。並開放源代碼到devbytes上面了。

如如下所看到的:git


  地址是:  https://android.googlesource.com/platform/development/+/master/samples/devbytes/graphics/ 。還有對應的視頻講解: DevBytes: Folding Layout - YouTube ,這個想看的話需要fq。執行效果例如如下:


  那後來就有人在此基礎之上加入了側滑菜單效果,包含DrawerLayout和PaneLayout兩種,此項目的github地址是 Folding-Android,執行效果例如如下:
一、DrawerLayout


  
  二、 PaneLayout

  如下主要環繞實現原理來展開,以谷歌的devbytes爲例。看一下它的本身定義ViewGroup:FoldingLayout。代碼例如如下,
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example.android.foldinglayout;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.graphics.Shader.TileMode;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

/**
* The folding layout where the number of folds, the anchor point and the
* orientation of the fold can be specified. Each of these parameters can
* be modified individually and updates and resets the fold to a default
* (unfolded) state. The fold factor varies between 0 (completely unfolded
* flat image) to 1.0 (completely folded, non-visible image).
*
* This layout throws an exception if there is more than one child added to the view.
* For more complicated view hierarchy's inside the folding layout, the views should all
* be nested inside 1 parent layout.
*
* This layout folds the contents of its child in real time. By applying matrix
* transformations when drawing to canvas, the contents of the child may change as
* the fold takes place. It is important to note that there are jagged edges about
* the perimeter of the layout as a result of applying transformations to a rectangle.
* This can be avoided by having the child of this layout wrap its content inside a
* 1 pixel transparent border. This will cause an anti-aliasing like effect and smoothen
* out the edges.
*
*/
public class FoldingLayout extends ViewGroup {

    public static enum Orientation {
        VERTICAL,
        HORIZONTAL
    }

    private final String FOLDING_VIEW_EXCEPTION_MESSAGE = "Folding Layout can only 1 child at " +
            "most";

    private final float SHADING_ALPHA = 0.8f;
    private final float SHADING_FACTOR = 0.5f;
    private final int DEPTH_CONSTANT = 1500;
    private final int NUM_OF_POLY_POINTS = 8;

    private Rect[] mFoldRectArray;

    private Matrix [] mMatrix;

    private Orientation mOrientation = Orientation.HORIZONTAL;

    private float mAnchorFactor = 0;
    private float mFoldFactor = 0;

    private int mNumberOfFolds = 2;

    private boolean mIsHorizontal = true;

    private int mOriginalWidth = 0;
    private int mOriginalHeight = 0;

    private float mFoldMaxWidth = 0;
    private float mFoldMaxHeight = 0;
    private float mFoldDrawWidth = 0;
    private float mFoldDrawHeight = 0;

    private boolean mIsFoldPrepared = false;
    private boolean mShouldDraw = true;

    private Paint mSolidShadow;
    private Paint mGradientShadow;
    private LinearGradient mShadowLinearGradient;
    private Matrix mShadowGradientMatrix;

    private float [] mSrc;
    private float [] mDst;

    private OnFoldListener mFoldListener;

    private float mPreviousFoldFactor = 0;

    private Bitmap mFullBitmap;
    private Rect mDstRect;

    public FoldingLayout(Context context) {
        super(context);
    }

    public FoldingLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public FoldingLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected boolean addViewInLayout(View child, int index, LayoutParams params,
                                      boolean preventRequestLayout) {
        throwCustomException(getChildCount());
        boolean returnValue = super.addViewInLayout(child, index, params, preventRequestLayout);
        return returnValue;
    }

    @Override
    public void addView(View child, int index, LayoutParams params) {
        throwCustomException(getChildCount());
        super.addView(child, index, params);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        View child = getChildAt(0);
        measureChild(child,widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        View child = getChildAt(0);
        child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
        updateFold();
    }

    /**
     * The custom exception to be thrown so as to limit the number of views in this
     * layout to at most one.
     */
    private class NumberOfFoldingLayoutChildrenException extends RuntimeException {
        public NumberOfFoldingLayoutChildrenException(String message) {
            super(message);
        }
    }

    /** Throws an exception if the number of views added to this layout exceeds one.*/
    private void throwCustomException (int numOfChildViews) {
        if (numOfChildViews == 1) {
            throw new NumberOfFoldingLayoutChildrenException(FOLDING_VIEW_EXCEPTION_MESSAGE);
        }
    }

    public void setFoldListener(OnFoldListener foldListener) {
        mFoldListener = foldListener;
    }

    /**
     * Sets the fold factor of the folding view and updates all the corresponding
     * matrices and values to account for the new fold factor. Once that is complete,
     * it redraws itself with the new fold. */
    public void setFoldFactor(float foldFactor) {
        if (foldFactor != mFoldFactor) {
            mFoldFactor = foldFactor;
            calculateMatrices();
            invalidate();
        }
    }

    public void setOrientation(Orientation orientation) {
        if (orientation != mOrientation) {
            mOrientation = orientation;
            updateFold();
        }
    }

    public void setAnchorFactor(float anchorFactor) {
        if (anchorFactor != mAnchorFactor) {
            mAnchorFactor = anchorFactor;
            updateFold();
        }
    }

    public void setNumberOfFolds(int numberOfFolds) {
        if (numberOfFolds != mNumberOfFolds) {
            mNumberOfFolds = numberOfFolds;
            updateFold();
        }
    }

    public float getAnchorFactor() {
        return mAnchorFactor;
    }

    public Orientation getOrientation() {
        return mOrientation;
    }

    public float getFoldFactor() {
        return mFoldFactor;
    }

    public int getNumberOfFolds() {
        return mNumberOfFolds;
    }

    private void updateFold() {
        prepareFold(mOrientation, mAnchorFactor, mNumberOfFolds);
        calculateMatrices();
        invalidate();
    }

    /**
     * This method is called in order to update the fold's orientation, anchor
     * point and number of folds. This creates the necessary setup in order to
     * prepare the layout for a fold with the specified parameters. Some of the
     * dimensions required for the folding transformation are also acquired here.
     *
     * After this method is called, it will be in a completely unfolded state by default.
     */
    private void prepareFold(Orientation orientation, float anchorFactor, int numberOfFolds) {

        mSrc = new float[NUM_OF_POLY_POINTS];
        mDst = new float[NUM_OF_POLY_POINTS];

        mDstRect = new Rect();

        mFoldFactor = 0;
        mPreviousFoldFactor = 0;

        mIsFoldPrepared = false;

        mSolidShadow = new Paint();
        mGradientShadow = new Paint();

        mOrientation = orientation;
        mIsHorizontal = (orientation == Orientation.HORIZONTAL);

        if (mIsHorizontal) {
            mShadowLinearGradient = new LinearGradient(0, 0, SHADING_FACTOR, 0, Color.BLACK,
                    Color.TRANSPARENT, TileMode.CLAMP);
        } else {
            mShadowLinearGradient = new LinearGradient(0, 0, 0, SHADING_FACTOR, Color.BLACK,
                    Color.TRANSPARENT, TileMode.CLAMP);
        }

        mGradientShadow.setStyle(Style.FILL);
        mGradientShadow.setShader(mShadowLinearGradient);
        mShadowGradientMatrix = new Matrix();

        mAnchorFactor = anchorFactor;
        mNumberOfFolds = numberOfFolds;

        mOriginalWidth = getMeasuredWidth();
        mOriginalHeight = getMeasuredHeight();

        mFoldRectArray = new Rect[mNumberOfFolds];
        mMatrix = new Matrix [mNumberOfFolds];

        for (int x = 0; x < mNumberOfFolds; x++) {
            mMatrix[x] = new Matrix();
        }

        int h = mOriginalHeight;
        int w = mOriginalWidth;

        if (FoldingLayoutActivity.IS_JBMR2) {
            mFullBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(mFullBitmap);
            getChildAt(0).draw(canvas);
        }

        int delta = Math.round(mIsHorizontal ?

((float) w) / ((float) mNumberOfFolds) : ((float) h) /((float) mNumberOfFolds)); /* Loops through the number of folds and segments the full layout into a number * of smaller equal components. If the number of folds is odd, then one of the * components will be smaller than all the rest. Note that deltap below handles * the calculation for an odd number of folds.*/ for (int x = 0; x < mNumberOfFolds; x++) { if (mIsHorizontal) { int deltap = (x + 1) * delta > w ?github

w - x * delta : delta; mFoldRectArray[x] = new Rect(x * delta, 0, x * delta + deltap, h); } else { int deltap = (x + 1) * delta > h ?web

h - x * delta : delta; mFoldRectArray[x] = new Rect(0, x * delta, w, x * delta + deltap); } } if (mIsHorizontal) { mFoldMaxHeight = h; mFoldMaxWidth = delta; } else { mFoldMaxHeight = delta; mFoldMaxWidth = w; } mIsFoldPrepared = true; } /* * Calculates the transformation matrices used to draw each of the separate folding * segments from this view. */ private void calculateMatrices() { mShouldDraw = true; if (!mIsFoldPrepared) { return; } /** If the fold factor is 1 than the folding view should not be seen * and the canvas can be left completely empty. */ if (mFoldFactor == 1) { mShouldDraw = false; return; } if (mFoldFactor == 0 && mPreviousFoldFactor > 0) { mFoldListener.onEndFold(); } if (mPreviousFoldFactor == 0 && mFoldFactor > 0) { mFoldListener.onStartFold(); } mPreviousFoldFactor = mFoldFactor; /* Reset all the transformation matrices back to identity before computing * the new transformation */ for (int x = 0; x < mNumberOfFolds; x++) { mMatrix[x].reset(); } float cTranslationFactor = 1 - mFoldFactor; float translatedDistance = mIsHorizontal ? mOriginalWidth * cTranslationFactor : mOriginalHeight * cTranslationFactor; float translatedDistancePerFold = Math.round(translatedDistance / mNumberOfFolds); /* For an odd number of folds, the rounding error may cause the * translatedDistancePerFold to be grater than the max fold width or height. */ mFoldDrawWidth = mFoldMaxWidth < translatedDistancePerFold ?express

translatedDistancePerFold : mFoldMaxWidth; mFoldDrawHeight = mFoldMaxHeight < translatedDistancePerFold ? translatedDistancePerFold : mFoldMaxHeight; float translatedDistanceFoldSquared = translatedDistancePerFold * translatedDistancePerFold; /* Calculate the depth of the fold into the screen using pythagorean theorem. */ float depth = mIsHorizontal ?apache

(float)Math.sqrt((double)(mFoldDrawWidth * mFoldDrawWidth - translatedDistanceFoldSquared)) : (float)Math.sqrt((double)(mFoldDrawHeight * mFoldDrawHeight - translatedDistanceFoldSquared)); /* The size of some object is always inversely proportional to the distance * it is away from the viewpoint. The constant can be varied to to affect the * amount of perspective. */ float scaleFactor = DEPTH_CONSTANT / (DEPTH_CONSTANT + depth); float scaledWidth, scaledHeight, bottomScaledPoint, topScaledPoint, rightScaledPoint, leftScaledPoint; if (mIsHorizontal) { scaledWidth = mFoldDrawWidth * cTranslationFactor; scaledHeight = mFoldDrawHeight * scaleFactor; } else { scaledWidth = mFoldDrawWidth * scaleFactor; scaledHeight = mFoldDrawHeight * cTranslationFactor; } topScaledPoint = (mFoldDrawHeight - scaledHeight) / 2.0f; bottomScaledPoint = topScaledPoint + scaledHeight; leftScaledPoint = (mFoldDrawWidth - scaledWidth) / 2.0f; rightScaledPoint = leftScaledPoint + scaledWidth; float anchorPoint = mIsHorizontal ?canvas

mAnchorFactor * mOriginalWidth : mAnchorFactor * mOriginalHeight; /* The fold along which the anchor point is located. */ float midFold = mIsHorizontal ?api

(anchorPoint / mFoldDrawWidth) : anchorPoint / mFoldDrawHeight; mSrc[0] = 0; mSrc[1] = 0; mSrc[2] = 0; mSrc[3] = mFoldDrawHeight; mSrc[4] = mFoldDrawWidth; mSrc[5] = 0; mSrc[6] = mFoldDrawWidth; mSrc[7] = mFoldDrawHeight; /* Computes the transformation matrix for each fold using the values calculated above. */ for (int x = 0; x < mNumberOfFolds; x++) { boolean isEven = (x % 2 == 0); if (mIsHorizontal) { mDst[0] = (anchorPoint > x * mFoldDrawWidth) ?數組

anchorPoint + (x - midFold) * scaledWidth : anchorPoint - (midFold - x) * scaledWidth; mDst[1] = isEven ? 0 : topScaledPoint; mDst[2] = mDst[0]; mDst[3] = isEven ? mFoldDrawHeight: bottomScaledPoint; mDst[4] = (anchorPoint > (x + 1) * mFoldDrawWidth) ? anchorPoint + (x + 1 - midFold) * scaledWidth : anchorPoint - (midFold - x - 1) * scaledWidth; mDst[5] = isEven ?

topScaledPoint : 0; mDst[6] = mDst[4]; mDst[7] = isEven ?

bottomScaledPoint : mFoldDrawHeight; } else { mDst[0] = isEven ? 0 : leftScaledPoint; mDst[1] = (anchorPoint > x * mFoldDrawHeight) ?

anchorPoint + (x - midFold) * scaledHeight : anchorPoint - (midFold - x) * scaledHeight; mDst[2] = isEven ?

leftScaledPoint: 0; mDst[3] = (anchorPoint > (x + 1) * mFoldDrawHeight) ? anchorPoint + (x + 1 - midFold) * scaledHeight : anchorPoint - (midFold - x - 1) * scaledHeight; mDst[4] = isEven ?

mFoldDrawWidth : rightScaledPoint; mDst[5] = mDst[1]; mDst[6] = isEven ? rightScaledPoint : mFoldDrawWidth; mDst[7] = mDst[3]; } /* Pixel fractions are present for odd number of folds which need to be * rounded off here.*/ for (int y = 0; y < 8; y ++) { mDst[y] = Math.round(mDst[y]); } /* If it so happens that any of the folds have reached a point where * the width or height of that fold is 0, then nothing needs to be * drawn onto the canvas because the view is essentially completely * folded.*/ if (mIsHorizontal) { if (mDst[4] <= mDst[0] || mDst[6] <= mDst[2]) { mShouldDraw = false; return; } } else { if (mDst[3] <= mDst[1] || mDst[7] <= mDst[5]) { mShouldDraw = false; return; } } /* Sets the shadow and bitmap transformation matrices.*/ mMatrix[x].setPolyToPoly(mSrc, 0, mDst, 0, NUM_OF_POLY_POINTS / 2); } /* The shadows on the folds are split into two parts: Solid shadows and gradients. * Every other fold has a solid shadow which overlays the whole fold. Similarly, * the folds in between these alternating folds also have an overlaying shadow. * However, it is a gradient that takes up part of the fold as opposed to a solid * shadow overlaying the whole fold.*/ /* Solid shadow paint object. */ int alpha = (int) (mFoldFactor * 255 * SHADING_ALPHA); mSolidShadow.setColor(Color.argb(alpha, 0, 0, 0)); if (mIsHorizontal) { mShadowGradientMatrix.setScale(mFoldDrawWidth, 1); mShadowLinearGradient.setLocalMatrix(mShadowGradientMatrix); } else { mShadowGradientMatrix.setScale(1, mFoldDrawHeight); mShadowLinearGradient.setLocalMatrix(mShadowGradientMatrix); } mGradientShadow.setAlpha(alpha); } @Override protected void dispatchDraw(Canvas canvas) { /** If prepareFold has not been called or if preparation has not completed yet, * then no custom drawing will take place so only need to invoke super's * onDraw and return. */ if (!mIsFoldPrepared || mFoldFactor == 0) { super.dispatchDraw(canvas); return; } if (!mShouldDraw) { return; } Rect src; /* Draws the bitmaps and shadows on the canvas with the appropriate transformations. */ for (int x = 0; x < mNumberOfFolds; x++) { src = mFoldRectArray[x]; /* The canvas is saved and restored for every individual fold*/ canvas.save(); /* Concatenates the canvas with the transformation matrix for the * the segment of the view corresponding to the actual image being * displayed. */ canvas.concat(mMatrix[x]); if (FoldingLayoutActivity.IS_JBMR2) { mDstRect.set(0, 0, src.width(), src.height()); canvas.drawBitmap(mFullBitmap, src, mDstRect, null); } else { /* The same transformation matrix is used for both the shadow and the image * segment. The canvas is clipped to account for the size of each fold and * is translated so they are drawn in the right place. The shadow is then drawn on * top of the different folds using the sametransformation matrix.*/ canvas.clipRect(0, 0, src.right - src.left, src.bottom - src.top); if (mIsHorizontal) { canvas.translate(-src.left, 0); } else { canvas.translate(0, -src.top); } super.dispatchDraw(canvas); if (mIsHorizontal) { canvas.translate(src.left, 0); } else { canvas.translate(0, src.top); } } /* Draws the shadows corresponding to this specific fold. */ if (x % 2 == 0) { canvas.drawRect(0, 0, mFoldDrawWidth, mFoldDrawHeight, mSolidShadow); } else { canvas.drawRect(0, 0, mFoldDrawWidth, mFoldDrawHeight, mGradientShadow); } canvas.restore(); } } }

  
 在變量中。看到使用了Rect、Matrix、LinearGradient、Bitmap等graphics類,主要是負責畫圖。固然還有最基礎的canvas和paint。在FoldingLayout中。大致可以分爲三個部分,分別以三個方法爲表明:
一、prepareFold(Orientation orientation, float anchorFactor, int numberOfFolds)
     初始化準備。爲變量賦值
二、calculateMatrices()
     計算轉換矩陣,用於繪製每一個單獨的摺疊部分
三、dispatchDraw(Canvas canvas)
      重載ViewGroup的方法,繪製圖形

  而後進入各個方法。在calculateMatrices()方法的最後,有一個如下的方法: 
  /* Sets the shadow and bitmap transformation matrices.*/
  mMatrix[x].setPolyToPoly(mSrc, 0, mDst, 0, NUM_OF_POLY_POINTS / 2);
  這種方法是Matrix類裏面的,那它是作什麼用的呢?首先確定是對矩陣進行變換,那會是什麼效果呢?結合摺疊的效果來講一下。在api中,這個類是這樣描寫敘述的,

  這種方法就是設置指定的矩陣的src點映射到指定的dst點。這些點表明一個float數組,每一點由兩個float值組成。比方一個矩形,用數組可以這樣表示:[x0,y0,x1,y1,x2,y2,x3,y3],圖示:
   再來回想一下摺疊效果是怎樣實現的。可以把它拆分紅例如如下圖所看到的:

   對於摺疊一次的圖形,可以把其分紅兩個部分,也就是先對圖形進行分割,以後再對每一個圖形作拉伸變換,因此很是easy想到使用Matrix類實現。用的正是setPolyToPoly()方法。如下以一個實例說一下setPolyToPoly()方法的使用,代碼例如如下:
package com.example.demo;

import android.support.v7.app.ActionBarActivity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.os.Bundle;
import android.view.View;


public class MainActivity extends ActionBarActivity {

     @Override
     protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(new MyView(this));

     }

     class MyView extends View {
          private Bitmap bitmap;
          private Matrix mMatrix;
          public MyView(Context context) {
               super(context);
               bitmap = BitmapFactory.decodeResource(context.getResources(),
                         R.drawable.a);
               mMatrix = new Matrix();
          }

          @Override
          protected void onDraw(Canvas canvas) {
               float[] src = new float[] { 0, 0, // 左上
                         bitmap.getWidth(), 0,// 右上
                         bitmap.getWidth(), bitmap.getHeight(),// 右下
                         0, bitmap.getHeight() };// 左下
               float[] dst = new float[] { 0, 0, bitmap.getWidth(), 50,
                         bitmap.getWidth(), bitmap.getHeight() - 50, 0,
                         bitmap.getHeight() };
               mMatrix.setPolyToPoly(src, 0, dst, 0, src.length >> 1);
               canvas.drawBitmap(bitmap, mMatrix, null);
               canvas.drawBitmap(bitmap, 0, bitmap.getHeight(), null);
          }
     }
}
  在數組中,設置的矩形四個頂點的座標,在目的矩形的座標中,改變了右上以及右下的座標,使其分別向下和向上移動50像素。因此效果例如如下:

 可以看到上圖正是一個摺疊效果(看圖別暈。大天然是否是真的很是奇妙)。

FoldingLayout就是在這個基礎之上延伸而來的,接下來就看看它是怎樣具體實現的。

一、首先看一下  方法,
  /**
     * This method is called in order to update the fold's orientation, anchor
     * point and number of folds. This creates the necessary setup in order to
     * prepare the layout for a fold with the specified parameters. Some of the
     * dimensions required for the folding transformation are also acquired here.
     *
     * After this method is called, it will be in a completely unfolded state by default.
     */
    private void prepareFold(Orientation orientation, float anchorFactor, int numberOfFolds) {
        //setPolyToPoly方法參數
        mSrc = new float[NUM_OF_POLY_POINTS];
        mDst = new float[NUM_OF_POLY_POINTS];
        //
        mDstRect = new Rect();

        mFoldFactor = 0;
        mPreviousFoldFactor = 0;
        //是否摺疊
        mIsFoldPrepared = false;
        //畫筆
        mSolidShadow = new Paint();
        mGradientShadow = new Paint();
        //方向
        mOrientation = orientation;
        mIsHorizontal = (orientation == Orientation.HORIZONTAL);
        //摺疊線附近的效果
        if (mIsHorizontal) {
            mShadowLinearGradient = new LinearGradient(0, 0, SHADING_FACTOR, 0, Color.RED,
                    Color.TRANSPARENT, TileMode.CLAMP);
        } else {
            mShadowLinearGradient = new LinearGradient(0, 0, 0, SHADING_FACTOR, Color.BLACK,
                    Color.TRANSPARENT, TileMode.CLAMP);
        }
        //摺疊線附近的效果
        mGradientShadow.setStyle(Style.FILL);
        mGradientShadow.setShader(mShadowLinearGradient);
        mShadowGradientMatrix = new Matrix();

        mAnchorFactor = anchorFactor;
        mNumberOfFolds = numberOfFolds;

        mOriginalWidth = getMeasuredWidth();
        Log.i(" mOriginalWidth",  mOriginalWidth+"");
        mOriginalHeight = getMeasuredHeight();
        Log.i(" mOriginalHeight",  mOriginalHeight+"");
       
        mFoldRectArray = new Rect[mNumberOfFolds];
        mMatrix = new Matrix [mNumberOfFolds];

        for (int x = 0; x < mNumberOfFolds; x++) {
            mMatrix[x] = new Matrix();
        }

        int h = mOriginalHeight;
        int w = mOriginalWidth;

       
        if (FoldingLayoutActivity.IS_JBMR2) {
            mFullBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(mFullBitmap);
            getChildAt(0).draw(canvas);
            Log.i("IS_JBMR2", FoldingLayoutActivity.IS_JBMR2+"");
        }
        //摺疊成幾個部分,每隔部分的寬
        int delta = Math.round(mIsHorizontal ? ((float) w) / ((float) mNumberOfFolds) :
                ((float) h) /((float) mNumberOfFolds));
        Log.i("delta", delta+"");
        /* Loops through the number of folds and segments the full layout into a number
         * of smaller equal components. If the number of folds is odd, then one of the
         * components will be smaller than all the rest. Note that deltap below handles
         * the calculation for an odd number of folds.*/
        for (int x = 0; x < mNumberOfFolds; x++) {
            if (mIsHorizontal) {
                int deltap = (x + 1) * delta > w ? w - x * delta : delta;
                Log.i("deltap", x+"x"+deltap+"");
                mFoldRectArray[x] = new Rect(x * delta, 0, x * delta + deltap, h);
            } else {
                int deltap = (x + 1) * delta > h ?

h - x * delta : delta; mFoldRectArray[x] = new Rect(0, x * delta, w, x * delta + deltap); } } if (mIsHorizontal) { mFoldMaxHeight = h; mFoldMaxWidth = delta; } else { mFoldMaxHeight = delta; mFoldMaxWidth = w; } //設置已經準備就緒 mIsFoldPrepared = true; }

  這種方法從名字就可以看出來是用於數據初始化。開始時初始化setPolyToPoly方法參數數組,而後設置了畫筆、摺疊方向、摺疊線附近的摺疊效果燈。

最重要的就是初始化這兩個參數:

  private Rect[] mFoldRectArray:
  private Matrix [] mMatrix:
 他們分別依據摺疊的數目來賦值。 

二、calculateMatrices()
  這種方法是計算的核心部分。
  /*
      * Calculates the transformation matrices used to draw each of the separate
      * folding segments from this view.
      */
     private void calculateMatrices() {
            // 設置可以畫圖了
            mShouldDraw = true;
            // 沒有準備好 返回
            if (! mIsFoldPrepared) {
                 return;
           }

            /**
            * If the fold factor is 1 than the folding view should not be seen and
            * the canvas can be left completely empty.
            */
            if ( mFoldFactor == 1) {
                 // 已經全然摺疊了不需要繪製了
                 mShouldDraw = false;
                 return;
           }
            // 摺疊結束
            if ( mFoldFactor == 0 && mPreviousFoldFactor > 0) {
                 mFoldListener.onEndFold();
           }
            // 摺疊開始
            if ( mPreviousFoldFactor == 0 && mFoldFactor > 0) {
                 mFoldListener.onStartFold();
           }
            // 已經摺疊了,將當前的摺疊因子賦值到已經摺疊過的摺疊因子
            mPreviousFoldFactor = mFoldFactor;

            /*
            * Reset all the transformation matrices back to identity before
            * computing the new transformation
            */
            for ( int x = 0; x < mNumberOfFolds; x++) {
                 mMatrix[x].reset();
           }
        //依據摺疊因子來獲得相對係數
            float cTranslationFactor = 1 - mFoldFactor;
           Log. i("cTranslationFactor" , cTranslationFactor + "" );
            //計算整體變換的距離
            float translatedDistance = mIsHorizontal ?

mOriginalWidth * cTranslationFactor : mOriginalHeight * cTranslationFactor; Log. i("translatedDistance" , translatedDistance + "" ); //獲得每一個摺疊視圖變換的移動距離 float translatedDistancePerFold = Math.round(translatedDistance / mNumberOfFolds); Log. i("translatedDistancePerFold" , translatedDistancePerFold + "" ); /* * For an odd number of folds, the rounding error may cause the * translatedDistancePerFold to be grater than the max fold width or * height. */ Log. i("mFoldMaxWidth" , mFoldMaxWidth + "" ); //計算出被畫視圖的寬度 mFoldDrawWidth = mFoldMaxWidth < translatedDistancePerFold ? translatedDistancePerFold : mFoldMaxWidth; Log. i("mFoldDrawWidth" , mFoldDrawWidth + "" ); //計算出被畫視圖的高度 mFoldDrawHeight = mFoldMaxHeight < translatedDistancePerFold ? translatedDistancePerFold : mFoldMaxHeight; Log. i("mFoldDrawHeight" , mFoldDrawHeight + "" ); float translatedDistanceFoldSquared = translatedDistancePerFold * translatedDistancePerFold; Log. i("translatedDistanceFoldSquared" , translatedDistanceFoldSquared + ""); /* * Calculate the depth of the fold into the screen using pythagorean * theorem. * 依據勾股定理計算出摺疊效果的深度 */ float depth = mIsHorizontal ? ( float) Math . sqrt((double) (mFoldDrawWidth * mFoldDrawWidth - translatedDistanceFoldSquared)) : ( float) Math . sqrt((double) (mFoldDrawHeight * mFoldDrawHeight - translatedDistanceFoldSquared)); Log. i("depth" , depth + "" ); /* * The size of some object is always inversely proportional to the * distance it is away from the viewpoint. The constant can be varied to * to affect the amount of perspective. * 這個地方又使用了一個因子,它是依據深度depth來獲得的。主要也是用來計算相對的值,體現在高度上。 */ float scaleFactor = DEPTH_CONSTANT / ( DEPTH_CONSTANT + depth); Log. i("scaleFactor" , scaleFactor + "" ); float scaledWidth, scaledHeight, bottomScaledPoint, topScaledPoint, rightScaledPoint, leftScaledPoint; if ( mIsHorizontal) { //獲得變化後的寬度,事實上和translatedDistance相差無幾 scaledWidth = mFoldDrawWidth * cTranslationFactor; Log. i("scaledWidth" , scaledWidth + "" ); //獲得變化後的高度 scaledHeight = mFoldDrawHeight * scaleFactor; Log. i("scaledHeight" , scaledHeight + "" ); } else { scaledWidth = mFoldDrawWidth * scaleFactor; scaledHeight = mFoldDrawHeight * cTranslationFactor; } //依據變化後的高度和寬度來計算出座標點 topScaledPoint = ( mFoldDrawHeight - scaledHeight) / 2.0f; bottomScaledPoint = topScaledPoint + scaledHeight; leftScaledPoint = ( mFoldDrawWidth - scaledWidth) / 2.0f; rightScaledPoint = leftScaledPoint + scaledWidth; //錨點計算 float anchorPoint = mIsHorizontal ? mAnchorFactor * mOriginalWidth : mAnchorFactor * mOriginalHeight; Log. i("anchorPoint" , anchorPoint + "" ); /* The fold along which the anchor point is located. */ float midFold = mIsHorizontal ? (anchorPoint / mFoldDrawWidth) : anchorPoint / mFoldDrawHeight; //一下的座標你們必定很是熟悉,就是以前介紹的方法的參數 表示單個摺疊視圖 mSrc[0] = 0; mSrc[1] = 0; mSrc[2] = 0; mSrc[3] = mFoldDrawHeight; mSrc[4] = mFoldDrawWidth; mSrc[5] = 0; mSrc[6] = mFoldDrawWidth; mSrc[7] = mFoldDrawHeight; /* * Computes the transformation matrix for each fold using the values * calculated above. */ for ( int x = 0; x < mNumberOfFolds; x++) { boolean isEven = (x % 2 == 0); if ( mIsHorizontal) { mDst[0] = (anchorPoint > x * mFoldDrawWidth) ? anchorPoint + (x - midFold) * scaledWidth : anchorPoint - (midFold - x) * scaledWidth; Log. i("mDst[0]" , mDst [0] + "" ); mDst[1] = isEven ? 0 : topScaledPoint; Log. i("mDst[1]" , mDst [1] + "" ); mDst[2] = mDst[0]; Log. i("mDst[2]" , mDst [2] + "" ); mDst[3] = isEven ?

mFoldDrawHeight : bottomScaledPoint; Log. i("mDst[3]" , mDst [4] + "" ); mDst[4] = (anchorPoint > (x + 1) * mFoldDrawWidth) ? anchorPoint + (x + 1 - midFold) * scaledWidth : anchorPoint - (midFold - x - 1) * scaledWidth; Log. i("mDst[4]" , mDst [4] + "" ); mDst[5] = isEven ? topScaledPoint : 0; Log. i("mDst[5]" , mDst [5] + "" ); mDst[6] = mDst[4]; Log. i("mDst[6]" , mDst [6] + "" ); mDst[7] = isEven ? bottomScaledPoint : mFoldDrawHeight; Log. i("mDst[7]" , mDst [7] + "" ); } else { mDst[0] = isEven ? 0 : leftScaledPoint; mDst[1] = (anchorPoint > x * mFoldDrawHeight) ? anchorPoint + (x - midFold) * scaledHeight : anchorPoint - (midFold - x) * scaledHeight; mDst[2] = isEven ?

leftScaledPoint : 0; mDst[3] = (anchorPoint > (x + 1) * mFoldDrawHeight) ?

anchorPoint + (x + 1 - midFold) * scaledHeight : anchorPoint - (midFold - x - 1) * scaledHeight; mDst[4] = isEven ? mFoldDrawWidth : rightScaledPoint; mDst[5] = mDst[1]; mDst[6] = isEven ? rightScaledPoint : mFoldDrawWidth; mDst[7] = mDst[3]; } /* * Pixel fractions are present for odd number of folds which need to * be rounded off here. */ for ( int y = 0; y < 8; y++) { mDst[y] = Math. round(mDst[y]); } /* * If it so happens that any of the folds have reached a point where * the width or height of that fold is 0, then nothing needs to be * drawn onto the canvas because the view is essentially completely * folded. */ if ( mIsHorizontal) { if ( mDst[4] <= mDst[0] || mDst[6] <= mDst[2]) { mShouldDraw = false; return; } } else { if ( mDst[3] <= mDst[1] || mDst[7] <= mDst[5]) { mShouldDraw = false; return; } } /* Sets the shadow and bitmap transformation matrices. */ mMatrix[x].setPolyToPoly( mSrc, 0, mDst, 0, NUM_OF_POLY_POINTS / 2); } /* * The shadows on the folds are split into two parts: Solid shadows and * gradients. Every other fold has a solid shadow which overlays the * whole fold. Similarly, the folds in between these alternating folds * also have an overlaying shadow. However, it is a gradient that takes * up part of the fold as opposed to a solid shadow overlaying the whole * fold. */ /* Solid shadow paint object. */ int alpha = ( int) ( mFoldFactor * 255 * SHADING_ALPHA); mSolidShadow.setColor(Color. argb(alpha, 0, 0, 0)); if ( mIsHorizontal) { mShadowGradientMatrix.setScale( mFoldDrawWidth, 1); mShadowLinearGradient.setLocalMatrix( mShadowGradientMatrix); } else { mShadowGradientMatrix.setScale(1, mFoldDrawHeight); mShadowLinearGradient.setLocalMatrix( mShadowGradientMatrix); } mGradientShadow.setAlpha(alpha); }

  首先依據mFoldFactor獲得cTranslationFactor。mFoldFactor是依據手指滑動的距離獲得的。是一個很是小的數值,假設滑動距離小的話每次僅僅有0.0幾的變化。因此獲得的cTranslationFactor就像是一個百分比同樣,來計算相對的數值。而後依據cTranslationFactor計算出translatedDistance、mFoldDrawWidth、depth等。

具體說明已經在代碼中凝視了,在此就不細說了,如圖:

三、dispatchDraw(Canvas canvas)
     最後就是把圖形繪製出來。這種方法在以前的本身定義ViewGroup博客中已經提到過。假設不重寫這種方法。那上面的工做室沒法顯示出來的。

看代碼:

@Override
     protected void dispatchDraw(Canvas canvas) {
            /**
            * If prepareFold has not been called or if preparation has not
            * completed yet, then no custom drawing will take place so only need to
            * invoke super's onDraw and return.
            */
            if (! mIsFoldPrepared || mFoldFactor == 0) {
                 super.dispatchDraw(canvas);
                 return;
           }

            if (! mShouldDraw) {
                 return;
           }

           Rect src;
            /*
            * Draws the bitmaps and shadows on the canvas with the appropriate
            * transformations.
            */
            for ( int x = 0; x < mNumberOfFolds; x++) {

                src = mFoldRectArray[x];
                 /* The canvas is saved and restored for every individual fold */
                canvas.save();

                 /*
                 * Concatenates the canvas with the transformation matrix for the
                 * the segment of the view corresponding to the actual image being
                 * displayed.
                 */
                canvas.concat( mMatrix[x]);
                 if (FoldingLayoutActivity. IS_JBMR2) {
                      mDstRect.set(0, 0, src.width(), src.height());
                     canvas.drawBitmap( mFullBitmap, src, mDstRect, null);
                } else {
                      /*
                      * The same transformation matrix is used for both the shadow
                      * and the image segment. The canvas is clipped to account for
                      * the size of each fold and is translated so they are drawn in
                      * the right place. The shadow is then drawn on top of the
                      * different folds using the sametransformation matrix.
                      */
                     canvas.clipRect(0, 0, src. right - src.left, src. bottom
                                - src. top);

                      if ( mIsHorizontal) {
                           canvas.translate(-src. left, 0);
                     } else {
                           canvas.translate(0, -src. top);
                     }

                      super.dispatchDraw(canvas);

                      if ( mIsHorizontal) {
                           canvas.translate(src. left, 0);
                     } else {
                           canvas.translate(0, src. top);
                     }
                }
                 /* Draws the shadows corresponding to this specific fold. */
                 if (x % 2 == 0) {
                     canvas.drawRect(0, 0, mFoldDrawWidth, mFoldDrawHeight,
                                 mSolidShadow);
                } else {
                     canvas.drawRect(0, 0, mFoldDrawWidth, mFoldDrawHeight,
                                 mGradientShadow);
                }
                canvas.restore();
           }
     }
  事實上這種方法最基本的兩個地方就是,canvas.concat( mMatrix[x])和canvas.clipRect(0, 0, src. right - src.left, src. bottom- src. top)。

首先在最外層是一個for循環。這個循環依據摺疊的數目迭代,本例中這個數值爲2,因此將迭代兩次。接着從mFoldRectArray數組中取出一個Rect賦值給src。而後就調用canvas.concat( mMatrix[x])方法了。咱們知道,這個Matrix剛纔已經使用setPolyToPoly方法進行變換了。那怎樣將變換後的矩陣的效果應用到視圖上呢,就是使用canvas.concat( mMatrix[x])方法,假設沒有這種方法。那就不會看到摺疊的效果。而後就是canvas.clipRect(0, 0, src.right - src.left, src.bottom - src.top)方法了,這種方法用於對圖形進行裁剪。顯示出每一個摺疊的視圖。需要注意的地方是這種方法要在興許畫畫操做以前進行剪切才幹生效,這也就是爲何這種方法在super.dispatchDraw(canvas)以前調用。接着還需要對視圖進行平移操做,canvas.translate(-src.left, 0)。否則的話會顯示兩個一樣的圖片。

相關文章
相關標籤/搜索