上一篇文章,我們利用Matrix的setPolyToPoly來實現圖片的3D旋轉,這一次,我們來實現一個漂亮一點的效果,讓一張圖片像摺扇一樣可以摺疊起來。
具體的效果如下
這個效果是我有一次在DevBytes上看到的一個視頻,由Google Android Team的員工介紹的一個效果,不過它們是把這個做成了一個可重複利用的自定義ViewGroup,我當時看了,發現這效果真是太帥了。於是自己就琢磨着應該怎麼實現,不過最後,還是跑去GitHub下了它的一份代碼,參考着,爭取把裏面主要的邏輯給理清了,給大家介紹一下。
其實我之所以寫前面那篇文章《Android學習小Demo(5)結合Matrix跟Porperty Animatin 實現推拉門效果》,目的只是爲了先讓大家先熟悉一下matrix的setPolyToPoly方法,因爲這個效果的實現就是利用matrix的這個方法的。
下面我們結合一下代碼來講一下思路,然後在最後,大家再下載源代碼去學習吧。
在主Activity上,有一個自定義的FoldingView,主要是實現摺疊效果的自定義View,下面有一個輸入框,用戶可以輸入數字,表明是要將這張圖片分成多少部分,然後點擊按鈕,開始動畫。
1)我們先看一下主Activity中的代碼:
- public class MainActivity extends Activity {
- private ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f,1f);
- private PolyToPolyView polyView;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- ...
- valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator arg0) {
- float rotateFactor = (Float)arg0.getAnimatedValue();
- polyView.setRotateFactor(rotateFactor);
- }
- });
- ...
- btnRotate.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- valueAnimator.start();
- }
- });
- }
- ...
- }
這代碼其實跟我們前面一篇文章的代碼是一樣的:
a)定義一個ValueAnimator,在其AnimatorUpdateListener中設置自定義View的旋轉因子,並設置圖片摺疊的份數
b)點擊按鈕,開始動畫。
2)在自定義View中,
a)我們會從資源中獲取一張圖片,然後根據Activity中輸入框的值,將圖片分成等寬的長方形,如下:
代碼如下:
- bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.photo1);
- bitmapWidth = bitmap.getWidth();
- bitmapHeight = bitmap.getHeight();
- widthPerFold = Math.round((float)bitmapWidth /(float)folds);
- heightPerFold = bitmapHeight;
- for (int i = 0; i < folds; i++) {
- rects[i] = new Rect(i * widthPerFold, 0, i* widthPerFold + widthPerFold, heightPerFold);
- }
- for(int i=0;i<folds;i++){
- matrices[i] = new Matrix();
- }
2)分成相同的等份之後,我們就要考慮如何爲每一個長方形設置變化的矩陣了。
2.1)分析拆分出來的矩形區域及摺疊時候的效果,可以發現,偶數位(從0開始)的矩形是右邊的那兩個角往後推,而奇數位的矩形則剛好相反,當偶數位的矩形在往後推的時候,奇數位的矩形則相對着其也在往後推,並且在往後推的同時,每個矩形的寬度縮小的比例也是一致的。所以根據這幾點,我們可以先算出一些公用的參數變化,比如每個矩形旋轉的比例,平移的距離等等,下面看一下代碼:
- translateFactor = 1 - foldFactor;
- translateWidth = bitmapWidth * translateFactor;
- translateWidthPerFold = Math.round(translateWidth / folds);
- foldDrawWidth = widthPerFold < translateWidthPerFold ? translateWidthPerFold : widthPerFold;
- foldDrawHeight = heightPerFold;
- float translateWidthPerfoldsquare = translateWidthPerFold * translateWidthPerFold;
- float deepth = (float)Math.sqrt(foldDrawWidth * foldDrawWidth - translateWidthPerfoldsquare);
- scaleFactor = DEPTH_CONSTANT / (DEPTH_CONSTANT + deepth);
- float scaleWidth = foldDrawWidth * translateFactor; // from 1 to 0, means width becomes small
- float scaleHeight = foldDrawHeight * scaleFactor;
- float topScaleHeightPoint = (foldDrawHeight - scaleHeight) / 2.f;
- float bottomScaleHeightPoint = topScaleHeightPoint + scaleHeight;
- srcPoly[0] = 0;
- srcPoly[1] = 0;
- srcPoly[2] = 0;
- srcPoly[3] = foldDrawHeight;
- srcPoly[4] = foldDrawWidth;
- srcPoly[5] = 0;
- srcPoly[6] = foldDrawWidth;
- srcPoly[7] = foldDrawHeight;
- for (int i = 0; i < folds; i++) {
- matrices[i].reset();
- boolean isEven = (i % 2 == 0);
- dstPoly[0] = i * scaleWidth;
- dstPoly[1] = isEven ? 0 : topScaleHeightPoint;
- dstPoly[2] = dstPoly[0];
- dstPoly[3] = isEven ? foldDrawHeight : bottomScaleHeightPoint;
- dstPoly[4] = (i + 1) * scaleWidth;
- dstPoly[5] = isEven ? topScaleHeightPoint : 0;
- dstPoly[6] = dstPoly[4];
- dstPoly[7] = isEven ? bottomScaleHeightPoint : foldDrawHeight;
- if(dstPoly[4] <= dstPoly[0] || dstPoly[6] <= dstPoly[2]){
- shouldDraw = false;
- return;
- }
- matrices[i].setPolyToPoly(srcPoly, 0, dstPoly, 0, POLY_POINTS / 2);
- }
大家如果仔細看一下,會發現前面計算縮放比例及深度變化等,都跟前面的文章是一樣的,關鍵是後面設置座標數組的時候,會根據奇偶來判斷。
2.2)在數組中,前面4位,分別是左上角,左下角的x,y座標,後面下位,則是右上角和右下角的座標。對於偶數位矩形來說,在變化的過程中,其x座標會根據平移和縮放的比例慢慢縮小並往左移,而左邊的y座標則是保持不變的,因爲它們是這個矩形的軸,而右邊的y座標,則會根據縮放比例變小,而對於奇數位來說,則剛好相反。
3)第三步,分別利用canvas的save和restore函數保存各個矩形自己的matrix變化,利用clipRect剪裁出各個矩形區域,交將圖片的對應的部分畫到canvas上。
4)加上一些陰影和漸變交果,讓其看起來是有縱容變化的感覺。
- int alpha = (int) (foldFactor * 255 * SHADOW_APLHA);
- paintSolid.setColor(Color.argb(alpha, 0, 0, 0));
- matrixShadowGradient.setScale(foldDrawWidth, 1);
- linearGradientShadow.setLocalMatrix(matrixShadowGradient);
- paintGradientShadow.setAlpha(alpha);
- ...
- if (i % 2 == 0) {
- canvas.drawRect(0, 0, foldDrawWidth, foldDrawHeight, paintSolid);
- } else {
- canvas.drawRect(0, 0, foldDrawWidth, foldDrawHeight, paintGradientShadow);
- }