1、概述
無意中翻到的FoldingLayout的介紹的部落格,以及github地址。感覺很nice呀,於是花了點時間研究以及編寫,本篇部落格將帶大家從最基本的原理分析,一步一步的實現我們的FoldingLayout,當然了,如果你能力過硬,可以直接下載github上的程式碼進行學習。
部落格基本分為以下幾個部分:
1、Matrix的setPolyToPoly使用
2、在圖片上使用漸變和陰影
3、初步的FoldingLayout的實現,完成圖片的摺疊顯示(可控制摺疊次數、包含陰影的繪製)
4、引入手勢,手指可以可以FoldingLayout的摺疊
5、結合DrawerLayout實現摺疊式側滑
6、結合SlidingPaneLayout實現摺疊式側滑
ok,貼下部分的效果圖:
改圖對應上述3,妹子不錯吧~
ok,對應上述4.
對應上述5。
ok,挑選了部分圖,不然太佔篇幅了。
那麼接下來,我們就按照順序往下學習了~~~
2、Matrix的setPolyToPoly使用
想要實現摺疊,最重要的就是其核心的原理了,那麼第一步我們要了解的就是,如何能把一張正常顯示的圖片,讓它能夠進行偏移顯示。
其實精髓就在於Matrix的setPolyToPoly的方法。
1 |
public boolean setPolyToPoly(float[] src, int srcIndex, float[] dst, int dstIndex,int pointCount) |
簡單看一下該方法的引數,src代表變換前的座標;dst代表變換後的座標;從src到dst的變換,可以通過srcIndex和dstIndex來制定第一個變換的點,一般可能都設定位0。pointCount代表支援的轉換座標的點數,最多支援4個。
如果不明白沒事,下面通過一個簡單的例子,帶大家瞭解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
package com.zhy.sample.folderlayout; import android.app.Activity; 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 MatrixPolyToPolyActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new PolyToPolyView(this)); } class PolyToPolyView extends View { private Bitmap mBitmap; private Matrix mMatrix; public PolyToPolyView(Context context) { super(context); mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.tanyan); mMatrix = new Matrix(); float[] src = { 0, 0,// mBitmap.getWidth(), 0,// mBitmap.getWidth(), mBitmap.getHeight(),// 0, mBitmap.getHeight() }; float[] dst = { 0, 0,// mBitmap.getWidth(), 100,// mBitmap.getWidth(), mBitmap.getHeight() - 100,// 0, mBitmap.getHeight() }; mMatrix.setPolyToPoly(src, 0, dst, 0, src.length >> 1); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawBitmap(mBitmap, mMatrix, null); } } } |
我們編寫了一個PolyToPolyView作為我們的Activity的主檢視。
在PolyToPolyView中,我們載入了一張圖片,初始化我們的Matrix,注意src和dst兩個陣列,src就是正常情況下圖片的4個頂點。dst將圖片右側兩個點的y座標做了些許的修改。
大家可以在紙上稍微標一下src和dst的四個點的位置。
最後我們在onDraw的時候進行影像的繪製,效果為:
如果你已經在紙上稍微的畫了dst的四個點,那麼這個結果你一定不陌生。
可以看到我們通過matrix.setPolyToPoly實現了圖片的傾斜,那麼引入到摺疊的情況,假設摺疊兩次,大家有思路麼,考慮一下,沒有的話,繼續往下看。
3、引入陰影
其實陰影應該在實現初步的摺疊以後來說,這樣演示其實比較方便,但是為了降低其理解的簡單性,我們先把陰影抽取出來說。
假設我們現在要給上圖加上陰影,希望的效果圖是這樣的:
可以看到我們左側加入了一點陰影,怎麼實現呢?
主要還是利用LinearGradient,我們從左到右新增一層從黑色到透明的漸變即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
public class MatrixPolyToPolyWithShadowActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new PolyToPolyView(this)); } class PolyToPolyView extends View { private Bitmap mBitmap; private Matrix mMatrix; private Paint mShadowPaint; private Matrix mShadowGradientMatrix; private LinearGradient mShadowGradientShader; public PolyToPolyView(Context context) { super(context); mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.tanyan); mMatrix = new Matrix(); mShadowPaint = new Paint(); mShadowPaint.setStyle(Style.FILL); mShadowGradientShader = new LinearGradient(0, 0, 0.5f, 0, Color.BLACK, Color.TRANSPARENT, TileMode.CLAMP); mShadowPaint.setShader(mShadowGradientShader); mShadowGradientMatrix = new Matrix(); mShadowGradientMatrix.setScale(mBitmap.getWidth(), 1); mShadowGradientShader.setLocalMatrix(mShadowGradientMatrix); mShadowPaint.setAlpha((int) (0.9*255)); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.save(); float[] src = //...; float[] dst = //...; mMatrix.setPolyToPoly(src, 0, dst, 0, src.length >> 1); canvas.concat(mMatrix); canvas.drawBitmap(mBitmap, 0, 0, null); //繪製陰影 canvas.drawRect(0, 0, mBitmap.getWidth(), mBitmap.getHeight(), mShadowPaint); canvas.restore(); } } } |
重點看mShadowPaint,mShadowGradientShader,mShadowGradientMatrix一個是畫筆,我們為畫筆設定了一個漸變的Shader,這個Shader的引數為
new LinearGradient(0, 0, 0.5f, 0,Color.BLACK, Color.TRANSPARENT, TileMode.CLAMP);
起點(0,0)、終點(0.5f,0);顏色從和BLACK到透明;模式為CLAMP,也就是拉伸最後一個畫素。
這裡你可能會問,這才為0.5個畫素的區域設定了漸變,不對呀,恩,是的,繼續看接下來我們使用了setLocalMatrix(mShadowGradientMatrix);,而這個
mShadowGradientMatrix將和座標擴大了mBitmap.getWidth()倍,也就是說現在設定漸變的區域為(0.5f*mBitmap.getWidth(),0)半張圖的大小,那麼後半張圖呢?
後半張應用CLAMP模式,拉伸的透明。
關於Shader、setLocalMatrix等用法也可以參考:Android BitmapShader 實戰 實現圓形、圓角圖片
4、初步實現摺疊
瞭解了原理以及陰影的繪製以後,接下來要開始學習真正的去摺疊了,我們的目標效果為:
妹子摺疊成了8份,且陰影的範圍為:每個沉下去夾縫的左右兩側,左側黑色半透明遮蓋,右側短距離的黑色到透明陰影(大家可以仔細看)。
現在其實大家以及會將圖片簡單傾斜和新增陰影了,那麼唯一的難點就是怎麼將一張圖分成很多快,我相信每塊的摺疊大家都會。
其實我們可以通過繪製該圖多次,比如第一次繪製往下傾斜;第二次繪製網上傾斜;這樣就和我們標題2的實現類似了,只需要利用setPolyToPoly。
那麼繪製多次,每次顯示肯定不是一整張圖,比如第一次,我只想顯示第一塊,所以我們還需要clipRect的配合,說到這,應該以及揭祕了~~~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
package com.zhy.sample.folderlayout; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; 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.Shader.TileMode; import android.os.Bundle; import android.view.View; public class SimpleUseActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new PolyToPolyView(this)); } class PolyToPolyView extends View { private static final int NUM_OF_POINT = 8; /** * 圖片的摺疊後的總寬度 */ private int mTranslateDis; /** * 摺疊後的總寬度與原圖寬度的比例 */ private float mFactor = 0.8f; /** * 摺疊塊的個數 */ private int mNumOfFolds = 8; private Matrix[] mMatrices = new Matrix[mNumOfFolds]; private Bitmap mBitmap; /** * 繪製黑色透明區域 */ private Paint mSolidPaint; /** * 繪製陰影 */ private Paint mShadowPaint; private Matrix mShadowGradientMatrix; private LinearGradient mShadowGradientShader; /*** * 原圖每塊的寬度 */ private int mFlodWidth; /** * 摺疊時,每塊的寬度 */ private int mTranslateDisPerFlod; public PolyToPolyView(Context context) { super(context); mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.tanyan); //摺疊後的總寬度 mTranslateDis = (int) (mBitmap.getWidth() * mFactor); //原圖每塊的寬度 mFlodWidth = mBitmap.getWidth() / mNumOfFolds; //摺疊時,每塊的寬度 mTranslateDisPerFlod = mTranslateDis / mNumOfFolds; //初始化matrix for (int i = 0; i < mNumOfFolds; i++) { mMatrices[i] = new Matrix(); } mSolidPaint = new Paint(); int alpha = (int) (255 * mFactor * 0.8f) ; mSolidPaint .setColor(Color.argb((int) (alpha*0.8F), 0, 0, 0)); mShadowPaint = new Paint(); mShadowPaint.setStyle(Style.FILL); mShadowGradientShader = new LinearGradient(0, 0, 0.5f, 0, Color.BLACK, Color.TRANSPARENT, TileMode.CLAMP); mShadowPaint.setShader(mShadowGradientShader); mShadowGradientMatrix = new Matrix(); mShadowGradientMatrix.setScale(mFlodWidth, 1); mShadowGradientShader.setLocalMatrix(mShadowGradientMatrix); mShadowPaint.setAlpha(alpha); //縱軸減小的那個高度,用勾股定理計算下 int depth = (int) Math.sqrt(mFlodWidth * mFlodWidth - mTranslateDisPerFlod * mTranslateDisPerFlod)/2; //轉換點 float[] src = new float[NUM_OF_POINT]; float[] dst = new float[NUM_OF_POINT]; /** * 原圖的每一塊,對應摺疊後的每一塊,方向為左上、右上、右下、左下,大家在紙上自己畫下 */ for (int i = 0; i < mNumOfFolds; i++) { src[0] = i * mFlodWidth; src[1] = 0; src[2] = src[0] + mFlodWidth; src[3] = 0; src[4] = src[2]; src[5] = mBitmap.getHeight(); src[6] = src[0]; src[7] = src[5]; boolean isEven = i % 2 == 0; dst[0] = i * mTranslateDisPerFlod; dst[1] = isEven ? 0 : depth; dst[2] = dst[0] + mTranslateDisPerFlod; dst[3] = isEven ? depth : 0; dst[4] = dst[2]; dst[5] = isEven ? mBitmap.getHeight() - depth : mBitmap .getHeight(); dst[6] = dst[0]; dst[7] = isEven ? mBitmap.getHeight() : mBitmap.getHeight() - depth; //setPolyToPoly mMatrices[i].setPolyToPoly(src, 0, dst, 0, src.length >> 1); } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //繪製mNumOfFolds次 for (int i = 0; i < mNumOfFolds; i++) { canvas.save(); //將matrix應用到canvas canvas.concat(mMatrices[i]); //控制顯示的大小 canvas.clipRect(mFlodWidth * i, 0, mFlodWidth * i + mFlodWidth, mBitmap.getHeight()); //繪製圖片 canvas.drawBitmap(mBitmap, 0, 0, null); //移動繪製陰影 canvas.translate(mFlodWidth * i, 0); if (i % 2 == 0) { //繪製黑色遮蓋 canvas.drawRect(0, 0, mFlodWidth, mBitmap.getHeight(), mSolidPaint); }else { //繪製陰影 canvas.drawRect(0, 0, mFlodWidth, mBitmap.getHeight(), mShadowPaint); } canvas.restore(); } } } } |
簡單講解下,不去管繪製陰影的部分,其實摺疊就是:
1、初始化轉換點,這裡註釋說的很清楚,大家最好在紙上繪製下,標一下每個變數。
2、為matrix.setPolyToPoly
3、繪製時使用該matrix,且clipRect控制顯示區域(這個區域也很簡單,原圖的第一塊到最後一塊),最好就是繪製bitmap了。
陰影這裡大家可以換個明亮點的圖片去看看~~
好了,由於篇幅原因,剩下的內容將在下一篇繼續完成,下一篇將展示如何將簡單的圖片的摺疊,轉化為對一個佈局內所有控制元件的摺疊效果,以及引入手勢、
和DrawerLayout等結合應用到側滑中去。
對於類似這種效果的,一定要拿出稿紙筆去畫一畫,否則很難弄明白。