Android翻頁效果原理實現之類比扭曲

來源:互聯網
上載者:User

標籤:android翻頁效果   custom view   翻頁   

尊重原創轉載請註明:From AigeStudio(http://blog.csdn.net/aigestudio)Power by Aige 侵權必究!

炮兵鎮樓

上一節我們實現了翻頁的曲線效果,但是效果有點小瑕疵不知道大家發現沒有:


,我們發現摺疊地區怪怪的,並沒有實現我們之前的“彎曲”效果,為什麼呢?是計算錯了嗎?其實不是的,我們之前測試的時候使用的將canvas填色,但是這裡我們用到的是一張位元影像,雖然我們的Path是曲線、Region有曲線地區,但是我們的Bitmap是個規規矩矩的矩形啊,怎麼彎曲~怎麼辦呢?說起扭曲,我們首先想到的是drawBitmapMesh方法,它是我們現在瞭解的也是唯一的一個能對映像進行扭曲的API,而使用drawBitmapMesh方法呢我們也可以有多種思路,最簡單的就是最大化恒定細分值,將映像分割成一定的網格地區,然後判斷離曲線起點和頂點最近的細分線擷取該地區內的細分線交點按指定方向百分比遞減移動起點和頂點的距離值即可,這種方法簡單粗暴,但扭曲不是很精確,正確地說精確度取決於細分,細分也大越精確當然也越耗效能,而第二種方法呢是根據曲線的起點和頂點動態產生細分值,我們可以確保在起點和頂點處都有一條細分線,這樣就可以很準確地計算扭曲範圍,但是我們就需要動態地去不斷計算細分值相當麻煩,用哪種呢?這裡鑒於時間關係還是嘗試用第一種去做,首先定義寬高的細分值:

private static final int SUB_WIDTH = 19, SUB_HEIGHT = 19;// 細分值橫豎各19個網格
19個網格將控制項分割為20x20的網格細分線條地區,爾後我們就不需要使用drawBitmap繪製摺疊地區了而是改用drawBitmapMesh。之前在講1/6的時候有盆友多次小窗過我離屏緩衝是個什麼意思需要注意什麼,這裡我權當示範,在繪製扭曲映像的時候使用一個單獨的Bitmap並將其裝載進一個額外的Canvas中:

private Canvas mCanvasFoldCache;// 執行繪製離屏緩衝的Canvasprivate Bitmap mBitmapFoldCache;// 儲存繪製離屏緩衝資料的Bitmap
在構造方法中我們執行個體化mCanvasFoldCache:

/* * 執行個體化Canvas */mCanvasFoldCache = new Canvas();
在onSizeChanged中我們產生mBitmapFoldCache:

/* * 產生緩衝位元影像並注入Canvas */mBitmapFoldCache = Bitmap.createBitmap(mViewWidth + 100, mViewHeight + 100, Bitmap.Config.ARGB_8888);mCanvasFoldCache.setBitmap(mBitmapFoldCache);
這裡+100的目的是讓Bitmap有多餘的空間繪製扭曲的那部分映像,我們之前說過Canvas的大小實際取決於內部裝載的Bitmap,如果這裡我們不+100,那麼mBitmapFoldCache的大小就剛好和我們的控制項一樣大,但是我們實現扭曲的那一部分映像是超出該範圍外的:


如透明紅色的範圍是我們mBitmapFoldCache的大小,但是底部和右側的扭曲沒有被包含進來,為了彌補這部分的損失我將mBitmapFoldCache的寬高各+100,當然你也可以計算出具體的值,這裡只做示範。

而在繪製時,我們先將所有的資料繪製到mBitmapFoldCache上再將該Bitmap繪製到我們的canvas中:

mCanvasFoldCache.drawBitmapMesh(mBitmaps.get(end - 1), SUB_WIDTH, SUB_HEIGHT, mVerts, 0, null, 0, null);canvas.drawBitmap(mBitmapFoldCache, 0, 0, null);
這裡要注意的是,我們的mBitmapFoldCache在onSizeChanged方法中產生,每次我們繪製的時候都不再重建,也就是說,每次繪製其實都是疊加在上一次的繪製資料上,那麼這就會給我們帶來一個問題,雖然顯示結果有可能不會出錯但是每次繪製都要不斷計算前面的像素次數一多必定會大大影響效能,這時候我們考慮在繪製每一次結果前清空掉mBitmapFoldCache中的內容:

mCanvasFoldCache.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);mCanvasFoldCache.drawBitmapMesh(mBitmaps.get(end - 1), SUB_WIDTH, SUB_HEIGHT, mVerts, 0, null, 0, null);canvas.drawBitmap(mBitmapFoldCache, 0, 0, null);
題外話到此為止,實際上我們不需要緩衝繪製,直接使用drawBitmapMesh即可:

canvas.drawBitmapMesh(mBitmaps.get(end - 1), SUB_WIDTH, SUB_HEIGHT, mVerts, 0, null, 0, null);
而重點則是我們的這些扭曲點怎麼產生,在構造方法中我們執行個體化座標數組:

// 執行個體化數組並初始化預設數組資料mVerts = new float[(SUB_WIDTH + 1) * (SUB_HEIGHT + 1) * 2];
在計算了曲線各個點座標之後我們產生扭曲座標:

if (sizeLong > mViewHeight) {// 省略大量代碼……} else {// 省略巨量代碼……/* * 產生摺疊地區的扭曲座標 */int index = 0;for (int y = 0; y <= SUB_HEIGHT; y++) {float fy = mViewHeight * y / SUB_HEIGHT;for (int x = 0; x <= SUB_WIDTH; x++) {float fx = mViewWidth * x / SUB_WIDTH;mVerts[index * 2 + 0] = fx;mVerts[index * 2 + 1] = fy;index += 1;}}}
雖然上面我們產生了座標數組,但是並沒有扭曲映像,在進行下一步操作前我們先來分析一下如何進行扭曲呢,當我們在摺疊地區以drawBitmapMesh的方式繪製Bitmap時這時候的映像實質上是被網格分割了的:


我們的方法其實很簡單,只需要把從短邊長度減短邊長度乘以1/4的位置開始到短邊長度位置的點按遞增向下拽即可對吧:


如所示的兩個藍點分別代表短邊長度減短邊長度乘以1/4的位置和短邊長度位置,因為我們的網格是不變的,但是位置在不斷改變,我們應當擷取離當前位置最近的網格點,比如中的兩個藍點此時我們應該擷取到網格中的對應位置是:


中綠色的藍點,考慮到更好的容差值,我們令起點往後挪一個點而終點往前挪一個點,最終我們的取捨點如下:


同樣,我們右側的也一樣:


那在代碼中的實現也很簡單:

// 計算底部扭曲的起始細分下標mSubWidthStart = Math.round((btmX / mSubMinWidth)) - 1;mSubWidthEnd = Math.round(((btmX + CURVATURE * sizeShort) / mSubMinWidth)) + 1;// 計算右側扭曲的起始細分下標mSubHeightStart = (int) (leftY / mSubMinHeight) - 1;mSubHeightEnd = (int) (leftY + CURVATURE * sizeLong / mSubMinHeight) + 1;
我們只需要將mSubWidthStart到mSubWidthEnd之間的點往下“拽”,mSubHeightStart到mSubHeightEnd的點往右“拽”即可實現初步的“扭曲”效果對吧,但是這個拽是有講究的,首先,拽的距離是倍增的,


每一個點的位移值相對於上一個點來說是倍增的,倍增多少呢?是基於最大的位移值來說的,這裡為了簡化一定的問題,我就不去計算了,而是給定一個固定的起始值和倍增率:

// 長邊位移float offsetLong = CURVATURE / 2F * sizeLong;// 長邊位移倍增float mulOffsetLong = 1.0F;// 短邊位移float offsetShort = CURVATURE / 2F * sizeShort;// 短邊位移倍增float mulOffsetShort = 1.0F;
這時候我們可以考慮開始計算扭曲座標:

// 計算底部扭曲的起始細分下標mSubWidthStart = Math.round((btmX / mSubMinWidth)) - 1;mSubWidthEnd = Math.round(((btmX + CURVATURE * sizeShort) / mSubMinWidth)) + 1;// 計算右側扭曲的起始細分下標mSubHeightStart = (int) (leftY / mSubMinHeight) - 1;mSubHeightEnd = (int) (leftY + CURVATURE * sizeLong / mSubMinHeight) + 1;/* * 產生摺疊地區的扭曲座標 */int index = 0;// 長邊位移float offsetLong = CURVATURE / 2F * sizeLong;// 長邊位移倍增float mulOffsetLong = 1.0F;// 短邊位移float offsetShort = CURVATURE / 2F * sizeShort;// 短邊位移倍增float mulOffsetShort = 1.0F;for (int y = 0; y <= SUB_HEIGHT; y++) {float fy = mViewHeight * y / SUB_HEIGHT;for (int x = 0; x <= SUB_WIDTH; x++) {float fx = mViewWidth * x / SUB_WIDTH;/* * 右側扭曲 */if (x == SUB_WIDTH) {if (y >= mSubHeightStart && y <= mSubHeightEnd) {fx = mViewWidth * x / SUB_WIDTH + offsetLong * mulOffsetLong;mulOffsetLong = mulOffsetLong / 1.5F;}}/* * 底部扭曲 */if (y == SUB_HEIGHT) {if (x >= mSubWidthStart && x <= mSubWidthEnd) {fy = mViewHeight * y / SUB_HEIGHT + offsetShort * mulOffsetShort;mulOffsetShort = mulOffsetShort / 1.5F;}}mVerts[index * 2 + 0] = fx;mVerts[index * 2 + 1] = fy;index += 1;}}
效果如下:


上面的圖因為上傳大小的限制我壓縮過可能大家看不清楚,如果大家DL我想項目運行可以看到在我們翻動的過程中扭曲的部分會有一點小跳動,原因很簡單,我們的扭曲只針對了底部最後的一行點y == SUB_HEIGHT和右側最右的一列點x == SUB_WIDTH,而事實上扭曲是個拉扯聯動的效果,扭曲不僅僅會影響最後一行/列同時也會影響倒數第二、三、四行等,只不過這個影響效力是遞減的,這部分就留給大家自己去做了,原理我講的很清楚了。

這一節到此為止,下一節我們將完善最終效果結束本例所有的Study~

源碼下載:傳送門

Android翻頁效果原理實現之類比扭曲

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.