android酷炫翻页效果+图形分析
package
sf.hmg.turntest;
import
android.content.Context;
import
android.graphics.Bitmap;
import
android.graphics.Canvas;
import
android.graphics.ColorMatrix;
import
android.graphics.ColorMatrixColorFilter;
import
android.graphics.Matrix;
import
android.graphics.Paint;
import
android.graphics.Path;
import
android.graphics.PointF;
import
android.graphics.Region;
import
android.graphics.drawable.GradientDrawable;
import
android.view.MotionEvent;
import
android.view.View;
import
android.widget.Scroller;
public
class
PageWidget
extends
View {
private
static
final
String TAG
=
"
hmg
"
;
private
int
mWidth
=
480
;
private
int
mHeight
=
800
;
private
int
mCornerX
=
0
;
//
拖拽点对应的页脚
private
int
mCornerY
=
0
;
private
Path mPath0;
private
Path mPath1;
Bitmap mCurPageBitmap
=
null
;
//
当前页
Bitmap mNextPageBitmap
=
null
;
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();
float
mMiddleX;
float
mMiddleY;
float
mDegrees;
float
mTouchToCornerDis;
ColorMatrixColorFilter mColorMatrixFilter;
Matrix mMatrix;
float
[] mMatrixArray
=
{
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
1.0f
};
boolean
mIsRTandLB;
//
是否属于右上左下
float
mMaxLength
=
(
float
) Math.hypot(mWidth, mHeight);
int
[] mBackShadowColors;
int
[] mFrontShadowColors;
GradientDrawable mBackShadowDrawableLR;
GradientDrawable mBackShadowDrawableRL;
GradientDrawable mFolderShadowDrawableLR;
GradientDrawable mFolderShadowDrawableRL;
GradientDrawable mFrontShadowDrawableHBT;
GradientDrawable mFrontShadowDrawableHTB;
GradientDrawable mFrontShadowDrawableVLR;
GradientDrawable mFrontShadowDrawableVRL;
Paint mPaint;
Scroller mScroller;
public
PageWidget(Context context) {
super
(context);
//
TODO Auto-generated constructor stub
mPath0
=
new
Path();
mPath1
=
new
Path();
createDrawable();
mPaint
=
new
Paint();
mPaint.setStyle(Paint.Style.FILL);
ColorMatrix cm
=
new
ColorMatrix();
float
array[]
=
{
0.55f
,
0
,
0
,
0
,
80.0f
,
0
,
0.55f
,
0
,
0
,
80.0f
,
0
,
0
,
0.55f
,
0
,
80.0f
,
0
,
0
,
0
,
0.2f
,
0
};
cm.set(array);
mColorMatrixFilter
=
new
ColorMatrixColorFilter(cm);
mMatrix
=
new
Matrix();
mScroller
=
new
Scroller(getContext());
mTouch.x
=
0.01f
;
//
不让x,y为0,否则在点计算时会有问题
mTouch.y
=
0.01f
;
}
/**
* Author : hmg25 Version: 1.0 Description : 计算拖拽点对应的拖拽脚
*/
public
void
calcCornerXY(
float
x,
float
y) {
if
(x
<=
mWidth
/
2
)
mCornerX
=
0
;
else
mCornerX
=
mWidth;
if
(y
<=
mHeight
/
2
)
mCornerY
=
0
;
else
mCornerY
=
mHeight;
if
((mCornerX
==
0
&&
mCornerY
==
mHeight)
||
(mCornerX
==
mWidth
&&
mCornerY
==
0
))
mIsRTandLB
=
true
;
else
mIsRTandLB
=
false
;
}
public
boolean
doTouchEvent(MotionEvent event) {
//
TODO Auto-generated method stub
if
(event.getAction()
==
MotionEvent.ACTION_MOVE) {
mTouch.x
=
event.getX();
mTouch.y
=
event.getY();
this
.postInvalidate();
}
if
(event.getAction()
==
MotionEvent.ACTION_DOWN) {
mTouch.x
=
event.getX();
mTouch.y
=
event.getY();
//
calcCornerXY(mTouch.x, mTouch.y);
//
this.postInvalidate();
}
if
(event.getAction()
==
MotionEvent.ACTION_UP) {
if
(canDragOver()) {
startAnimation(
1200
);
}
else
{
mTouch.x
=
mCornerX
-
0.09f
;
mTouch.y
=
mCornerY
-
0.09f
;
}
this
.postInvalidate();
}
//
return super.onTouchEvent(event);
return
true
;
}
/**
* Author : hmg25 Version: 1.0 Description : 求解直线P1P2和直线P3P4的交点坐标
*/
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;
}
private
void
calcPoints() {
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);
//
Log.i("hmg", "mTouchX " + mTouch.x + " mTouchY " + mTouch.y);
//
Log.i("hmg", "mBezierControl1.x " + mBezierControl1.x
//
+ " mBezierControl1.y " + mBezierControl1.y);
//
Log.i("hmg", "mBezierControl2.x " + mBezierControl2.x
//
+ " mBezierControl2.y " + mBezierControl2.y);
mBezierStart1.x
=
mBezierControl1.x
-
(mCornerX
-
mBezierControl1.x)
/
2
;
mBezierStart1.y
=
mCornerY;
//
当mBezierStart1.x < 0或者mBezierStart1.x > 480时
//
如果继续翻页,会出现BUG故在此限制
if
(mTouch.x
>
0
&&
mTouch.x
<
mWidth) {
if
(mBezierStart1.x
<
0
||
mBezierStart1.x
>
mWidth) {
if
(mBezierStart1.x
<
0
)
mBezierStart1.x
=
mWidth
-
mBezierStart1.x;
float
f1
=
Math.abs(mCornerX
-
mTouch.x);
float
f2
=
mWidth
*
f1
/
mBezierStart1.x;
mTouch.x
=
Math.abs(mCornerX
-
f2);
float
f3
=
Math.abs(mCornerX
-
mTouch.x)
*
Math.abs(mCornerY
-
mTouch.y)
/
f1;
mTouch.y
=
Math.abs(mCornerY
-
f3);
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);
//
Log.i("hmg", "mTouchX --> " + mTouch.x + " mTouchY--> "
//
+ mTouch.y);
//
Log.i("hmg", "mBezierControl1.x-- " + mBezierControl1.x
//
+ " mBezierControl1.y -- " + mBezierControl1.y);
//
Log.i("hmg", "mBezierControl2.x -- " + mBezierControl2.x
//
+ " mBezierControl2.y -- " + mBezierControl2.y);
mBezierStart1.x
=
mBezierControl1.x
-
(mCornerX
-
mBezierControl1.x)
/
2
;
}
}
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);
//
Log.i("hmg", "mBezierEnd1.x " + mBezierEnd1.x + " mBezierEnd1.y "
//
+ mBezierEnd1.y);
//
Log.i("hmg", "mBezierEnd2.x " + mBezierEnd2.x + " mBezierEnd2.y "
//
+ mBezierEnd2.y);
/*
* 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
;
}
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();
}
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));
int
leftx;
int
rightx;
GradientDrawable mBackShadowDrawable;
if
(mIsRTandLB) {
leftx
=
(
int
) (mBezierStart1.x);
rightx
=
(
int
) (mBezierStart1.x
+
mTouchToCornerDis
/
4
);
mBackShadowDrawable
=
mBackShadowDrawableLR;
}
else
{
leftx
=
(
int
) (mBezierStart1.x
-
mTouchToCornerDis
/
4
);
rightx
=
(
int
) mBezierStart1.x;
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();
}
public
void
setBitmaps(Bitmap bm1, Bitmap bm2) {
mCurPageBitmap
=
bm1;
mNextPageBitmap
=
bm2;
}
public
void
setScreen(
int
w,
int
h) {
mWidth
=
w;
mHeight
=
h;
}
@Override
protected
void
onDraw(Canvas canvas) {
canvas.drawColor(
0xFFAAAAAA
);
calcPoints();
drawCurrentPageArea(canvas, mCurPageBitmap, mPath0);
drawNextPageAreaAndShadow(canvas, mNextPageBitmap);
drawCurrentPageShadow(canvas);
drawCurrentBackArea(canvas, mCurPageBitmap);
}
/**
* Author : hmg25 Version: 1.0 Description : 创建阴影的GradientDrawable
*/
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
[] {
0x80111111
,
0x111111
};
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);
}
/**
* Author : hmg25 Version: 1.0 Description : 绘制翻起页的阴影
*/
public
void
drawCurrentPageShadow(Canvas canvas) {
double
degree;
if
(mIsRTandLB) {
degree
=
Math.PI
/
4
-
Math.atan2(mBezierControl1.y
-
mTouch.y, mTouch.x
-
mBezierControl1.x);
}
else
{
degree
=
Math.PI
/
4
-
Math.atan2(mTouch.y
-
mBezierControl1.y, mTouch.x
-
mBezierControl1.x);
}
//
翻起页阴影顶点与touch点的距离
double
d1
=
(
float
)
25
*
1.414
*
Math.cos(degree);
double
d2
=
(
float
)
25
*
1.414
*
Math.sin(degree);
float
x
=
(
float
) (mTouch.x
+
d1);
float
y;
if
(mIsRTandLB) {
y
=
(
float
) (mTouch.y
+
d2);
}
else
{
y
=
(
float
) (mTouch.y
-
d2);
}
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
+
1
;
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
-
mMaxLength), 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
+
1
);
mCurrentPageShadow
=
mFrontShadowDrawableHBT;
}
rotateDegrees
=
(
float
) Math.toDegrees(Math.atan2(mBezierControl2.y
-
mTouch.y, mBezierControl2.x
-
mTouch.x));
canvas.rotate(rotateDegrees, mBezierControl2.x, mBezierControl2.y);
float
temp;
if
(mBezierControl2.y
<
0
)
temp
=
mBezierControl2.y
-
mHeight;
else
temp
=
mBezierControl2.y;
int
hmg
=
(
int
) Math.hypot(mBezierControl2.x, temp);
if
(hmg
>
mMaxLength)
mCurrentPageShadow
.setBounds((
int
) (mBezierControl2.x
-
25
)
-
hmg, leftx,
(
int
) (mBezierControl2.x
+
mMaxLength)
-
hmg,
rightx);
else
mCurrentPageShadow.setBounds(
(
int
) (mBezierControl2.x
-
mMaxLength), leftx,
(
int
) (mBezierControl2.x), rightx);
//
Log.i("hmg", "mBezierControl2.x " + mBezierControl2.x
//
+ " mBezierControl2.y " + mBezierControl2.y);
mCurrentPageShadow.draw(canvas);
canvas.restore();
}
/**
* Author : hmg25 Version: 1.0 Description : 绘制翻起页背面
*/
private
void
drawCurrentBackArea(Canvas canvas, Bitmap bitmap) {
int
i
=
(
int
) (mBezierStart1.x
+
mBezierControl1.x)
/
2
;
float
f1
=
Math.abs(i
-
mBezierControl1.x);
int
i1
=
(
int
) (mBezierStart2.y
+
mBezierControl2.y)
/
2
;
float
f2
=
Math.abs(i1
-
mBezierControl2.y);
float
f3
=
Math.min(f1, f2);
mPath1.reset();
mPath1.moveTo(mBeziervertex2.x, mBeziervertex2.y);
mPath1.lineTo(mBeziervertex1.x, mBeziervertex1.y);
mPath1.lineTo(mBezierEnd1.x, mBezierEnd1.y);
mPath1.lineTo(mTouch.x, mTouch.y);
mPath1.lineTo(mBezierEnd2.x, mBezierEnd2.y);
mPath1.close();
GradientDrawable mFolderShadowDrawable;
int
left;
int
right;
if
(mIsRTandLB) {
left
=
(
int
) (mBezierStart1.x
-
1
);
right
=
(
int
) (mBezierStart1.x
+
f3
+
1
);
mFolderShadowDrawable
=
mFolderShadowDrawableLR;
}
else
{
left
=
(
int
) (mBezierStart1.x
-
f3
-
1
);
right
=
(
int
) (mBezierStart1.x
+
1
);
mFolderShadowDrawable
=
mFolderShadowDrawableRL;
}
canvas.save();
canvas.clipPath(mPath0);
canvas.clipPath(mPath1, Region.Op.INTERSECT);
mPaint.setColorFilter(mColorMatrixFilter);
float
dis
=
(
float
) Math.hypot(mCornerX
-
mBezierControl1.x,
mBezierControl2.y
-
mCornerY);
float
f8
=
(mCornerX
-
mBezierControl1.x)
/
dis;
float
f9
=
(mBezierControl2.y
-
mCornerY)
/
dis;
mMatrixArray[
0
]
=
1
-
2
*
f9
*
f9;
mMatrixArray[
1
]
=
2
*
f8
*
f9;
mMatrixArray[
3
]
=
mMatrixArray[
1
];
mMatrixArray[
4
]
=
1
-
2
*
f8
*
f8;
mMatrix.reset();
mMatrix.setValues(mMatrixArray);
mMatrix.preTranslate(
-
mBezierControl1.x,
-
mBezierControl1.y);
mMatrix.postTranslate(mBezierControl1.x, mBezierControl1.y);
canvas.drawBitmap(bitmap, mMatrix, mPaint);
//
canvas.drawBitmap(bitmap, mMatrix, null);
mPaint.setColorFilter(
null
);
canvas.rotate(mDegrees, mBezierStart1.x, mBezierStart1.y);
mFolderShadowDrawable.setBounds(left, (
int
) mBezierStart1.y, right,
(
int
) (mBezierStart1.y
+
mMaxLength));
mFolderShadowDrawable.draw(canvas);
canvas.restore();
}
public
void
computeScroll() {
super
.computeScroll();
if
(mScroller.computeScrollOffset()) {
float
x
=
mScroller.getCurrX();
float
y
=
mScroller.getCurrY();
mTouch.x
=
x;
mTouch.y
=
y;
postInvalidate();
}
}
private
void
startAnimation(
int
delayMillis) {
int
dx, dy;
//
dx 水平方向滑动的距离,负值会使滚动向左滚动
//
dy 垂直方向滑动的距离,负值会使滚动向上滚动
if
(mCornerX
>
0
) {
dx
=
-
(
int
) (mWidth
+
mTouch.x);
}
else
{
dx
=
(
int
) (mWidth
-
mTouch.x
+
mWidth);
}
if
(mCornerY
>
0
) {
dy
=
(
int
) (mHeight
-
mTouch.y);
}
else
{
dy
=
(
int
) (
1
-
mTouch.y);
//
防止mTouch.y最终变为0
}
mScroller.startScroll((
int
) mTouch.x, (
int
) mTouch.y, dx, dy,
delayMillis);
}
public
void
abortAnimation() {
if
(
!
mScroller.isFinished()) {
mScroller.abortAnimation();
}
}
public
boolean
canDragOver() {
if
(mTouchToCornerDis
>
mWidth
/
10
)
return
true
;
return
false
;
}
/**
* Author : hmg25 Version: 1.0 Description : 是否从左边翻向右边
*/
public
boolean
DragToRight() {
if
(mCornerX
>
0
)
return
false
;
return
true
;
}
}
/**
* Author : hmg25
* Description :
*/
package
sf.hmg.turntest;
import
java.io.File;
import
java.io.IOException;
import
java.io.RandomAccessFile;
import
java.io.UnsupportedEncodingException;
import
java.nio.MappedByteBuffer;
import
java.nio.channels.FileChannel;
import
java.text.DecimalFormat;
import
java.util.Vector;
import
android.graphics.Bitmap;
import
android.graphics.Canvas;
import
android.graphics.Color;
import
android.graphics.Paint;
import
android.graphics.Paint.Align;
public
class
BookPageFactory {
private
File book_file
=
null
;
private
MappedByteBuffer m_mbBuf
=
null
;
private
int
m_mbBufLen
=
0
;
private
int
m_mbBufBegin
=
0
;
private
int
m_mbBufEnd
=
0
;
private
String m_strCharsetName
=
"
GBK
"
;
private
Bitmap m_book_bg
=
null
;
private
int
mWidth;
private
int
mHeight;
private
Vector
<
String
>
m_lines
=
new
Vector
<
String
>
();
private
int
m_fontSize
=
24
;
private
int
m_textColor
=
Color.BLACK;
private
int
m_backColor
=
0xffff9e85
;
//
背景颜色
private
int
marginWidth
=
15
;
//
左右与边缘的距离
private
int
marginHeight
=
20
;
//
上下与边缘的距离
private
int
mLineCount;
//
每页可以显示的行数
private
float
mVisibleHeight;
//
绘制内容的宽
private
float
mVisibleWidth;
//
绘制内容的宽
private
boolean
m_isfirstPage,m_islastPage;
//
private int m_nLineSpaceing = 5;
private
Paint mPaint;
public
BookPageFactory(
int
w,
int
h) {
//
TODO Auto-generated constructor stub
mWidth
=
w;
mHeight
=
h;
mPaint
=
new
Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setTextAlign(Align.LEFT);
mPaint.setTextSize(m_fontSize);
mPaint.setColor(m_textColor);
mVisibleWidth
=
mWidth
-
marginWidth
*
2
;
mVisibleHeight
=
mHeight
-
marginHeight
*
2
;
mLineCount
=
(
int
) (mVisibleHeight
/
m_fontSize);
//
可显示的行数
}
public
void
openbook(String strFilePath)
throws
IOException {
book_file
=
new
File(strFilePath);
long
lLen
=
book_file.length();
m_mbBufLen
=
(
int
) lLen;
m_mbBuf
=
new
RandomAccessFile(book_file,
"
r
"
).getChannel().map(
FileChannel.MapMode.READ_ONLY,
0
, lLen);
}
protected
byte
[] readParagraphBack(
int
nFromPos) {
int
nEnd
=
nFromPos;
int
i;
byte
b0, b1;
if
(m_strCharsetName.equals(
"
UTF-16LE
"
)) {
i
=
nEnd
-
2
;
while
(i
>
0
) {
b0
=
m_mbBuf.get(i);
b1
=
m_mbBuf.get(i
+
1
);
if
(b0
==
0x0a
&&
b1
==
0x00
&&
i
!=
nEnd
-
2
) {
i
+=
2
;
break
;
}
i
--
;
}
}
else
if
(m_strCharsetName.equals(
"
UTF-16BE
"
)) {
i
=
nEnd
-
2
;
while
(i
>
0
) {
b0
=
m_mbBuf.get(i);
b1
=
m_mbBuf.get(i
+
1
);
if
(b0
==
0x00
&&
b1
==
0x0a
&&
i
!=
nEnd
-
2
) {
i
+=
2
;
break
;
}
i
--
;
}
}
else
{
i
=
nEnd
-
1
;
while
(i
>
0
) {
b0
=
m_mbBuf.get(i);
if
(b0
==
0x0a
&&
i
!=
nEnd
-
1
) {
i
++
;
break
;
}
i
--
;
}
}
if
(i
<
0
)
i
=
0
;
int
nParaSize
=
nEnd
-
i;
int
j;
byte
[] buf
=
new
byte
[nParaSize];
for
(j
=
0
; j
<
nParaSize; j
++
) {
buf[j]
=
m_mbBuf.get(i
+
j);
}
return
buf;
}
//
读取上一段落
protected
byte
[] readParagraphForward(
int
nFromPos) {
int
nStart
=
nFromPos;
int
i
=
nStart;
byte
b0, b1;
//
根据编码格式判断换行
if
(m_strCharsetName.equals(
"
UTF-16LE
"
)) {
while
(i
<
m_mbBufLen
-
1
) {
b0
=
m_mbBuf.get(i
++
);
b1
=
m_mbBuf.get(i
++
);
if
(b0
==
0x0a
&&
b1
==
0x00
) {
break
;
}
}
}
else
if
(m_strCharsetName.equals(
"
UTF-16BE
"
)) {
while
(i
<
m_mbBufLen
-
1
) {
b0
=
m_mbBuf.get(i
++
);
b1
=
m_mbBuf.get(i
++
);
if
(b0
==
0x00
&&
b1
==
0x0a
) {
break
;
}
}
}
else
{
while
(i
<
m_mbBufLen) {
b0
=
m_mbBuf.get(i
++
);
if
(b0
==
0x0a
) {
break
;
}
}
}
int
nParaSize
=
i
-
nStart;
byte
[] buf
=
new
byte
[nParaSize];
for
(i
=
0
; i
<
nParaSize; i
++
) {
buf[i]
=
m_mbBuf.get(nFromPos
+
i);
}
return
buf;
}
protected
Vector
<
String
>
pageDown() {
String strParagraph
=
""
;
Vector
<
String
>
lines
=
new
Vector
<
String
>
();
while
(lines.size()
<
mLineCount
&&
m_mbBufEnd
<
m_mbBufLen) {
byte
[] paraBuf
=
readParagraphForward(m_mbBufEnd);
//
读取一个段落
m_mbBufEnd
+=
paraBuf.length;
try
{
strParagraph
=
new
String(paraBuf, m_strCharsetName);
}
catch
(UnsupportedEncodingException e) {
//
TODO Auto-generated catch block
e.printStackTrace();
}
String strReturn
=
""
;
if
(strParagraph.indexOf(
"
\r\n
"
)
!=
-
1
) {
strReturn
=
"
\r\n
"
;
strParagraph
=
strParagraph.replaceAll(
"
\r\n
"
,
""
);
}
else
if
(strParagraph.indexOf(
"
\n
"
)
!=
-
1
) {
strReturn
=
"
\n
"
;
strParagraph
=
strParagraph.replaceAll(
"
\n
"
,
""
);
}
if
(strParagraph.length()
==
0
) {
lines.add(strParagraph);
}
while
(strParagraph.length()
>
0
) {
int
nSize
=
mPaint.breakText(strParagraph,
true
, mVisibleWidth,
null
);
lines.add(strParagraph.substring(
0
, nSize));
strParagraph
=
strParagraph.substring(nSize);
if
(lines.size()
>=
mLineCount) {
break
;
}
}
if
(strParagraph.length()
!=
0
) {
try
{
m_mbBufEnd
-=
(strParagraph
+
strReturn)
.getBytes(m_strCharsetName).length;
}
catch
(UnsupportedEncodingException e) {
//
TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return
lines;
}
protected
void
pageUp() {
if
(m_mbBufBegin
<
0
)
m_mbBufBegin
=
0
;
Vector
<
String
>
lines
=
new
Vector
<
String
>
();
String strParagraph
=
""
;
while
(lines.size()
<
mLineCount
&&
m_mbBufBegin
>
0
) {
Vector
<
String
>
paraLines
=
new
Vector
<
String
>
();
byte
[] paraBuf
=
readParagraphBack(m_mbBufBegin);
m_mbBufBegin
-=
paraBuf.length;
try
{
strParagraph
=
new
String(paraBuf, m_strCharsetName);
}
catch
(UnsupportedEncodingException e) {
//
TODO Auto-generated catch block
e.printStackTrace();
}
strParagraph
=
strParagraph.replaceAll(
"
\r\n
"
,
""
);
strParagraph
=
strParagraph.replaceAll(
"
\n
"
,
""
);
if
(strParagraph.length()
==
0
) {
paraLines.add(strParagraph);
}
while
(strParagraph.length()
>
0
) {
int
nSize
=
mPaint.breakText(strParagraph,
true
, mVisibleWidth,
null
);
paraLines.add(strParagraph.substring(
0
, nSize));
strParagraph
=
strParagraph.substring(nSize);
}
lines.addAll(
0
, paraLines);
}
while
(lines.size()
>
mLineCount) {
try
{
m_mbBufBegin
+=
lines.get(
0
).getBytes(m_strCharsetName).length;
lines.remove(
0
);
}
catch
(UnsupportedEncodingException e) {
//
TODO Auto-generated catch block
e.printStackTrace();
}
}
m_mbBufEnd
=
m_mbBufBegin;
return
;
}
protected
void
prePage()
throws
IOException {
if
(m_mbBufBegin
<=
0
) {
m_mbBufBegin
=
0
;
m_isfirstPage
=
true
;
return
;
}
else
m_isfirstPage
=
false
;
m_lines.clear();
pageUp();
m_lines
=
pageDown();
}
public
void
nextPage()
throws
IOException {
if
(m_mbBufEnd
>=
m_mbBufLen) {
m_islastPage
=
true
;
return
;
}
else
m_islastPage
=
false
;
m_lines.clear();
m_mbBufBegin
=
m_mbBufEnd;
m_lines
=
pageDown();
}
public
void
onDraw(Canvas c) {
if
(m_lines.size()
==
0
)
m_lines
=
pageDown();
if
(m_lines.size()
>
0
) {
if
(m_book_bg
==
null
)
c.drawColor(m_backColor);
else
c.drawBitmap(m_book_bg,
0
,
0
,
null
);
int
y
=
marginHeight;
for
(String strLine : m_lines) {
y
+=
m_fontSize;
c.drawText(strLine, marginWidth, y, mPaint);
}
}
float
fPercent
=
(
float
) (m_mbBufBegin
*
1.0
/
m_mbBufLen);
DecimalFormat df
=
new
DecimalFormat(
"
#0.0
"
);
String strPercent
=
df.format(fPercent
*
100
)
+
"
%
"
;
int
nPercentWidth
=
(
int
) mPaint.measureText(
"
999.9%
"
)
+
1
;
c.drawText(strPercent, mWidth
-
nPercentWidth, mHeight
-
5
, mPaint);
}
public
void
setBgBitmap(Bitmap BG) {
m_book_bg
=
BG;
}
public
boolean
isfirstPage() {
return
m_isfirstPage;
}
public
boolean
islastPage() {
return
m_islastPage;
}
}
package
sf.hmg.turntest;
import
java.io.IOException;
import
android.app.Activity;
import
android.graphics.Bitmap;
import
android.graphics.BitmapFactory;
import
android.graphics.Canvas;
import
android.graphics.Paint;
import
android.os.Bundle;
import
android.view.MotionEvent;
import
android.view.View;
import
android.view.View.OnTouchListener;
import
android.view.Window;
import
android.view.WindowManager;
import
android.widget.Toast;
public
class
turntest
extends
Activity {
/**
Called when the activity is first created.
*/
private
PageWidget mPageWidget;
Bitmap mCurPageBitmap, mNextPageBitmap;
Canvas mCurPageCanvas, mNextPageCanvas;
BookPageFactory pagefactory;
@Override
public
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
mPageWidget
=
new
PageWidget(
this
);
setContentView(mPageWidget);
mCurPageBitmap
=
Bitmap.createBitmap(
480
,
800
, Bitmap.Config.ARGB_8888);
mNextPageBitmap
=
Bitmap
.createBitmap(
480
,
800
, Bitmap.Config.ARGB_8888);
mCurPageCanvas
=
new
Canvas(mCurPageBitmap);
mNextPageCanvas
=
new
Canvas(mNextPageBitmap);
pagefactory
=
new
BookPageFactory(
480
,
800
);
pagefactory.setBgBitmap(BitmapFactory.decodeResource(
this
.getResources(), R.drawable.bg));
try
{
pagefactory.openbook(
"
/sdcard/test.txt
"
);
pagefactory.onDraw(mCurPageCanvas);
}
catch
(IOException e1) {
//
TODO Auto-generated catch block
e1.printStackTrace();
Toast.makeText(
this
,
"
电子书不存在,请将《test.txt》放在SD卡根目录下
"
,
Toast.LENGTH_SHORT).show();
}
mPageWidget.setBitmaps(mCurPageBitmap, mCurPageBitmap);
mPageWidget.setOnTouchListener(
new
OnTouchListener() {
@Override
public
boolean
onTouch(View v, MotionEvent e) {
//
TODO Auto-generated method stub
boolean
ret
=
false
;
if
(v
==
mPageWidget) {
if
(e.getAction()
==
MotionEvent.ACTION_DOWN) {
mPageWidget.abortAnimation();
mPageWidget.calcCornerXY(e.getX(), e.getY());
pagefactory.onDraw(mCurPageCanvas);
if
(mPageWidget.DragToRight()) {
try
{
pagefactory.prePage();
}
catch
(IOException e1) {
//
TODO Auto-generated catch block
e1.printStackTrace();
}
if
(pagefactory.isfirstPage())
return
false
;
pagefactory.onDraw(mNextPageCanvas);
}
else
{
try
{
pagefactory.nextPage();
}
catch
(IOException e1) {
//
TODO Auto-generated catch block
e1.printStackTrace();
}
if
(pagefactory.islastPage())
return
false
;
pagefactory.onDraw(mNextPageCanvas);
}
mPageWidget.setBitmaps(mCurPageBitmap, mNextPageBitmap);
}
ret
=
mPageWidget.doTouchEvent(e);
return
ret;
}
return
false
;
}
});
}
}