• 周四. 4 月 23rd, 2026

物嫩软件资讯网

软件资讯来物嫩

阅读图书翻页

admin@wunen

5 月 12, 2025



1.自定义View



package sf.hmg.turntest;

import android.content.Context;

import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.graphics.Canvas;

import android.graphics.Color;

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.util.Log;

import android.view.MotionEvent;

import android.view.View;

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 mCurPageBackBitmap = 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; // 是否属于右上左下

// for test

float mMaxLength = (float) Math.hypot(480, 800);

int[] mBackShadowColors;

int[] mFrontShadowColors;

GradientDrawable mBackShadowDrawableLR;

GradientDrawable mBackShadowDrawableRL;

GradientDrawable mFolderShadowDrawableLR;

GradientDrawable mFolderShadowDrawableRL;

GradientDrawable mFrontShadowDrawableHBT;

GradientDrawable mFrontShadowDrawableHTB;

GradientDrawable mFrontShadowDrawableVLR;

GradientDrawable mFrontShadowDrawableVRL;

private Bitmap mBitmap;

private Canvas mCanvas;

private Paint mBitmapPaint;

Paint paint;

Paint mPaint;

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);

paint = new Paint();

mPaint = new Paint();

mPaint.setStyle(Paint.Style.FILL);

mCurPageBitmap = BitmapFactory.decodeResource(this.getResources(),

R.drawable.page1);

mNextPageBitmap = BitmapFactory.decodeResource(this.getResources(),

R.drawable.page2);

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();

}

/**

* Author : hmg25 Version: 1.0 Description : 计算拖拽点对应的拖拽脚

*/

private 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;

}

@Override

public boolean onTouchEvent(MotionEvent event) {


// TODO Auto-generated method stub

if (event.getAction() == MotionEvent.ACTION_MOVE) {


mCanvas.drawColor(0xFFAAAAAA);

mTouch.x = event.getX();

mTouch.y = event.getY();

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;

mTouch.y = mCornerY;

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 (mBezierStart1.x < 0 || mBezierStart1.x > 480) {

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, Bitmap bm3) {


mCurPageBitmap = bm1;

mCurPageBackBitmap = bm2;

mNextPageBitmap = bm3;

}

@Override

protected void onDraw(Canvas canvas) {


canvas.drawColor(0xFFAAAAAA);

calcPoints();

drawCurrentPageArea(mCanvas, mCurPageBitmap, mPath0);

drawNextPageAreaAndShadow(mCanvas, mNextPageBitmap);

drawCurrentPageShadow(mCanvas);

drawCurrentBackArea(mCanvas, mCurPageBitmap);

canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);

}

/**

* 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 – 800;

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();

}

}


Activity 不用写布局




直接给



package sf.hmg.turntest;

import android.app.Activity;

import android.graphics.Paint;

import android.os.Bundle;

import android.view.Window;

import android.view.WindowManager;

public class turntest extends Activity {


/** Called when the activity is first created. */

private Paint mPaint;

@Override

public void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);

//无标题栏

requestWindowFeature(Window.FEATURE_NO_TITLE);

//布局

getWindow().setFlags(

WindowManager.LayoutParams.FLAG_FULLSCREEN,

WindowManager.LayoutParams.FLAG_FULLSCREEN);

setContentView(new PageWidget(this));

}

}


发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注