1. 程式人生 > >2014-11-6Android學習------Android 模擬翻頁效果實現--------貝塞爾曲線(二)

2014-11-6Android學習------Android 模擬翻頁效果實現--------貝塞爾曲線(二)

寫一篇文章很辛苦啊!!!

轉載請註明,聯絡請郵件[email protected]

我學習Android都是結合原始碼去學習,這樣比較直觀,非常清楚的看清效果,覺得很好,今天的學習原始碼是網上找的原始碼 百度搜就知道很多下載的地方  網上原始碼的名字叫:Android模擬翻頁效果.zip我的部落格寫的比較亂,如果本篇文章沒有看懂,

要想實現一種翻頁的效果,就是當我的手指點在手機介面上的時候能夠像翻書那樣有一種很直觀的效果,要想實現這樣的效果,

首先必須要知道一個知識點,就是:漸變式的背景    GradientDrawable

關於這個類有一些常量:

GradientDrawable.Orientation BL_TR                從繪製漸變左下到右上
GradientDrawable.Orientation BOTTOM_TOP   繪製漸變,從底部到頂部
GradientDrawable.Orientation BR_TL                從右下角到左上角的繪製漸變
GradientDrawable.Orientation LEFT_RIGHT      繪製漸變從左側到右側
GradientDrawable.Orientation RIGHT_LEFT      從向左右側繪製漸變
GradientDrawable.Orientation TL_BR               繪製漸變,從左上角向右下角
GradientDrawable.Orientation TOP_BOTTOM   從頂部至底部繪製漸變
GradientDrawable.Orientation TR_BL               從右上角到左下角的繪製漸變

我們首先看看效果,然後你就有明顯的感受了:


通過這幅圖片,大家應該有很直觀的印象了,知道什麼是漸變式背景了把

接下來我們就是如何去實現這樣的效果

1.首先我們要知道,我們能夠從很多地方翻頁,從左上角開始,右上角開始,左下角,右下角,

我們翻到什麼位置,就是對應的這個四個角,

2.我們必須要知道,當我們翻頁的時候,其實是出現了三種概念上的陰影效果:前面的,後面的,以及被夾在中間的

用上面的圖我們可以看到三種主調色:黃,綠,灰  他們分別對應著前,後,中間的色彩,

另外這個陰影色是漸變的,它有中立體感

3.知道了這三種狀態下的陰影,但是方向還沒有確定,所以這裡就是一種組合形式的漸變式背景了,例如,前面陰影從左下到右上

如此一來,我們就必須先定義這些變量了:

GradientDrawable mBackShadowDrawableLR;//後面的陰影效果,從左邊到右邊
GradientDrawable mBackShadowDrawableRL;//後面的陰影效果,從右邊到左邊
GradientDrawable mFolderShadowDrawableLR;//夾在中間的陰影效果,從左邊到右邊
GradientDrawable mFolderShadowDrawableRL;//夾在中間的陰影效果,從右邊到左邊

GradientDrawable mFrontShadowDrawableHBT;//前面的陰影效果,右下角到左上角
GradientDrawable mFrontShadowDrawableHTB;//前面的陰影效果,從左上到右下
GradientDrawable mFrontShadowDrawableVLR;//前面的陰影效果,從左到右
GradientDrawable mFrontShadowDrawableVRL;//前面的陰影效果,從右到左

當我們把這些變數定義出來了,我們就需要去初始化它,

public GradientDrawable (GradientDrawable.Orientation orientation, int[] colors)
Create a new gradient drawable given an orientation and an array of colors for the gradient.

引數:常量orientation代表漸變的效果模式,colors陣列,代表漸變的顏色,從什麼顏色到什麼顏色

這樣一來,我們還需要先定義一個顏色的陣列,型別為int

int[] mBackShadowColors;//後面的陰影顏色
int[] mFrontShadowColors;//前面的陰影顏色

接下來事先確定他們的顏色:黃 綠 灰

0x80888888, 0x888888 
0xFF111111, 0x111111
0x333333, 0xB0333333

接下來就是去寫構造這些漸變式背景的函數了:

private void createDrawable() {

//夾在中間的
int[] color = { 0x333333, 0xB0333333 };
mFolderShadowDrawableRL = new GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, color);
mFolderShadowDrawableRL.setGradientType(GradientDrawable.LINEAR_GRADIENT);
mFolderShadowDrawableLR = new GradientDrawable(
GradientDrawable.Orientation.LEFT_RIGHT, color);
mFolderShadowDrawableLR.setGradientType(GradientDrawable.LINEAR_GRADIENT);

//後面的
mBackShadowColors = new int[] { 0xFF111111, 0x111111 };
mBackShadowDrawableRL = new GradientDrawable(
GradientDrawable.Orientation.RIGHT_LEFT, mBackShadowColors);
mBackShadowDrawableRL.setGradientType(GradientDrawable.LINEAR_GRADIENT);
mBackShadowDrawableLR = new GradientDrawable(
GradientDrawable.Orientation.LEFT_RIGHT, mBackShadowColors);
mBackShadowDrawableLR.setGradientType(GradientDrawable.LINEAR_GRADIENT);
//前面的
mFrontShadowColors = new int[] { 0x80888888, 0x888888 };
mFrontShadowDrawableVLR = new GradientDrawable(
GradientDrawable.Orientation.LEFT_RIGHT, mFrontShadowColors);
mFrontShadowDrawableVLR.setGradientType(GradientDrawable.LINEAR_GRADIENT);
mFrontShadowDrawableVRL = new GradientDrawable(
GradientDrawable.Orientation.RIGHT_LEFT, mFrontShadowColors);
mFrontShadowDrawableVRL.setGradientType(GradientDrawable.LINEAR_GRADIENT);
mFrontShadowDrawableHTB = new GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM, mFrontShadowColors);
mFrontShadowDrawableHTB.setGradientType(GradientDrawable.LINEAR_GRADIENT);
mFrontShadowDrawableHBT = new GradientDrawable(
GradientDrawable.Orientation.BOTTOM_TOP, mFrontShadowColors);
mFrontShadowDrawableHBT.setGradientType(GradientDrawable.LINEAR_GRADIENT);
}

好了,上面的程式碼就處理完我們需要預定的背景了

二.接下來首先需要考慮的是,如何把這些背景畫到畫布上去呢。前面的知識點都說了,要想把這些背景畫到畫布上去,需要一個

點陣圖物件,Bitmap,這裡比較特殊,我們需要畫三個點陣圖,分別對應著前,後,中間,

1.那麼我們需要去定義這三個點陣圖變數

Bitmap mCurPageBitmap = null; // 前面,當前頁,黃色
Bitmap mCurPageBackBitmap = null;//夾在中間的的,灰色
Bitmap mNextPageBitmap = null;//後面的 綠色

設定函式:作用不大,基本用不上

public void setBitmaps(Bitmap bm1, Bitmap bm2, Bitmap bm3) {
mCurPageBitmap = bm1;
mCurPageBackBitmap = bm2;
mNextPageBitmap = bm3;
}

2.接下來還需要畫筆 畫布 路徑 等相關變數

private Bitmap mBitmap;//開啟介面時的檢視,上面的三個點陣圖都是在這個初始的點陣圖上繪製出來
private Canvas mCanvas;//畫布
private Paint mBitmapPaint;//畫點陣圖的畫筆
Paint paint;//手指拖到翻頁就是畫一條曲線

private Path mPath0;//路徑0  對應當前頁  灰色的路徑
private Path mPath1;//路徑1  對應前面的頁 黃色的路徑

3.接下來需要定義的是座標,手指觸控事件的觸發是通過座標的改變來畫出這個曲線的

private int mCornerX = 0; // 拖拽點對應的頁尾 這個變量表示翻頁的起始點對應的四個角的座標,介面上四個角
private int mCornerY = 0;

float mMiddleX;//代表介面寬度的一半,他們是用來判斷頁尾座標的
float mMiddleY;//代表高度的一半,

        float mDegrees;//角度,主要是顯示的時候那種立體的角度,也就是頁尾座標和你手指拖動的過程中會產生角度,很重要

4.定義貝塞爾曲線需要的座標點:

PointF mTouch = new PointF(); // 拖拽點,
PointF mBezierStart1 = new PointF(); // 貝塞爾曲線起始點。第一條貝塞爾曲線對應的是黃色 前面的
PointF mBezierControl1 = new PointF(); // 貝塞爾曲線控制點
PointF mBeziervertex1 = new PointF(); // 貝塞爾曲線頂點
PointF mBezierEnd1 = new PointF(); // 貝塞爾曲線結束點


PointF mBezierStart2 = new PointF(); // 另一條貝塞爾曲線  對應的是綠色 後面的
PointF mBezierControl2 = new PointF();
PointF mBeziervertex2 = new PointF();
PointF mBezierEnd2 = new PointF();

5.預設創造的點陣圖的高寬,也就是你模擬器的型別吧
private int mWidth = 480;
private int mHeight = 800;

6.要計算出手指觸到的座標到頁尾座標的直線距離,需要用到一個變數

float mTouchToCornerDis;

這個變數的初始化為:

mTouchToCornerDis = (float) Math.hypot((mTouch.x - mCornerX),(mTouch.y - mCornerY));

這個數學函式的意思是:java.lang.Math.hypot(double x, double y) 返回sqrt(x2 +y2沒有中間的上溢或下溢

這個變數對畫貝塞爾曲線有參考作用:

7.還需要一個判斷是不是特定方向的變數

boolean mIsRTandLB; // 是否屬於右上左下

三.變數定義完之後我們就需要初始化

1.由於我們當前的類是繼承View類的,有兩件事要幹,第一:建構函式需要重寫,第二:過載onDraw()函式

建構函式:

public PageWidget(Context context) {
super(context);
// TODO Auto-generated constructor stub
mPath0 = new Path();//灰色。夾在中間的貝塞爾曲線路徑的初始化
mPath1 = new Path();//黃色,前面的貝塞爾曲線路徑的初始化
createDrawable();//漸變式點陣圖的初始化
// ---------------------------------------
mBitmap = Bitmap.createBitmap(480, 800, Bitmap.Config.ARGB_8888);//創造檢視的背景
mCanvas = new Canvas(mBitmap);//在這個背景上載入畫布
mBitmapPaint = new Paint(Paint.DITHER_FLAG);//在畫布上繪製背景的畫筆


mCurPageBitmap = Bitmap.createBitmap(480, 800, Bitmap.Config.ARGB_8888);//黃色的點陣圖
Canvas canvas = new Canvas(mCurPageBitmap);//在點陣圖上載入畫布
paint = new Paint();//這個點陣圖上需要一個畫筆
canvas.drawColor(Color.YELLOW);//設定顏色為黃色
canvas.drawBitmap(mCurPageBitmap, 0, 0, paint);//把當前的點陣圖畫上去


mNextPageBitmap = Bitmap.createBitmap(480, 800, Bitmap.Config.ARGB_8888);//綠色的點陣圖
canvas = new Canvas(mNextPageBitmap);//在這個點陣圖上載入畫布
canvas.drawColor(Color.GREEN);//設定顏色為綠色
canvas.drawBitmap(mNextPageBitmap, 0, 0, paint);//把這個點陣圖畫出來
}

onDraw():

@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(0xFFAAAAAA);//設定畫布的顏色
calcPoints();//計算座標點,也就是給兩個貝塞爾路徑曲線的各個座標進行初始化已經跟蹤,
drawCurrentPageArea(mCanvas, mCurPageBitmap, mPath0);//在黃色點陣圖上畫出對應貝塞爾曲線
drawNextPageAreaAndShadow(mCanvas, mNextPageBitmap);//在綠色點陣圖上畫對陰影效果
drawCurrentPageShadow(mCanvas);//黃色點陣圖上也有陰影效果
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);//在預設設定的點陣圖上畫上設計的點陣圖
}

2.如何去確定座標呢?首先是要找到這些座標,然後再去計算貝塞爾曲線的各個座標點

1)找到座標,是需要做觸控監聽事件處理的

@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
if (event.getAction() == MotionEvent.ACTION_MOVE) {//按下並拖動
mCanvas.drawColor(0xFFAAAAAA);//設定顏色
mTouch.x = event.getX();//拖動的x座標
mTouch.y = event.getY();//拖動的y座標
this.postInvalidate();//更新檢視
}
if (event.getAction() == MotionEvent.ACTION_DOWN) {按下
mCanvas.drawColor(0xFFAAAAAA);
mTouch.x = event.getX();
mTouch.y = event.getY();
calcCornerXY(mTouch.x, mTouch.y);//如果按下了,我們需要去計算它對應的頁尾座標
this.postInvalidate();
}
if (event.getAction() == MotionEvent.ACTION_UP) {//鬆開
mCanvas.drawColor(0xFFAAAAAA);
mTouch.x = mCornerX;//鬆開之後x座標回到頁尾的x座標
mTouch.y = mCornerY;//鬆開之後y座標回到頁尾的y座標

// 其實這裡還可以做其他的處理,鬆開之後實現成功翻頁等等,
this.postInvalidate();
}
// return super.onTouchEvent(event);
return true;
}

2)當我們的觸控座標有了,接下來就是根據這個座標去計算畫出貝塞爾曲線所需要到的座標

1)).計算頁尾座標

private void calcCornerXY(float x, float y) {
if (x <= mWidth / 2)//如果手指觸控點的x座標小於寬度的一半,頁尾的x座標就是0,也就是左上角
mCornerX = 0;
else//右上角
mCornerX = mWidth;//當觸控點的x座標超過螢幕寬度的一半的時候,頁尾的x座標就是寬度的值
if (y <= mHeight / 2)//如果觸控點的高度小於螢幕高度的一半,頁尾的y座標就是0,左上角
mCornerY = 0;
else//右下角
mCornerY = mHeight;//如果觸控點的高度大於螢幕的高度的一半,頁尾的y座標就是高度,
if ((mCornerX == 0 && mCornerY == mHeight)//代表是左下角
|| (mCornerX == mWidth && mCornerY == 0))//代表右上角
mIsRTandLB = true;//這個標誌設定true
else
mIsRTandLB = false;//否則標誌設定false
}

2)).計算貝塞爾曲線需要的各個點的座標:

private void calcPoints() {

//座標的中間點的值,觸控點的x,y座標與頁尾座標的和的一半
mMiddleX = (mTouch.x + mCornerX) / 2;
mMiddleY = (mTouch.y + mCornerY) / 2;

//第一條貝塞爾曲線的控制點座標,也就是黃色的那個點陣圖,這個計算值請看數學方法
mBezierControl1.x = mMiddleX - (mCornerY - mMiddleY)
* (mCornerY - mMiddleY) / (mCornerX - mMiddleX);
mBezierControl1.y = mCornerY;

//第二條貝塞爾曲線的控制點座標,也就是綠色的那個點陣圖,這個計算值請看數學方法
mBezierControl2.x = mCornerX;
mBezierControl2.y = mMiddleY - (mCornerX - mMiddleX)
* (mCornerX - mMiddleX) / (mCornerY - mMiddleY);

//第一條貝塞爾曲線的起始點座標,也就是黃色的那個點陣圖,這個計算值請看數學方法
mBezierStart1.x = mBezierControl1.x - (mCornerX - mBezierControl1.x)/ 2;
mBezierStart1.y = mCornerY;
//第二條貝塞爾曲線的起始點座標,也就是綠色的那個點陣圖,這個計算值請看數學方法
mBezierStart2.x = mCornerX;
mBezierStart2.y = mBezierControl2.y - (mCornerY - mBezierControl2.y)/ 2;
//當前座標與頁尾的直線距離
mTouchToCornerDis = (float) Math.hypot((mTouch.x - mCornerX),(mTouch.y - mCornerY));
//第一條貝塞爾曲線的結束點座標,也就是黃色的那個點陣圖,這個計算值請看數學方法
mBezierEnd1 =
getCross(mTouch, mBezierControl1, mBezierStart1,mBezierStart2);//兩直線交點
mBezierEnd2 =
getCross(mTouch, mBezierControl2, mBezierStart1,mBezierStart2);
//貝塞爾曲線頂點座標的初始化,這個計算值請看數學方法
/*
* mBeziervertex1.x 推導
* ((mBezierStart1.x+mBezierEnd1.x)/2+mBezierControl1.x)/2 化簡等價於
* (mBezierStart1.x+ 2*mBezierControl1.x+mBezierEnd1.x) / 4
*/
mBeziervertex1.x = (mBezierStart1.x + 2 * mBezierControl1.x + mBezierEnd1.x) / 4;
mBeziervertex1.y = (2 * mBezierControl1.y + mBezierStart1.y + mBezierEnd1.y) / 4;
mBeziervertex2.x = (mBezierStart2.x + 2 * mBezierControl2.x + mBezierEnd2.x) / 4;
mBeziervertex2.y = (2 * mBezierControl2.y + mBezierStart2.y + mBezierEnd2.y) / 4;

}

3)).兩條直線的交點計算:

public PointF getCross(PointF P1, PointF P2, PointF P3, PointF P4) {
PointF CrossP = new PointF();
// 二元函式通式: y=ax+b
float a1 = (P2.y - P1.y) / (P2.x - P1.x);
float b1 = ((P1.x * P2.y) - (P2.x * P1.y)) / (P1.x - P2.x);


float a2 = (P4.y - P3.y) / (P4.x - P3.x);
float b2 = ((P3.x * P4.y) - (P4.x * P3.y)) / (P3.x - P4.x);
CrossP.x = (b2 - b1) / (a1 - a2);
CrossP.y = a1 * CrossP.x + b1;
return CrossP;
}

3)座標都做完了之後,我們接下來就是繪製貝塞爾曲線:

1)).當前的點陣圖,也就是黃色的

private void drawCurrentPageArea(Canvas canvas, Bitmap bitmap, Path path) {
mPath0.reset();//開始畫之前需要把路徑清空
mPath0.moveTo(mBezierStart1.x, mBezierStart1.y);//移動到起始點
mPath0.quadTo(mBezierControl1.x, mBezierControl1.y, mBezierEnd1.x,mBezierEnd1.y);//貝塞爾
mPath0.lineTo(mTouch.x, mTouch.y);//手指在移動,貝塞爾曲線也跟著移,路徑也就移動
mPath0.lineTo(mBezierEnd2.x, mBezierEnd2.y);//移動到第二條貝塞爾曲線的終點,立體感,綠色
mPath0.quadTo(mBezierControl2.x, mBezierControl2.y, mBezierStart2.x,mBezierStart2.y);
mPath0.lineTo(mCornerX, mCornerY);//畫到頁尾那個位置去
mPath0.close();//把這個路徑封閉起來,得到一個平面圖形,下面取補集切割,得到黃色的部分


canvas.save();//畫布儲存,儲存畫布的狀態
canvas.
clipPath(path, Region.Op.XOR);//切割畫布,補集
canvas.drawBitmap(bitmap, 0, 0, null);//畫出點陣圖
canvas.restore();//取出畫布的狀態,一般跟save()同時出現,匹配響應
}

畫布屬性解釋:

1.canvas.clipRect(30, 30, 70, 70, Region.Op.XOR);最後一個引數有多個選擇分別是:

            //DIFFERENCE是第一次不同於第二次的部分顯示出來
            //REPLACE是顯示第二次的
            //REVERSE_DIFFERENCE 是第二次不同於第一次的部分顯示
            //INTERSECT交集顯示
            //UNION全部顯示
            //XOR補集 就是全集的減去交集剩餘部分顯示

2.

canvas.save();和canvas.restore();是兩個相互匹配出現的,作用是用來儲存畫布的狀態和取出儲存的狀態的。這裡稍微解釋一下,

  當我們對畫布進行旋轉,縮放,平移等操作的時候其實我們是想對特定的元素進行操作,比如圖片,一個矩形等,但是當你用canvas的方法來進行這些操作的時候,其實是對整個畫布進行了操作,那麼之後在畫布上的元素都會受到影響,所以我們在操作之前呼叫canvas.save()來儲存畫布當前的狀態,當操作之後取出之前儲存過的狀態,這樣就不會對其他的元素進行影響

2))畫出綠色部分的貝塞爾曲線以及陰影的效果

private void drawNextPageAreaAndShadow(Canvas canvas, Bitmap bitmap) {
mPath1.reset();//路徑在開始畫之前需要清空,
mPath1.moveTo(mBezierStart1.x, mBezierStart1.y);//路徑移動到開始點
mPath1.lineTo(mBeziervertex1.x, mBeziervertex1.y);//移動到第一條曲線的頂點
mPath1.lineTo(mBeziervertex2.x, mBeziervertex2.y);//移動到第二條曲線的頂點
mPath1.lineTo(mBezierStart2.x, mBezierStart2.y);//移動到第二條曲線的開始點
mPath1.lineTo(mCornerX, mCornerY);//移動到頁尾
mPath1.close();//將這個圖形封閉起來,得到一個平面,下面的角度處理就形成了立體

//角度,與控制點的座標有關係,具體的請看數學方法
mDegrees = (float) Math.toDegrees(Math.atan2(mBezierControl1.x
- mCornerX, mBezierControl2.y - mCornerY));
float f5 = mTouchToCornerDis / 4;//這個值在後面被直接使用了右邊的表示式
int leftx;
int rightx;
GradientDrawable mBackShadowDrawable;//漸變式背景灰色的陰影效果
if (mIsRTandLB) {//左下右上的方式
leftx = (int) (mBezierStart1.x - 1);
rightx = (int) (mBezierStart1.x + mTouchToCornerDis / 4 + 1);
mBackShadowDrawable = mBackShadowDrawableLR;//漸變式效果是從左到右
} else {
leftx = (int) (mBezierStart1.x - mTouchToCornerDis / 4 - 1);
rightx = (int) mBezierStart1.x + 1;
mBackShadowDrawable = mBackShadowDrawableRL;//漸變式效果是從右向左
}

canvas.save();//儲存畫布狀態,
canvas.clipPath(mPath0);//切割畫布
canvas.clipPath(mPath1, Region.Op.INTERSECT);//切割畫布,交集,交集區域就是灰色
canvas.drawBitmap(bitmap, 0, 0, null);//繪製點陣圖
canvas.rotate(mDegrees, mBezierStart1.x, mBezierStart1.y);//翻轉效果
mBackShadowDrawable.setBounds(leftx, (int) mBezierStart1.y, rightx,
(int) (mMaxLength + mBezierStart1.y));//設定灰色部分的邊界
mBackShadowDrawable.draw(canvas);//在畫布上畫出這個陰影的漸變式效果
canvas.restore();//取出畫布的狀態
}

度的計算:數學方法:

java.lang.Math.toDegrees(double angrad)轉換以弧度為單位測得的角度大致相等的角度,以度衡量。

上面的函式是把弧度轉換成度,

java.lang.Math.atan2(double y,double x)返回正切值 tan(θ) = y / x

返回值為笛卡爾平面中的角度,該角度由 x 軸和起點為原點 (0,0)、終點為 (x,y) 的向量構成。

3)).畫出當前頁,就是黃色部分的陰影效果

public void drawCurrentPageShadow(Canvas canvas) {

// 注:拖拽頂點的陰影,定位有問題,待修改~~
double d1 = Math.atan2(mBezierControl1.y - mTouch.y, mTouch.x- mBezierControl1.x);
double d3 = (float) 25 / Math.cos(d1);
float x = (float) (mTouch.x - d3);
float y = (float) (mTouch.y - d3);

// 前面陰影的第一邊,請仔細看圖,無論從哪個方向翻頁,都是在前面產生兩個陰影效果
mPath1.reset();//路徑在開始畫之前需要清空
mPath1.moveTo(x, y);//移動到開始點
mPath1.lineTo(mTouch.x, mTouch.y);移動到手指觸控的點
mPath1.lineTo(mBezierControl1.x, mBezierControl1.y);//移動到第一條貝塞爾曲線的控制點
mPath1.lineTo(mBezierStart1.x, mBezierStart1.y);//移動到第一條貝塞爾曲線的開始點
mPath1.close();//把曲線封閉起來,形成一個平面
float rotateDegrees;//翻轉的角度
canvas.save();//儲存畫布的狀態
canvas.clipPath(mPath0, Region.Op.XOR);//切割畫布,補集
canvas.clipPath(mPath1, Region.Op.INTERSECT);//切割畫布,交集
int leftx;
int rightx;
GradientDrawable mCurrentPageShadow;//陰影部分的漸變式效果
if (mIsRTandLB) {//如果方向是左下右上
leftx = (int) (mBezierControl1.x);
rightx = (int) mBezierControl1.x + 25;
mCurrentPageShadow = mFrontShadowDrawableVLR;//漸變式效果從左向右
} else {//否則
leftx = (int) (mBezierControl1.x - 25);
rightx = (int) mBezierControl1.x;
mCurrentPageShadow = mFrontShadowDrawableVRL;//漸變式效果從右向左
}
rotateDegrees = (float) Math.toDegrees(Math.atan2(mTouch.x
- mBezierControl1.x, mBezierControl1.y - mTouch.y));
canvas.rotate(rotateDegrees, mBezierControl1.x, mBezierControl1.y);//翻轉效果


mCurrentPageShadow.setBounds(leftx, (int) (mBezierControl1.y - 500),
rightx, (int) (mBezierControl1.y));//設定邊界
mCurrentPageShadow.draw(canvas);//在畫布上畫出來
canvas.restore();//取出畫布的狀態

//前面陰影的第二邊
mPath1.reset();//路徑清空,因為上面的畫布狀態已經取出來了,
mPath1.moveTo(x, y);//路徑移動到起始點
mPath1.lineTo(mTouch.x, mTouch.y);//移動到手指觸控點
mPath1.lineTo(mBezierControl2.x, mBezierControl2.y);//移動到第二條貝塞爾曲線的控制點
mPath1.lineTo(mBezierStart2.x, mBezierStart2.y);//移動到第二條貝塞爾曲線的開始點
mPath1.close();//把路徑封閉起來,形成一個平面
canvas.save();//儲存畫布的狀態
canvas.clipPath(mPath0, Region.Op.XOR);//切割畫布,補集
canvas.clipPath(mPath1, Region.Op.INTERSECT);//切割畫布,交集


if (mIsRTandLB) {//如果是左下右上
leftx = (int) (mBezierControl2.y);
rightx = (int) (mBezierControl2.y + 25);
mCurrentPageShadow = mFrontShadowDrawableHTB;//設定漸變式效果左下到右上
} else {//否則
leftx = (int) (mBezierControl2.y - 25);
rightx = (int) (mBezierControl2.y);
mCurrentPageShadow = mFrontShadowDrawableHBT;//漸變式效果右上到左下
}

//旋轉的角度
rotateDegrees = (float) Math.toDegrees(Math.atan2(mBezierControl2.y
- mTouch.y, mBezierControl2.x - mTouch.x));
canvas.rotate(rotateDegrees, mBezierControl2.x, mBezierControl2.y);
mCurrentPageShadow.setBounds((int) (mBezierControl2.x - 500), leftx,
(int) (mBezierControl2.x), rightx);//設定邊界
mCurrentPageShadow.draw(canvas);//把這個陰影繪製出來
canvas.restore();//取出畫布的狀態
}

到這裡,整個程式碼就分析完畢了,實現的效果就如文章開頭給出的效果