先上个效果图:
效果还是很不错的,不过与ibook那个效果比起来,还是有差距的。应为这个没用到openGL做3D效果,只是用的2d的canvas画布去画的view,添加了阴影效果,还是挺有立体感的。而且比较流畅。openGL实现肯定效果会更好,不过就我目前的技术实力,实现希望还是渺茫的。
废话少说,还是上代码吧:
这里需要两个UI的view类和一个使用方法的demo。
第一个pageTurnerView.java:
001.
public
class
PageTurnerViewP1 extends RelativeLayout{
002.
003.
private
static
final
int
CORNER_RIGHT_MASK = 1;
004.
private
static
final
int
CORNER_TOP_MASK = 2;
005.
public
static
final
int
CORNER_BOTTOM_LEFT = 0;
006.
public
static
final
int
CORNER_BOTTOM_RIGHT = 1;
007.
public
static
final
int
CORNER_TOP_LEFT = 2;
008.
public
static
final
int
CORNER_TOP_RIGHT = 3;
009.
private
static
final
int
INVALIDATE = 1;
010.
private
static
final
int
INITIAL_TIME_DELAY = 100;
011.
private
static
final
int
TIME_DELAY = 10;
012.
// private static final int TIME_STEPS = 30;
013.
private
boolean mPageTurning;
014.
private
boolean mStepping;
015.
public
long
mNextTime;
016.
private
int
mTimeStep;
017.
private
int
mDrawnTimeStep;
018.
private
int
mCorner;
019.
private
Drawable mBackPage;
020.
private
Drawable mPageBackground;
021.
private
Path mForegroundPath;
022.
private
Path mBackPagePath;
023.
private
Path mBackgroundPath;
024.
private
float
mRotation;
025.
private
Rect mChildRect =
new
Rect();
026.
private
int
mOuterOffsetX;
027.
private
int
mOuterOffsetY;
028.
public
float
mPivotX;
029.
private
int
mPageId;
030.
private
PageViewP1 mPage;
031.
private
PointF mPageTurnCorner =
new
PointF();
032.
private
PointF mOppositeCorner =
new
PointF();
033.
private
PointF mPageDim =
new
PointF();
034.
private
int
mStepLen = 1;
035.
public
static
final
int
KEEP = 0;
036.
public
static
final
int
NEXT = 1;
037.
public
static
final
int
LAST = 2;
038.
public
int
mWhere = KEEP;
039.
public
boolean isBgInit =
true
;
040.
public
boolean isBackInit =
true
;
041.
private
float
ax,ay,bx,by,cx,cy,dx,dy,ex,ey,c0x,c0y;
042.
private
int
mMaxStep=30;
043.
public
final Handler mHandler =
new
Handler() {
044.
public
void
handleMessage(Message msg) {
045.
if
(msg.what != 1) {
return
; }
046.
// PageTurnerViewP1.this.invalidate();
047.
refreshUI();
048.
// PageTurnerViewP1.this.invalidate((int)bx, (int)ay, (int)dx, (int)dy);
049.
if
(PageTurnerViewP1.
this
.mStepping) {
return
; }
050.
msg = obtainMessage(1);
051.
long
current = SystemClock.uptimeMillis();
052.
if
(PageTurnerViewP1.
this
.mNextTime < current) {
053.
//PageTurnerViewP1.access$102(PageTurnerViewP1.this, current + 10L);
054.
PageTurnerViewP1.
this
.mNextTime= current + 5L;
055.
}
056.
sendMessageAtTime(msg, PageTurnerViewP1.
this
.mNextTime);
057.
//PageTurnerViewP1.access$114(PageTurnerViewP1.this, 10L);
058.
PageTurnerViewP1.
this
.mNextTime+= 5L;
059.
}
060.
};
061.
062.
063.
public
PageTurnerViewP1(Context context) {
064.
super(context);
065.
Log.i(
"==================== PageTurnerViewP1(Context context) ================="
,
""
+
this
);
066.
}
067.
068.
public
PageTurnerViewP1(Context context, AttributeSet attrs) {
069.
super(context, attrs);
070.
071.
// TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PageTurner);
072.
073.
// this.mBackPage = a.getDrawable(1);
074.
// this.mPageBackground = a.getDrawable(0);
075.
//
076.
// this.mPageId = a.getResourceId(3, -1);
077.
//
078.
// this.mCorner = a.getInt(2, 0);
079.
// this.mBackPage = this.getResources().getDrawable(com.tom.clip.R.drawable.a5);
080.
// this.mPageBackground =this.getResources().getDrawable(com.tom.clip.R.drawable.a6);
081.
082.
this
.mPageId = -1;
083.
this
.mCorner = -1;
084.
}
085.
086.
protected
void
onFinishInflate() {
087.
super.onFinishInflate();
088.
if
(
this
.mPageId != -1) {
089.
this
.mPage = ((PageViewP1)findViewById(
this
.mPageId));
090.
if
(
this
.mPage != null)
091.
this
.mPage.setPageTurner(
this
);
092.
}
093.
}
094.
095.
public
void
setPageId(
int
pageId) {
096.
this
.mPageId = pageId;
097.
this
.mPage = ((PageViewP1)findViewById(
this
.mPageId));
098.
if
(
this
.mPage != null)
099.
this
.mPage.setPageTurner(
this
);
100.
}
101.
102.
public
int
getPageId() {
103.
return
this
.mPageId;
104.
}
105.
106.
public
void
setPage(PageViewP1 pageViewP1) {
107.
this
.mPage = pageViewP1;
108.
}
109.
110.
public
PageViewP1 getPage() {
111.
return
this
.mPage;
112.
}
113.
114.
public
void
setCorner(
int
corner) {
115.
this
.mCorner = corner;
116.
}
117.
118.
public
int
getCorner() {
119.
return
this
.mCorner;
120.
}
121.
122.
protected
void
dispatchDraw(Canvas canvas) {
123.
124.
Log.v(
"log dispatchDraw:"
,
"drawing back page"
+mPageTurning);
125.
// if ((this.mPageTurning) && (this.mPage != null) && (computePageTurn())) {
126.
if
((computePageTurn())&& (
this
.mPageTurning) && (
this
.mPage != null) ) {
127.
this
.mPage.setClipPath(
this
.mForegroundPath);
128.
}
129.
130.
super.dispatchDraw(canvas);
131.
132.
if
(
this
.mPageTurning) {
133.
drawBackground(canvas);
134.
drawBackPage(canvas);
135.
136.
if
(!updateTimeStep()) {
137.
this
.mHandler.removeMessages(1);
138.
if
(
this
.mPage != null) {
139.
this
.mPage.onPageTurnFinished(canvas);
140.
}
141.
this
.mPageTurning =
false
;
142.
this
.mStepping =
false
;
143.
invalidate();
144.
}
145.
}
146.
}
147.
148.
public
void
startPageTurn(
int
mTimeStep) {
149.
if
((
this
.mPage == null) && (
this
.mPageId != -1)) {
150.
this
.mPage = ((PageViewP1)findViewById(
this
.mPageId));
151.
}
152.
if
(
this
.mPage == null) {
153.
return
;
154.
}
155.
this
.mPage.setPageTurner(
this
);
156.
Drawable d =
this
.mPage.getPageBackground();
157.
if
(d != null) {
158.
this
.mPageBackground = d;
159.
}
160.
161.
d =
this
.mPage.getBackPage();
162.
if
(d != null) {
163.
this
.mBackPage = d;
164.
}
165.
166.
int
corner =
this
.mPage.getCorner();
167.
if
(corner != -1) {
168.
this
.mCorner = corner;
169.
}
170.
// this.mStepping=false;
171.
this
.mPageTurning =
true
;
172.
this
.mTimeStep = mTimeStep;
173.
this
.mDrawnTimeStep = -1;
174.
Message msg =
this
.mHandler.obtainMessage(1);
175.
this
.mNextTime = (SystemClock.uptimeMillis() + 5L);
176.
this
.mHandler.sendMessageAtTime(msg,
this
.mNextTime);
177.
}
178.
179.
public
void
stepPageTurn() {
180.
if
(!
this
.mStepping) {
181.
this
.mStepping =
true
;
182.
startPageTurn(
this
.mTimeStep);
183.
}
else
{
184.
// te((int)bx, 0, (int)mPageTurnCorner.x, (int)mPageTurnCorner.y);
185.
// sendUIhandler();
186.
refreshUI();
187.
}
188.
}
189.
public
void
refreshUI(){
190.
computePageTurn();
191.
//
192.
this
.invalidate();
193.
}
194.
private
void
sendUIhandler(){
195.
Message msg =
this
.mHandler.obtainMessage(1);
196.
this
.mNextTime = (SystemClock.uptimeMillis() + 30L);
197.
this
.mHandler.sendMessageAtTime(msg,
this
.mNextTime);
198.
}
199.
private
boolean updateTimeStep() {
200.
if
(
this
.mTimeStep >mMaxStep ||
this
.mTimeStep<0) {
201.
return
false
;
202.
}
203.
this
.mTimeStep +=mStepLen;
204.
return
true
;
205.
}
206.
207.
private
boolean computePageTurn() {
208.
// if ((this.mDrawnTimeStep == this.mTimeStep) ||(this.mTimeStep <0)||(this.mTimeStep >this.mMaxStep)) {
209.
if
( (
this
.mTimeStep <0)||(
this
.mTimeStep >
this
.mMaxStep)) {
210.
return
false
;
211.
}
212.
if
(
this
.mPage == null) {
213.
return
false
;
214.
}
215.
this
.mDrawnTimeStep =
this
.mTimeStep;
216.
View child =
this
.mPage.getChildAt(0);
217.
child.getDrawingRect(
this
.mChildRect);
218.
// this.mOuterOffsetX = (child.getLeft() - getLeft());
219.
// this.mOuterOffsetY = (child.getTop() - getTop());
220.
this
.mOuterOffsetX = 0;
221.
this
.mOuterOffsetY = 0;
222.
223.
float
width =
this
.mChildRect.right;
224.
float
height =
this
.mChildRect.bottom;
225.
if
(!mStepping){
226.
this
.mPivotX = (
this
.mTimeStep / 30.0f * width);
227.
}
228.
this
.mForegroundPath =
new
Path();
229.
this
.mBackPagePath =
new
Path();
230.
this
.mBackgroundPath =
new
Path();
231.
float
slope = width / (
this
.mPivotX - width);
232.
float
y =
this
.mPivotX * slope;
233.
234.
this
.mPageTurnCorner.x = 0.0F;
235.
this
.mPageTurnCorner.y = height;
236.
this
.mOppositeCorner.x = width;
237.
this
.mOppositeCorner.y = 0.0F;
238.
float
x0 =
this
.mPivotX;
239.
float
cornerIntersect = height * width / (height + width);
240.
if
((
this
.mCorner & 0x1) != 0) {
241.
this
.mPageTurnCorner.x = width;
242.
this
.mOppositeCorner.x = 0.0F;
243.
x0 = width - x0;
244.
}
245.
246.
if
((
this
.mCorner & 0x2) != 0) {
247.
this
.mPageTurnCorner.y = 0.0F;
248.
this
.mOppositeCorner.y = height;
249.
}
250.
251.
this
.mPageDim.x = width;
252.
this
.mPageDim.y = height;
253.
float
page_slope;
254.
255.
if
(
this
.mPivotX <= cornerIntersect) {
256.
page_slope = firstHalfPageTurn(
this
.mPageDim,
this
.mPageTurnCorner,
this
.mOppositeCorner, x0, slope, y);
257.
}
else
{
258.
page_slope = secondHalfPageTurn(
this
.mPageDim,
this
.mPageTurnCorner,
this
.mOppositeCorner, x0, slope, y);
259.
}
260.
261.
this
.mRotation = (
float
)Math.
atan
(page_slope);
262.
263.
this
.mRotation = (
float
)(-
this
.mRotation * 180.0D / 3.141592653589793D);
264.
265.
return
true
;
266.
}
267.
268.
private
float
firstHalfPageTurn(PointF pageDim, PointF pageTurnCorner, PointF oppositeCorner,
float
xPivot,
float
slope,
float
y) {
269.
float
width = pageDim.x;
270.
float
height = pageDim.y;
271.
View child =
this
.mPage.getChildAt(0);
272.
int
innerOffsetX = child.getLeft() -
this
.mPage.getLeft();
273.
int
innerOffsetY = child.getTop() -
this
.mPage.getTop();
274.
275.
float
y1 = height + y;
276.
277.
float
x2 = (
float
)(2.0D * y / (slope + 1.0D / slope));
278.
float
y2 = height + x2 / slope;
279.
float
page_slope = (height - y2) / (x2 -
this
.mPivotX);
280.
if
((
this
.mCorner & 0x1) != 0) {
281.
x2 = width - x2;
282.
page_slope = -page_slope;
283.
}
284.
if
((
this
.mCorner & 0x2) != 0) {
285.
y1 = height - y1;
286.
y2 = height - y2;
287.
page_slope = -page_slope;
288.
}
289.
290.
float
x0 = xPivot;
291.
float
x1 = pageTurnCorner.x;
292.
293.
294.
//a点是右上角,b点位左下角,c点为右下角,d点为固定的右下角
295.
ax=
this
.mOuterOffsetX + x1;
296.
ay=
this
.mOuterOffsetY + y1;
297.
298.
bx=
this
.mOuterOffsetX + x2;
299.
by=
this
.mOuterOffsetY + y2;
300.
301.
cx=
this
.mOuterOffsetX + x0;
302.
cy=
this
.mOuterOffsetY + pageTurnCorner.y;
303.
304.
dx=
this
.mOuterOffsetX + pageTurnCorner.x;
305.
dy=
this
.mOuterOffsetY + pageTurnCorner.y;
306.
307.
ex=(dx+bx)/2;
308.
ey=(dy+by)/2;
309.
310.
this
.mForegroundPath.moveTo(innerOffsetX + x1, innerOffsetY + y1);
311.
this
.mForegroundPath.lineTo(innerOffsetX + pageTurnCorner.x, innerOffsetY + oppositeCorner.y);
312.
this
.mForegroundPath.lineTo(innerOffsetX + oppositeCorner.x, innerOffsetY + oppositeCorner.y);
313.
this
.mForegroundPath.lineTo(innerOffsetX + oppositeCorner.x, innerOffsetY + pageTurnCorner.y);
314.
this
.mForegroundPath.lineTo(innerOffsetX + x0, innerOffsetY + pageTurnCorner.y);
315.
this
.mForegroundPath.lineTo(innerOffsetX + x2, innerOffsetY + y2);
316.
// this.mForegroundPath.quadTo(ex, ey, bx, by);
317.
this
.mForegroundPath.lineTo(innerOffsetX + x1, innerOffsetY + y1);
318.
319.
this
.mBackPagePath.moveTo(
this
.mOuterOffsetX + x1,
this
.mOuterOffsetY + y1);
320.
this
.mBackPagePath.lineTo(
this
.mOuterOffsetX + x2,
this
.mOuterOffsetY + y2);
321.
//b到c的那条线,可以考虑画个弧线
322.
this
.mBackPagePath.lineTo(
this
.mOuterOffsetX + x0,
this
.mOuterOffsetY + pageTurnCorner.y);
323.
// this.mBackPagePath.arcTo(new RectF(bx,by,cx,cy), 220, 180,false);
324.
// this.mBackPagePath.quadTo(ex, ey, cx, cy);
325.
// this.mBackPagePath.cubicTo(100, -50, 200, 50, cx, cy);//贝赛尔曲线
326.
this
.mBackPagePath.lineTo(
this
.mOuterOffsetX + x1,
this
.mOuterOffsetY + y1);
327.
328.
329.
this
.mBackgroundPath.moveTo(
this
.mOuterOffsetX + x1,
this
.mOuterOffsetY + y1);
330.
this
.mBackgroundPath.lineTo(
this
.mOuterOffsetX + x0,
this
.mOuterOffsetY + pageTurnCorner.y);
331.
this
.mBackgroundPath.lineTo(
this
.mOuterOffsetX + pageTurnCorner.x,
this
.mOuterOffsetY + pageTurnCorner.y);
332.
this
.mBackgroundPath.lineTo(
this
.mOuterOffsetX + x1,
this
.mOuterOffsetY + y1);
333.
334.
return
page_slope;
335.
}
336.
337.
338.
private
float
secondHalfPageTurn(PointF pageDim, PointF pageTurnCorner, PointF oppositeCorner,
float
xPivot,
float
slope,
float
y) {
339.
float
width = pageDim.x;
340.
float
height = pageDim.y;
341.
View child =
this
.mPage.getChildAt(0);
342.
int
xOffset = child.getLeft() -
this
.mPage.getLeft();
343.
int
yOffset = child.getTop() -
this
.mPage.getTop();
344.
345.
float
y1 = 0.0F;
346.
float
x1 = width - (height + width) * (width -
this
.mPivotX) / width;
347.
348.
float
x3 = (
float
)(2.0D * y / (slope + 1.0D / slope));
349.
float
y3 = height + x3 / slope;
350.
float
page_slope = (height - y3) / (x3 -
this
.mPivotX);
351.
float
b = height - x1 * page_slope;
352.
float
x2 = (
float
)((-y - b) / (page_slope + 1.0D / page_slope));
353.
float
y2 = (x1 - x2) * page_slope;
354.
355.
if
((
this
.mCorner & 0x1) != 0) {
356.
x1 = width - x1;
357.
x2 = width - x2;
358.
x3 = width - x3;
359.
page_slope = -page_slope;
360.
}
361.
if
((
this
.mCorner & 0x2) != 0) {
362.
y1 = height - y1;
363.
y2 = height - y2;
364.
y3 = height - y3;
365.
page_slope = -page_slope;
366.
}
367.
368.
float
x0 = xPivot;
369.
370.
this
.mForegroundPath.moveTo(xOffset + x1, yOffset + y1);
371.
this
.mForegroundPath.lineTo(xOffset + oppositeCorner.x, yOffset + oppositeCorner.y);
372.
this
.mForegroundPath.lineTo(xOffset + oppositeCorner.x, yOffset + pageTurnCorner.y);
373.
this
.mForegroundPath.lineTo(xOffset + x0, yOffset + pageTurnCorner.y);
374.
this
.mForegroundPath.lineTo(xOffset + x1, yOffset + y1);
375.
376.
//a点是右上角,c0点位左上角,b点为左下角,d点为固定的右下角
377.
ax=
this
.mOuterOffsetX + x1;
378.
ay=
this
.mOuterOffsetY + y1;
379.
380.
c0x=
this
.mOuterOffsetX + x2;
381.
c0y=
this
.mOuterOffsetY + y2;
382.
383.
bx=
this
.mOuterOffsetX + x3;
384.
by=
this
.mOuterOffsetY + y3;
385.
cx=
this
.mOuterOffsetX + x0;
386.
cy=
this
.mOuterOffsetY + pageTurnCorner.y;
387.
388.
dx=
this
.mOuterOffsetX + pageTurnCorner.x;
389.
dy=
this
.mOuterOffsetY + pageTurnCorner.y;
390.
391.
ex=(dx+bx)/2;
392.
ey=(dy+by)/2;
393.
394.
this
.mBackPagePath.moveTo(
this
.mOuterOffsetX + x1,
this
.mOuterOffsetY + y1);
395.
this
.mBackPagePath.lineTo(
this
.mOuterOffsetX + x2,
this
.mOuterOffsetY + y2);
396.
this
.mBackPagePath.lineTo(
this
.mOuterOffsetX + x3,
this
.mOuterOffsetY + y3);
397.
this
.mBackPagePath.lineTo(
this
.mOuterOffsetX + x0,
this
.mOuterOffsetY + pageTurnCorner.y);
398.
// this.mBackPagePath.quadTo(ex, ey, cx, cy);
399.
this
.mBackPagePath.lineTo(
this
.mOuterOffsetX + x1,
this
.mOuterOffsetY + y1);
400.
401.
402.
403.
this
.mBackgroundPath.moveTo(
this
.mOuterOffsetX + x1,
this
.mOuterOffsetY + y1);
404.
this
.mBackgroundPath.lineTo(
this
.mOuterOffsetX + x0,
this
.mOuterOffsetY + pageTurnCorner.y);
405.
this
.mBackgroundPath.lineTo(
this
.mOuterOffsetX + pageTurnCorner.x,
this
.mOuterOffsetY + pageTurnCorner.y);
406.
this
.mBackgroundPath.lineTo(
this
.mOuterOffsetX + pageTurnCorner.x,
this
.mOuterOffsetY + oppositeCorner.y);
407.
this
.mBackgroundPath.lineTo(
this
.mOuterOffsetX + x1,
this
.mOuterOffsetY + y1);
408.
409.
return
page_slope;
410.
}
411.
412.
private
void
drawBackground(Canvas canvas) {
413.
canvas.save();
414.
// canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG));
415.
canvas.clipPath(
this
.mBackgroundPath, Region.Op.INTERSECT);
416.
canvas.translate(
this
.mOuterOffsetX,
this
.mOuterOffsetY);
417.
418.
if
(
this
.mPageBackground != null) {
419.
this
.mPageBackground.setBounds(0, 0,
this
.mChildRect.right,
this
.mChildRect.bottom);
420.
421.
this
.mPageBackground.draw(canvas);
422.
}
423.
if
(
this
.mPage != null) {
424.
this
.mPage.drawBackground(canvas);
425.
}
426.
427.
// add drop shadow start test
428.
if
(mTimeStep<=29&&ex>2){
429.
LinearGradient grad =
new
LinearGradient(
430.
ex,ey,ex+(dx-ex)/4,ey+(dy-ey)/4,R.color.gray3,Color.TRANSPARENT,Shader.TileMode.CLAMP);
431.
Paint p=
new
Paint();
432.
p.setShader(grad);
433.
// p.setAlpha(120);
434.
canvas.drawPath(
this
.mBackgroundPath,p);
435.
//end test
436.
}
437.
canvas.restore();
438.
439.
440.
}
441.
442.
private
void
drawBackPage(Canvas canvas) {
443.
float
width =
this
.mChildRect.right;
444.
float
height =
this
.mChildRect.bottom;
445.
canvas.save();
446.
// canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG));
447.
canvas.clipPath(
this
.mBackPagePath, Region.Op.INTERSECT);
448.
float
xShift = 2.0F *
this
.mPivotX - width;
449.
float
xRotate = width -
this
.mPivotX;
450.
float
yRotate = height;
451.
if
((
this
.mCorner & 0x1) != 0) {
452.
xShift = width - 2.0F *
this
.mPivotX;
453.
xRotate =
this
.mPivotX;
454.
}
455.
if
((
this
.mCorner & 0x2) != 0) {
456.
yRotate = 0.0F;
457.
}
458.
canvas.translate(
this
.mOuterOffsetX + xShift,
this
.mOuterOffsetY);
459.
canvas.rotate(
this
.mRotation, xRotate, yRotate);
460.
461.
//画原本的背面
462.
if
(
this
.mBackPage != null) {
463.
this
.mBackPage.setBounds(0, 0,
this
.mChildRect.right,
this
.mChildRect.bottom);
464.
this
.mBackPage.draw(canvas);
465.
}
466.
//画回调函数中的画背面
467.
if
(
this
.mPage != null) {
468.
Log.v(
"log2 drawBackPage2:"
,
"drawing back page"
);
469.
this
.mPage.drawBackPage(canvas);
470.
}
471.
472.
canvas.restore();
473.
canvas.save();
474.